From bce3bf9f1e57047ffc89f23feb47d738be704fd7 Mon Sep 17 00:00:00 2001 From: Hediadyoin1 Date: Fri, 20 Aug 2021 17:09:23 +0200 Subject: [PATCH] UserspaceEmulator: Handle PerfEvent syscalls We only froward String setting and FlagPost creation for now, due to the other performance events being nonsensical to forward. We also record these signposts in the optionally generated profile. --- .../DevTools/UserspaceEmulator/Emulator.h | 12 +++++- .../UserspaceEmulator/Emulator_syscalls.cpp | 38 ++++++++++++++++++- Userland/DevTools/UserspaceEmulator/main.cpp | 20 ++++++++-- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.h b/Userland/DevTools/UserspaceEmulator/Emulator.h index c3cecbbdd9e..1c2f4d57f6c 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 9fe0a55b812..4fd60b2f2c6 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 8612ac0600c..659f9437245 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; }