Skip to content

Commit

Permalink
Access cached symbols when profiling
Browse files Browse the repository at this point in the history
Also, update libriscv
  • Loading branch information
fwsGonzo committed Dec 22, 2024
1 parent e04ad2b commit f6016ec
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 28 deletions.
2 changes: 1 addition & 1 deletion ext/libriscv
40 changes: 31 additions & 9 deletions src/sandbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,9 +535,9 @@ bool Sandbox::load(const PackedByteArray *buffer, const std::vector<std::string>
// Cache the public API functions from the ELFScript object
for (int i = 0; i < this->m_program_data->functions.size(); i++) {
const Dictionary func = this->m_program_data->functions[i];
const String &name = func["name"];
String name = func["name"];
const gaddr_t address = func.get("address", 0x0);
this->m_lookup.insert_or_assign(name.hash(), address);
this->m_lookup.insert_or_assign(name.hash(), LookupEntry{ std::move(name), address });
}
this->m_program_data->update_public_api_functions();
}
Expand Down Expand Up @@ -854,9 +854,19 @@ Variant Sandbox::vmcall_internal(gaddr_t address, const Variant **args, int argc
// Update the global profiler
{
std::scoped_lock lock(profiling_mutex);
std::unordered_map<gaddr_t, int> &visited = gprofdata.visited[path];
ProfilingState &gprofstate = gprofdata.state[path];
// Add all the local known functions to the global state,
// to aid lookup in the profiler later on
if (gprofstate.lookup.size() < this->m_lookup.size()) {
gprofstate.lookup.clear();
for (const auto [hash, entry] : this->m_lookup) {
gprofstate.lookup.push_back(entry);
}
}
// Update the global visited map
std::unordered_map<gaddr_t, int> &hotspots = gprofstate.hotspots;
for (const gaddr_t address : profdata.visited) {
visited[address] ++;
hotspots[address] ++;
}
}
profdata.visited.clear();
Expand Down Expand Up @@ -959,19 +969,27 @@ gaddr_t Sandbox::cached_address_of(int64_t hash, const String &function) const {
ERR_PRINT(error);
}
}
m_lookup.insert_or_assign(hash, address);
// Cache the address and symbol name
LookupEntry entry{ function, address };
m_lookup.insert_or_assign(hash, std::move(entry));
} else {
address = it->second;
address = it->second.address;
}

return address;
}

gaddr_t Sandbox::address_of(const String &symbol) const {
return machine().address_of(std::string_view(symbol.utf8().ptr()));
const int64_t hash = symbol.hash();
return cached_address_of(hash, symbol);
}

String Sandbox::lookup_address(gaddr_t address) const {
for (const auto &entry : m_lookup) {
if (entry.second.address == address) {
return entry.second.name;
}
}
riscv::Memory<RISCV_ARCH>::Callsite callsite = machine().memory.lookup(address);
return String::utf8(callsite.name.c_str(), callsite.name.size());
}
Expand All @@ -981,8 +999,8 @@ bool Sandbox::has_function(const StringName &p_function) const {
return address != 0x0;
}

void Sandbox::add_cached_address(int64_t hash, gaddr_t address) {
m_lookup.insert_or_assign(hash, address);
void Sandbox::add_cached_address(const String &name, gaddr_t address) const {
m_lookup.insert_or_assign(name.hash(), LookupEntry{name, address});
}

//-- Scoped objects and variants --//
Expand Down Expand Up @@ -1179,6 +1197,10 @@ void Sandbox::add_property(const String &name, Variant::Type vtype, uint64_t set
}
}
m_properties.push_back(SandboxProperty(name, vtype, setter, getter, def));

// Make the property getter/setter functions visible to address_of and profiling
this->add_cached_address("set_" + name, getter);
this->add_cached_address("get_" + name, setter);
}

