Skip to content

Commit 995bc16

Browse files
authored
Merge lazy loading support for code objects
Add lazy loading support for code objects
2 parents 429afe4 + f87a0d9 commit 995bc16

6 files changed

Lines changed: 638 additions & 114 deletions

File tree

include/kernelDB.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ THE SOFTWARE.
5353
#include <vector>
5454
#include <thread>
5555
#include <mutex>
56+
#include <condition_variable>
5657
#include <utility>
5758
#include <shared_mutex>
5859
#include <filesystem>
@@ -113,6 +114,7 @@ struct KernelArgument {
113114
bool buildDwarfAddressMap(const char* filename, size_t offset, size_t hsaco_length, std::map<Dwarf_Addr, SourceLocation>& addressMap);
114115
SourceLocation getSourceLocation(const std::map<Dwarf_Addr, SourceLocation>& addrMap, Dwarf_Addr addr);
115116
__attribute__((visibility("default"))) bool getDisassembly(hsa_agent_t agent, const std::string& fileName, std::string& out);
117+
bool getDisassemblyForSymbol(hsa_agent_t agent, const std::string& fileName, const std::string& symbolName, std::string& out);
116118
bool invokeProgram(const std::string& programName, const std::vector<std::string>& params, const std::string& outputFileName);
117119
std::string create_temp_file_segment(const std::string& filename, std::streamoff offset, std::streamsize length);
118120
__attribute__((visibility("default"))) std::vector<std::string> extractCodeObjects(hsa_agent_t agent, const std::string& fileName);
@@ -210,7 +212,7 @@ class __attribute__((visibility("default"))) kernelDB {
210212
~kernelDB();
211213
bool getBasicBlocks(const std::string& name, std::vector<basicBlock>&);
212214
CDNAKernel& getKernel(const std::string& name);
213-
bool addFile(const std::string& name, hsa_agent_t agent, const std::string& strFilter);
215+
bool addFile(const std::string& name, hsa_agent_t agent, const std::string& strFilter, bool lazy = false);
214216
bool parseDisassembly(const std::string& text);
215217
void mapDisassemblyToSource(hsa_agent_t agent, const char *elfFilePath);
216218
bool addKernel(std::unique_ptr<CDNAKernel> kernel);
@@ -227,9 +229,15 @@ class __attribute__((visibility("default"))) kernelDB {
227229
bool scanCodeObject(const std::string& co_file);
228230
bool hasKernel(const std::string& name);
229231
private:
232+
bool scanCodeObjectForKernel(const std::string& co_file, const std::string& kernelName);
233+
bool parseDisassemblyForKernel(const std::string& text, const std::string& targetKernel);
234+
/// Get kernel symbol names from a .hsaco ELF without disassembling (reads .symtab).
235+
static std::vector<std::string> getKernelNamesFromElf(const std::string& fileName);
236+
/// If kernel is lazy-loaded, disassemble its code object and fill kernels_; then remove from lazy set.
237+
void ensureKernelLoaded(const std::string& name);
230238
void buildLineMap(size_t offset, size_t hsaco_length, const char *elfFilePath);
231239
void extractArgumentsFromDwarf(hsa_agent_t agent, const char *elfFilePath, bool resolve_typedefs);
232-
void processKernelsWithAddressMap(const std::map<Dwarf_Addr, SourceLocation>& addrMap);
240+
void processKernelsWithAddressMap(const std::map<Dwarf_Addr, SourceLocation>& addrMap, const std::string& targetKernel = "");
233241
parse_mode getLineType(std::string& line);
234242
std::string extractKernelName(const std::string& line);
235243
static bool isBranch(const std::string& instruction);
@@ -240,6 +248,17 @@ class __attribute__((visibility("default"))) kernelDB {
240248
std::string fileName_;
241249
std::map<std::string, std::vector<std::string>> file_map_;
242250
std::set<std::string> scanned_code_objects_;
251+
struct LazyKernelEntry {
252+
std::string hsaco_path;
253+
std::string logical_file;
254+
std::string elf_symbol; // raw (mangled) ELF symbol name for --disassemble-symbols
255+
};
256+
/// Lazy-loaded kernels: canonical name -> entry. Filled by addFile(..., lazy=true).
257+
std::map<std::string, LazyKernelEntry> lazy_kernels_;
258+
/// Kernels currently being loaded — prevents concurrent disassembly of the same kernel.
259+
std::set<std::string> loading_kernels_;
260+
std::mutex loading_mutex_;
261+
std::condition_variable loading_cv_;
243262
std::shared_mutex mutex_;
244263
};
245264

kerneldb/api.py

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,16 @@ class KernelDB:
3838
to query instruction-level information mapped to source lines.
3939
"""
4040

41-
def __init__(self, binary_path: Optional[str] = None, agent_id: Optional[int] = None):
41+
def __init__(self, binary_path: Optional[str] = None, agent_id: Optional[int] = None, lazy: bool = False):
4242
"""
4343
Initialize KernelDB
4444
4545
Args:
46-
binary_path: Path to HSACO or HIP binary file. If empty string or None,
47-
will search in the running process for fat binaries.
46+
binary_path: Path to HSACO or HIP binary file. If None and lazy=False,
47+
empty string loads the running process executable + shared libs.
4848
agent_id: HSA agent handle (if None, will use first GPU)
49+
lazy: If True, create an empty DB; load binaries later with add_file() to avoid
50+
loading the whole process. If False, load binary_path (or process) in constructor.
4951
"""
5052
# Initialize HSA
5153
status = _kerneldb.hsa_init()
@@ -61,10 +63,53 @@ def __init__(self, binary_path: Optional[str] = None, agent_id: Optional[int] =
6163
if self.agent.handle == 0:
6264
raise RuntimeError("No GPU agent found")
6365

64-
# Create kernelDB instance (analysis happens in constructor)
65-
binary_path = binary_path or ""
66-
self._kdb = _kerneldb.KernelDB(self.agent, binary_path)
67-
self.binary_path = binary_path
66+
if lazy:
67+
# Empty DB; add files with add_file() for lazy loading
68+
self._kdb = _kerneldb.KernelDB(self.agent)
69+
self.binary_path = None
70+
else:
71+
binary_path = binary_path or ""
72+
self._kdb = _kerneldb.KernelDB(self.agent, binary_path)
73+
self.binary_path = binary_path or None
74+
75+
def add_file(self, path: str, filter: str = "", lazy: bool = True) -> bool:
76+
"""
77+
Add a binary (HIP executable or .hsaco).
78+
79+
With lazy=True (default): only indexes kernel names and their code-object
80+
locations—no disassembly. Disassembly is done on demand when you call
81+
get_kernel(), get_kernel_lines(), get_instructions_for_line(), or access
82+
assembly/arguments for a kernel.
83+
84+
With lazy=False: full load (disassemble all code objects and map to source),
85+
same as the previous behavior.
86+
87+
Args:
88+
path: Path to HIP fat binary or .hsaco file
89+
filter: Optional kernel name filter (currently unused in C++)
90+
lazy: If True (default), only index; disassemble on first use per code object.
91+
92+
Returns:
93+
True on success
94+
"""
95+
return self._kdb.add_file(path, self.agent, filter, lazy)
96+
97+
def scan_code_object(self, co_file: str) -> bool:
98+
"""
99+
Scan a single .hsaco code object (disassembly + DWARF + args).
100+
Idempotent if the code object was already scanned.
101+
102+
Args:
103+
co_file: Path to a .hsaco file
104+
105+
Returns:
106+
True on success
107+
"""
108+
return self._kdb.scan_code_object(co_file)
109+
110+
def has_kernel(self, name: str) -> bool:
111+
"""Return True if a kernel with the given name exists."""
112+
return self._kdb.has_kernel(name)
68113

69114
def get_kernels(self) -> List[str]:
70115
"""

src/disassemble.cc

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ THE SOFTWARE.
2424
#include <sstream>
2525
#include "include/kernelDB.h"
2626

27-
std::vector<std::string> disassembly_params = {"-d ", "--arch-name=amdgcn"};
27+
std::vector<std::string> disassembly_params = {"-d", "--arch-name=amdgcn"};
2828

2929
void readFileToString(const std::string& filename, std::string& content) {
3030
std::ifstream file(filename, std::ios::binary);
@@ -72,33 +72,73 @@ bool getDisassembly(hsa_agent_t agent, const std::string& fileName, std::string&
7272
ss << "--mcpu=" << name;
7373
parms.push_back(ss.str());
7474
parms.push_back(fileName);
75-
// Create a temporary file using tmpnam (note: tmpnam is not the most secure option)
76-
char temp_filename[L_tmpnam];
77-
if (tmpnam(temp_filename) == nullptr)
78-
throw std::runtime_error("Failed to generate temporary filename");
79-
else if (invokeProgram(disassembler, parms, temp_filename))
75+
char temp_template[] = "/tmp/kdb_dis_XXXXXX";
76+
int fd = mkstemp(temp_template);
77+
if (fd < 0)
78+
throw std::runtime_error("Failed to create temporary file");
79+
close(fd);
80+
if (invokeProgram(disassembler, parms, temp_template))
8081
{
81-
// read file contents here
82-
readFileToString(temp_filename, out);
83-
unlink(temp_filename);
82+
readFileToString(temp_template, out);
83+
unlink(temp_template);
8484
return out.length() != 0;
8585
}
86-
else
87-
return false;
86+
unlink(temp_template);
87+
return false;
8888
}
8989
else
9090
return false;
9191

9292
return true;
9393
}
9494

95+
bool getDisassemblyForSymbol(hsa_agent_t agent, const std::string& fileName,
96+
const std::string& symbolName, std::string& out)
97+
{
98+
// Use --disassemble-symbols to extract only the requested kernel.
99+
std::vector<std::string> parms = {"--arch-name=amdgcn"};
100+
char name[64];
101+
memset(name, 0, sizeof(name));
102+
hsa_status_t status = hsa_agent_get_info(agent, HSA_AGENT_INFO_NAME, name);
103+
if (status != HSA_STATUS_SUCCESS)
104+
return false;
105+
106+
std::stringstream ss;
107+
ss << "--mcpu=" << name;
108+
parms.push_back(ss.str());
109+
parms.push_back("--disassemble-symbols=" + symbolName);
110+
parms.push_back(fileName);
111+
112+
char temp_template[] = "/tmp/kdb_dis_XXXXXX";
113+
int fd = mkstemp(temp_template);
114+
if (fd < 0)
115+
throw std::runtime_error("Failed to create temporary file");
116+
close(fd);
117+
118+
if (invokeProgram(disassembler, parms, temp_template))
119+
{
120+
readFileToString(temp_template, out);
121+
unlink(temp_template);
122+
return out.length() != 0;
123+
}
124+
unlink(temp_template);
125+
return false;
126+
}
127+
95128
bool invokeProgram(const std::string& programName, const std::vector<std::string>& params, const std::string& outputFileName) {
96-
// Construct the command string
129+
// Construct the command string with shell-safe quoting.
130+
// Parameters are single-quoted so kernel names containing parentheses,
131+
// spaces, or other shell metacharacters are passed through safely.
97132
std::stringstream command;
98133
command << programName;
99134
for (const auto& param : params) {
100-
// Basic escaping of parameters (assumes no spaces in params; enhance if needed)
101-
command << " " << param;
135+
std::string escaped = param;
136+
size_t pos = 0;
137+
while ((pos = escaped.find('\'', pos)) != std::string::npos) {
138+
escaped.replace(pos, 1, "'\\''");
139+
pos += 4;
140+
}
141+
command << " '" << escaped << "'";
102142
}
103143
// Redirect stdout to outputFileName
104144
command << " > " << outputFileName;

0 commit comments

Comments
 (0)