diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.h b/Userland/DevTools/UserspaceEmulator/Emulator.h index c3cecbbdd9..1c2f4d57f6 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator.h +++ b/Userland/DevTools/UserspaceEmulator/Emulator.h @@ -33,11 +33,13 @@ public: Emulator(String const& executable_path, Vector const& arguments, Vector const& environment); - void set_profiling_details(bool should_dump_profile, size_t instruction_interval, OutputFileStream* profile_stream) + void set_profiling_details(bool should_dump_profile, size_t instruction_interval, OutputFileStream* profile_stream, NonnullOwnPtrVector* profiler_strings, Vector* profiler_string_id_map) { m_is_profiling = should_dump_profile; m_profile_instruction_interval = instruction_interval; m_profile_stream = profile_stream; + m_profiler_strings = profiler_strings; + m_profiler_string_id_map = profiler_string_id_map; } void set_in_region_of_interest(bool value) @@ -46,6 +48,9 @@ public: } OutputFileStream& profile_stream() { return *m_profile_stream; } + NonnullOwnPtrVector& profiler_strings() { return *m_profiler_strings; } + Vector& profiler_string_id_map() { return *m_profiler_string_id_map; } + bool is_profiling() const { return m_is_profiling; } bool is_in_region_of_interest() const { return m_is_in_region_of_interest; } size_t profile_instruction_interval() const { return m_profile_instruction_interval; } @@ -137,6 +142,8 @@ private: int virt$gethostname(FlatPtr, ssize_t); int virt$profiling_enable(pid_t); int virt$profiling_disable(pid_t); + FlatPtr virt$perf_event(int type, FlatPtr arg1, FlatPtr arg2); + FlatPtr virt$perf_register_string(FlatPtr, size_t); int virt$disown(pid_t); int virt$purge(int mode); u32 virt$mmap(u32); @@ -274,6 +281,9 @@ private: RangeAllocator m_range_allocator; OutputFileStream* m_profile_stream { nullptr }; + Vector* m_profiler_string_id_map { nullptr }; + NonnullOwnPtrVector* m_profiler_strings { nullptr }; + bool m_is_profiling { false }; size_t m_profile_instruction_interval { 0 }; bool m_is_in_region_of_interest { false }; diff --git a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp index 9fe0a55b81..4fd60b2f2c 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp +++ b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,10 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) return virt$profiling_enable(arg1); case SC_profiling_disable: return virt$profiling_disable(arg1); + case SC_perf_event: + return virt$perf_event((int)arg1, arg2, arg3); + case SC_perf_register_string: + return virt$perf_register_string(arg1, arg2); case SC_disown: return virt$disown(arg1); case SC_purge: @@ -248,8 +253,6 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) case SC_futex: return virt$futex(arg1); case SC_map_time_page: - case SC_perf_register_string: - case SC_perf_event: return -ENOSYS; default: reportln("\n=={}== \033[31;1mUnimplemented syscall: {}\033[0m, {:p}", getpid(), Syscall::to_string((Syscall::Function)function), function); @@ -283,6 +286,37 @@ int Emulator::virt$profiling_disable(pid_t pid) return syscall(SC_profiling_disable, pid); } +FlatPtr Emulator::virt$perf_event(int event, FlatPtr arg1, FlatPtr arg2) +{ + if (event == PERF_EVENT_SIGNPOST) { + if (is_profiling()) { + if (profiler_string_id_map().size() > arg1) + emit_profile_event(profile_stream(), "signpost", String::formatted("\"arg1\": {}, \"arg2\": {}", arg1, arg2)); + syscall(SC_perf_event, PERF_EVENT_SIGNPOST, profiler_string_id_map().at(arg1), arg2); + } else { + syscall(SC_perf_event, PERF_EVENT_SIGNPOST, arg1, arg2); + } + return 0; + } + return -ENOSYS; +} + +FlatPtr Emulator::virt$perf_register_string(FlatPtr string, size_t size) +{ + char* buffer = (char*)alloca(size + 4); + // FIXME: not nice, but works + __builtin_memcpy(buffer, "UE: ", 4); + mmu().copy_from_vm((buffer + 4), string, size); + auto ret = (int)syscall(SC_perf_register_string, buffer, size + 4); + + if (ret >= 0 && is_profiling()) { + profiler_strings().append(make(StringView { buffer + 4, size })); + profiler_string_id_map().append(ret); + ret = profiler_string_id_map().size() - 1; + } + return ret; +} + int Emulator::virt$disown(pid_t pid) { return syscall(SC_disown, pid); diff --git a/Userland/DevTools/UserspaceEmulator/main.cpp b/Userland/DevTools/UserspaceEmulator/main.cpp index 8612ac0600..659f943724 100644 --- a/Userland/DevTools/UserspaceEmulator/main.cpp +++ b/Userland/DevTools/UserspaceEmulator/main.cpp @@ -59,6 +59,9 @@ int main(int argc, char** argv, char** env) profile_dump_path = String::formatted("{}.{}.profile", LexicalPath(executable_path).basename(), getpid()); OwnPtr profile_stream; + OwnPtr> profile_strings; + OwnPtr> profile_string_id_map; + if (dump_profile) { profile_output_file = fopen(profile_dump_path.characters(), "w+"); if (profile_output_file == nullptr) { @@ -67,6 +70,8 @@ int main(int argc, char** argv, char** env) return 1; } profile_stream = make(profile_output_file); + profile_strings = make>(); + profile_string_id_map = make>(); profile_stream->write_or_error(R"({"events":[)"sv.bytes()); timeval tv {}; @@ -85,7 +90,8 @@ int main(int argc, char** argv, char** env) // FIXME: It might be nice to tear down the emulator properly. auto& emulator = *new UserspaceEmulator::Emulator(executable_path, arguments, environment); - emulator.set_profiling_details(dump_profile, profile_instruction_interval, profile_stream); + + emulator.set_profiling_details(dump_profile, profile_instruction_interval, profile_stream, profile_strings, profile_string_id_map); emulator.set_in_region_of_interest(!enable_roi_mode); if (!emulator.load_elf()) @@ -109,8 +115,14 @@ int main(int argc, char** argv, char** env) rc = emulator.exec(); - if (dump_profile) - emulator.profile_stream().write_or_error(R"(], "strings": []})"sv.bytes()); - + if (dump_profile) { + emulator.profile_stream().write_or_error(", \"strings\": ["sv.bytes()); + if (emulator.profiler_strings().size()) { + for (size_t i = 0; i < emulator.profiler_strings().size() - 1; ++i) + emulator.profile_stream().write_or_error(String::formatted("\"{}\", ", emulator.profiler_strings().at(i)).bytes()); + emulator.profile_stream().write_or_error(String::formatted("\"{}\"", emulator.profiler_strings().last()).bytes()); + } + emulator.profile_stream().write_or_error("]}"sv.bytes()); + } return rc; }