bool Sandbox::set_property(const StringName &name, const Variant &value) {
Expand Down
16 changes: 12 additions & 4 deletions src/sandbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ class Sandbox : public Node {
void reset();
bool is_mutable_variant(const Variant &var) const;
};
struct LookupEntry {
String name;
gaddr_t address;
};
struct ProfilingState {
std::unordered_map<gaddr_t, int> hotspots;
std::vector<LookupEntry> lookup;
};

Sandbox();
Sandbox(const PackedByteArray &buffer);
Expand Down Expand Up @@ -178,9 +186,9 @@ class Sandbox : public Node {
bool has_function(const StringName &p_function) const;

/// @brief Add a hash to address mapping to the cache.
/// @param hash The hash of the name.
/// @param name The name of the function or symbol.
/// @param address The address of the function or symbol.
void add_cached_address(int64_t hash, gaddr_t address);
void add_cached_address(const String &name, gaddr_t address) const;

// -= Call State Management =-

Expand Down Expand Up @@ -548,7 +556,7 @@ class Sandbox : public Node {

// Properties
mutable std::vector<SandboxProperty> m_properties;
mutable std::unordered_map<int64_t, gaddr_t> m_lookup;
mutable std::unordered_map<int64_t, LookupEntry> m_lookup;

// Restrictions
std::unordered_set<godot::Object *> m_allowed_objects;
Expand Down Expand Up @@ -584,7 +592,7 @@ class Sandbox : public Node {
struct ProfilingData {
// ELF path -> Address -> Count
// Anonymous sandboxes are stored as ""
std::unordered_map<std::string_view, std::unordered_map<gaddr_t, int>> visited;
std::unordered_map<std::string_view, ProfilingState> state;
};
static inline std::unique_ptr<ProfilingData> m_profiling_data = nullptr;
struct LocalProfilingData {
Expand Down
44 changes: 32 additions & 12 deletions src/sandbox_profiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ void Sandbox::enable_profiling(bool enable, uint32_t interval) {
struct Result {
std::string elf;
gaddr_t pc = 0;
int64_t offset = 0;
int count = 0;
int line = 0;
String function;
Expand All @@ -65,7 +66,9 @@ static ProfilingMachine *requisition(const std::string &elf) {
return &it->second;
}

static void resolve(Result &res, const Callable &callback) {
static void resolve(Result &res, const Callable &callback,
const Sandbox::ProfilingState &gprofstate) {
// Try to resolve the address using addr2line
#ifdef __linux__
if (USE_ADDR2LINE && !res.elf.empty()) {
// execute riscv64-linux-gnu-addr2line -e <binary> -f -C 0x<address>
Expand Down Expand Up @@ -116,10 +119,26 @@ static void resolve(Result &res, const Callable &callback) {
res.file = "(unknown)";
res.function = "??";
if (!res.elf.empty()) {
ProfilingMachine *pm = requisition(res.elf);
if (pm) {
riscv::Memory<RISCV_ARCH>::Callsite callsite = pm->machine->memory.lookup(res.pc);
res.function = String::utf8(callsite.name.c_str(), callsite.name.size());
// Prefer gprofstate lookup
gaddr_t best = ~0ULL;
res.offset = 0x0;
for (const auto &entry : gprofstate.lookup) {
if (res.pc < entry.address || entry.address >= best) {
continue;
}
best = entry.address;
res.offset = res.pc - entry.address;
res.function = entry.name;
}

// Try to resolve the address using the ELF file, if the offset from lookup was too large
if (best == ~0ULL || res.offset > 0x4000) {
ProfilingMachine *pm = requisition(res.elf);
if (pm) {
riscv::Memory<RISCV_ARCH>::Callsite callsite = pm->machine->memory.lookup(res.pc);
res.function = String::utf8(callsite.name.c_str(), callsite.name.size());
res.offset = callsite.offset;
}
}
res.file = String::utf8(res.elf.c_str(), res.elf.size());
}
Expand All @@ -130,7 +149,7 @@ static void resolve(Result &res, const Callable &callback) {
}

Array Sandbox::get_hotspots(unsigned total, const Callable &callable) {
std::unordered_map<std::string_view, std::unordered_map<gaddr_t, int>> visited;
std::unordered_map<std::string_view, ProfilingState> gprofstate;
{
std::scoped_lock lock(profiling_mutex);
if (!m_profiling_data) {
Expand All @@ -139,7 +158,7 @@ Array Sandbox::get_hotspots(unsigned total, const Callable &callable) {
}
ProfilingData &profdata = *m_profiling_data;
// Copy the profiling data
visited = profdata.visited;
gprofstate = profdata.state;
}
// Prevent re-entrancy into the profiling data
std::scoped_lock lock(generate_hotspots_mutex);
Expand All @@ -148,18 +167,18 @@ Array Sandbox::get_hotspots(unsigned total, const Callable &callable) {
std::vector<Result> results;
unsigned total_measurements = 0;

for (const auto &path : visited) {
for (const auto &path : gprofstate) {
std::string_view elf_path = path.first;
const auto &visited = path.second;
const auto &state = path.second;

for (const auto &entry : visited) {
for (const auto &entry : state.hotspots) {
Result res;
res.elf = elf_path;
res.pc = entry.first;
res.count = entry.second;
total_measurements += res.count;

resolve(res, callable);
resolve(res, callable, state);

results.push_back(std::move(res));
}
Expand Down Expand Up @@ -199,6 +218,7 @@ Array Sandbox::get_hotspots(unsigned total, const Callable &callable) {
Dictionary hotspot;
hotspot["function"] = res.function;
hotspot["address"] = String::num_int64(res.pc, 16);
hotspot["offset"] = String::num_int64(res.offset, 16);
hotspot["file"] = res.file;
hotspot["line"] = res.line;
hotspot["samples"] = res.count;
Expand All @@ -220,6 +240,6 @@ void Sandbox::clear_hotspots() {
ERR_PRINT("Profiling is not currently enabled.");
return;
}
m_profiling_data->visited.clear();
m_profiling_data->state.clear();
lookup_machines.clear();
}
3 changes: 1 addition & 2 deletions src/sandbox_syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1877,8 +1877,7 @@ APICALL(api_sandbox_add) {
}
}
// Cache the function name hash with the address for faster lookup.
int64_t name_hash = String::utf8(name.data(), name.size()).hash();
emu.add_cached_address(name_hash, address);
emu.add_cached_address(String::utf8(name.data(), name.size()), address);
} break;
default:
ERR_PRINT("Invalid sandbox add operation");
Expand Down

0 comments on commit f6016ec

Please sign in to comment.