diff --git a/ext/libriscv b/ext/libriscv index 6fe4117..0be7d94 160000 --- a/ext/libriscv +++ b/ext/libriscv @@ -1 +1 @@ -Subproject commit 6fe4117bb308bb76992b25eff19f8740464ff701 +Subproject commit 0be7d9465516cfe8042a0e051b8aabf43098569d diff --git a/src/sandbox.cpp b/src/sandbox.cpp index 65a6cba..b756e87 100644 --- a/src/sandbox.cpp +++ b/src/sandbox.cpp @@ -535,9 +535,9 @@ bool Sandbox::load(const PackedByteArray *buffer, const std::vector // 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(); } @@ -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 &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 &hotspots = gprofstate.hotspots; for (const gaddr_t address : profdata.visited) { - visited[address] ++; + hotspots[address] ++; } } profdata.visited.clear(); @@ -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::Callsite callsite = machine().memory.lookup(address); return String::utf8(callsite.name.c_str(), callsite.name.size()); } @@ -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 --// @@ -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) { diff --git a/src/sandbox.h b/src/sandbox.h index 3ad60cc..b13edba 100644 --- a/src/sandbox.h +++ b/src/sandbox.h @@ -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 hotspots; + std::vector lookup; + }; Sandbox(); Sandbox(const PackedByteArray &buffer); @@ -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 =- @@ -548,7 +556,7 @@ class Sandbox : public Node { // Properties mutable std::vector m_properties; - mutable std::unordered_map m_lookup; + mutable std::unordered_map m_lookup; // Restrictions std::unordered_set m_allowed_objects; @@ -584,7 +592,7 @@ class Sandbox : public Node { struct ProfilingData { // ELF path -> Address -> Count // Anonymous sandboxes are stored as "" - std::unordered_map> visited; + std::unordered_map state; }; static inline std::unique_ptr m_profiling_data = nullptr; struct LocalProfilingData { diff --git a/src/sandbox_profiling.cpp b/src/sandbox_profiling.cpp index b278c35..9b5a4a3 100644 --- a/src/sandbox_profiling.cpp +++ b/src/sandbox_profiling.cpp @@ -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; @@ -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 -f -C 0x
@@ -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::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::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()); } @@ -130,7 +149,7 @@ static void resolve(Result &res, const Callable &callback) { } Array Sandbox::get_hotspots(unsigned total, const Callable &callable) { - std::unordered_map> visited; + std::unordered_map gprofstate; { std::scoped_lock lock(profiling_mutex); if (!m_profiling_data) { @@ -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); @@ -148,18 +167,18 @@ Array Sandbox::get_hotspots(unsigned total, const Callable &callable) { std::vector 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)); } @@ -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; @@ -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(); } diff --git a/src/sandbox_syscalls.cpp b/src/sandbox_syscalls.cpp index a5b3ca2..97ead17 100644 --- a/src/sandbox_syscalls.cpp +++ b/src/sandbox_syscalls.cpp @@ -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");