From 572bbf28ccd1cd5f8e4f17a38d0fbd989b1d56bf Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Fri, 14 May 2021 08:10:43 +0200 Subject: [PATCH] Kernel+LibC: Add support for filtering profiling events This adds the -t command-line argument for the profile tool. Using this argument you can filter which event types you want in your profile. --- Kernel/PerformanceEventBuffer.cpp | 3 +++ Kernel/PerformanceEventBuffer.h | 3 ++- Kernel/Process.h | 2 +- Kernel/Syscalls/profiling.cpp | 6 ++++- Kernel/UnixTypes.h | 22 ++++++++--------- Kernel/init.cpp | 2 +- Userland/DevTools/Profiler/main.cpp | 2 +- Userland/Libraries/LibC/serenity.cpp | 4 ++-- Userland/Libraries/LibC/serenity.h | 26 +++++++++++---------- Userland/Utilities/profile.cpp | 35 +++++++++++++++++++++++++--- 10 files changed, 72 insertions(+), 33 deletions(-) diff --git a/Kernel/PerformanceEventBuffer.cpp b/Kernel/PerformanceEventBuffer.cpp index 2fa5b8680d..b22f8376b5 100644 --- a/Kernel/PerformanceEventBuffer.cpp +++ b/Kernel/PerformanceEventBuffer.cpp @@ -60,6 +60,9 @@ KResult PerformanceEventBuffer::append_with_eip_and_ebp(ProcessID pid, ThreadID if (count() >= capacity()) return ENOBUFS; + if ((g_profiling_event_mask & type) == 0) + return EINVAL; + PerformanceEvent event; event.type = type; event.lost_samples = lost_samples; diff --git a/Kernel/PerformanceEventBuffer.h b/Kernel/PerformanceEventBuffer.h index 4b31b5f516..5186f584e6 100644 --- a/Kernel/PerformanceEventBuffer.h +++ b/Kernel/PerformanceEventBuffer.h @@ -53,7 +53,7 @@ struct [[gnu::packed]] ContextSwitchPerformanceEvent { }; struct [[gnu::packed]] PerformanceEvent { - u8 type { 0 }; + u16 type { 0 }; u8 stack_size { 0 }; u32 pid { 0 }; u32 tid { 0 }; @@ -116,5 +116,6 @@ private: extern bool g_profiling_all_threads; extern PerformanceEventBuffer* g_global_perf_events; +extern u64 g_profiling_event_mask; } diff --git a/Kernel/Process.h b/Kernel/Process.h index 2f40982850..0867ddac37 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -392,7 +392,7 @@ public: KResultOr sys$setkeymap(Userspace); KResultOr sys$module_load(Userspace path, size_t path_length); KResultOr sys$module_unload(Userspace name, size_t name_length); - KResultOr sys$profiling_enable(pid_t); + KResultOr sys$profiling_enable(pid_t, u64); KResultOr sys$profiling_disable(pid_t); KResultOr sys$profiling_free_buffer(pid_t); KResultOr sys$futex(Userspace); diff --git a/Kernel/Syscalls/profiling.cpp b/Kernel/Syscalls/profiling.cpp index 9fbc7c599e..76a6ad92fd 100644 --- a/Kernel/Syscalls/profiling.cpp +++ b/Kernel/Syscalls/profiling.cpp @@ -15,8 +15,9 @@ namespace Kernel { bool g_profiling_all_threads; PerformanceEventBuffer* g_global_perf_events; +u64 g_profiling_event_mask; -KResultOr Process::sys$profiling_enable(pid_t pid) +KResultOr Process::sys$profiling_enable(pid_t pid, u64 event_mask) { REQUIRE_NO_PROMISES; @@ -24,6 +25,7 @@ KResultOr Process::sys$profiling_enable(pid_t pid) if (!is_superuser()) return EPERM; ScopedCritical critical; + g_profiling_event_mask = event_mask; if (g_global_perf_events) g_global_perf_events->clear(); else @@ -33,6 +35,7 @@ KResultOr Process::sys$profiling_enable(pid_t pid) if (!TimeManagement::the().enable_profile_timer()) return ENOTSUP; g_profiling_all_threads = true; + PerformanceManager::add_process_created_event(*Scheduler::colonel()); Process::for_each([](auto& process) { PerformanceManager::add_process_created_event(process); return IterationDecision::Continue; @@ -52,6 +55,7 @@ KResultOr Process::sys$profiling_enable(pid_t pid) return ENOMEM; if (!TimeManagement::the().enable_profile_timer()) return ENOTSUP; + g_profiling_event_mask = event_mask; process->set_profiling(true); return 0; } diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index f1d2a0a27d..f3428b8e0d 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -47,17 +47,17 @@ enum { }; enum { - PERF_EVENT_SAMPLE, - PERF_EVENT_MALLOC, - PERF_EVENT_FREE, - PERF_EVENT_MMAP, - PERF_EVENT_MUNMAP, - PERF_EVENT_PROCESS_CREATE, - PERF_EVENT_PROCESS_EXEC, - PERF_EVENT_PROCESS_EXIT, - PERF_EVENT_THREAD_CREATE, - PERF_EVENT_THREAD_EXIT, - PERF_EVENT_CONTEXT_SWITCH, + PERF_EVENT_SAMPLE = 1, + PERF_EVENT_MALLOC = 2, + PERF_EVENT_FREE = 4, + PERF_EVENT_MMAP = 8, + PERF_EVENT_MUNMAP = 16, + PERF_EVENT_PROCESS_CREATE = 32, + PERF_EVENT_PROCESS_EXEC = 64, + PERF_EVENT_PROCESS_EXIT = 128, + PERF_EVENT_THREAD_CREATE = 256, + PERF_EVENT_THREAD_EXIT = 512, + PERF_EVENT_CONTEXT_SWITCH = 1024, }; #define WNOHANG 1 diff --git a/Kernel/init.cpp b/Kernel/init.cpp index ec6552f924..8be6e2d266 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -287,7 +287,7 @@ void init_stage2(void*) if (boot_profiling) { dbgln("Starting full system boot profiling"); - auto result = Process::current()->sys$profiling_enable(-1); + auto result = Process::current()->sys$profiling_enable(-1, ~0ull); VERIFY(!result.is_error()); } diff --git a/Userland/DevTools/Profiler/main.cpp b/Userland/DevTools/Profiler/main.cpp index d94ee2e18a..bc05a18865 100644 --- a/Userland/DevTools/Profiler/main.cpp +++ b/Userland/DevTools/Profiler/main.cpp @@ -280,7 +280,7 @@ bool generate_profile(pid_t& pid) process_name = "(unknown)"; } - if (profiling_enable(pid) < 0) { + if (profiling_enable(pid, PERF_EVENT_MASK_ALL) < 0) { int saved_errno = errno; GUI::MessageBox::show(nullptr, String::formatted("Unable to profile process {}({}): {}", process_name, pid, strerror(saved_errno)), "Profiler", GUI::MessageBox::Type::Error); return false; diff --git a/Userland/Libraries/LibC/serenity.cpp b/Userland/Libraries/LibC/serenity.cpp index 1b59675dd7..0f112ca0ae 100644 --- a/Userland/Libraries/LibC/serenity.cpp +++ b/Userland/Libraries/LibC/serenity.cpp @@ -30,9 +30,9 @@ int module_unload(const char* name, size_t name_length) __RETURN_WITH_ERRNO(rc, rc, -1); } -int profiling_enable(pid_t pid) +int profiling_enable(pid_t pid, uint64_t event_mask) { - int rc = syscall(SC_profiling_enable, pid); + int rc = syscall(SC_profiling_enable, pid, event_mask); __RETURN_WITH_ERRNO(rc, rc, -1); } diff --git a/Userland/Libraries/LibC/serenity.h b/Userland/Libraries/LibC/serenity.h index 72282cf449..acc94f6b40 100644 --- a/Userland/Libraries/LibC/serenity.h +++ b/Userland/Libraries/LibC/serenity.h @@ -17,7 +17,7 @@ int disown(pid_t); int module_load(const char* path, size_t path_length); int module_unload(const char* name, size_t name_length); -int profiling_enable(pid_t); +int profiling_enable(pid_t, uint64_t); int profiling_disable(pid_t); int profiling_free_buffer(pid_t); @@ -76,19 +76,21 @@ int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struc int purge(int mode); enum { - PERF_EVENT_SAMPLE, - PERF_EVENT_MALLOC, - PERF_EVENT_FREE, - PERF_EVENT_MMAP, - PERF_EVENT_MUNMAP, - PERF_EVENT_PROCESS_CREATE, - PERF_EVENT_PROCESS_EXEC, - PERF_EVENT_PROCESS_EXIT, - PERF_EVENT_THREAD_CREATE, - PERF_EVENT_THREAD_EXIT, - PERF_EVENT_CONTEXT_SWITCH, + PERF_EVENT_SAMPLE = 1, + PERF_EVENT_MALLOC = 2, + PERF_EVENT_FREE = 4, + PERF_EVENT_MMAP = 8, + PERF_EVENT_MUNMAP = 16, + PERF_EVENT_PROCESS_CREATE = 32, + PERF_EVENT_PROCESS_EXEC = 64, + PERF_EVENT_PROCESS_EXIT = 128, + PERF_EVENT_THREAD_CREATE = 256, + PERF_EVENT_THREAD_EXIT = 512, + PERF_EVENT_CONTEXT_SWITCH = 1024, }; +#define PERF_EVENT_MASK_ALL (~0ull) + int perf_event(int type, uintptr_t arg1, uintptr_t arg2); int get_stack_bounds(uintptr_t* user_stack_base, size_t* user_stack_size); diff --git a/Userland/Utilities/profile.cpp b/Userland/Utilities/profile.cpp index 5b350dcb8e..116ac880ab 100644 --- a/Userland/Utilities/profile.cpp +++ b/Userland/Utilities/profile.cpp @@ -21,6 +21,9 @@ int main(int argc, char** argv) bool enable = false; bool disable = false; bool all_processes = false; + u64 event_mask = PERF_EVENT_MMAP | PERF_EVENT_MUNMAP | PERF_EVENT_PROCESS_CREATE + | PERF_EVENT_PROCESS_EXEC | PERF_EVENT_PROCESS_EXIT | PERF_EVENT_THREAD_CREATE | PERF_EVENT_THREAD_EXIT; + bool seen_event_type_arg = false; args_parser.add_option(pid_argument, "Target PID", nullptr, 'p', "PID"); args_parser.add_option(all_processes, "Profile all processes (super-user only)", nullptr, 'a'); @@ -29,14 +32,40 @@ int main(int argc, char** argv) args_parser.add_option(free, "Free the profiling buffer for the associated process(es).", nullptr, 'f'); args_parser.add_option(wait, "Enable profiling and wait for user input to disable.", nullptr, 'w'); args_parser.add_option(cmd_argument, "Command", nullptr, 'c', "command"); + args_parser.add_option(Core::ArgsParser::Option { + true, "Enable tracking specific event type", nullptr, 't', "event_type", + [&](String event_type) { + seen_event_type_arg = true; + if (event_type == "sample") + event_mask |= PERF_EVENT_SAMPLE; + else if (event_type == "context_switch") + event_mask |= PERF_EVENT_CONTEXT_SWITCH; + else { + warnln("Unknown event type '{}' specified.", event_type); + exit(1); + } + return true; + } }); - args_parser.parse(argc, argv); + auto print_types = [] { + outln(); + outln("Event type can be one of: sample and context_switch."); + }; + + if (!args_parser.parse(argc, argv, false)) { + print_types(); + exit(1); + } if (!pid_argument && !cmd_argument && !all_processes) { args_parser.print_usage(stdout, argv[0]); + print_types(); return 0; } + if (!seen_event_type_arg) + event_mask |= PERF_EVENT_SAMPLE; + if (pid_argument || all_processes) { if (!(enable ^ disable ^ wait ^ free)) { fprintf(stderr, "-p requires -e xor -d xor -w xor -f.\n"); @@ -46,7 +75,7 @@ int main(int argc, char** argv) pid_t pid = all_processes ? -1 : atoi(pid_argument); if (wait || enable) { - if (profiling_enable(pid) < 0) { + if (profiling_enable(pid, event_mask) < 0) { perror("profiling_enable"); return 1; } @@ -85,7 +114,7 @@ int main(int argc, char** argv) cmd_argv.append(nullptr); dbgln("Enabling profiling for PID {}", getpid()); - profiling_enable(getpid()); + profiling_enable(getpid(), event_mask); if (execvp(cmd_argv[0], const_cast(cmd_argv.data())) < 0) { perror("execv"); return 1;