فهرست منبع

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.
Gunnar Beutner 4 سال پیش
والد
کامیت
572bbf28cc

+ 3 - 0
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;

+ 2 - 1
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;
 
 }

+ 1 - 1
Kernel/Process.h

@@ -392,7 +392,7 @@ public:
     KResultOr<int> sys$setkeymap(Userspace<const Syscall::SC_setkeymap_params*>);
     KResultOr<int> sys$module_load(Userspace<const char*> path, size_t path_length);
     KResultOr<int> sys$module_unload(Userspace<const char*> name, size_t name_length);
-    KResultOr<int> sys$profiling_enable(pid_t);
+    KResultOr<int> sys$profiling_enable(pid_t, u64);
     KResultOr<int> sys$profiling_disable(pid_t);
     KResultOr<int> sys$profiling_free_buffer(pid_t);
     KResultOr<int> sys$futex(Userspace<const Syscall::SC_futex_params*>);

+ 5 - 1
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<int> Process::sys$profiling_enable(pid_t pid)
+KResultOr<int> Process::sys$profiling_enable(pid_t pid, u64 event_mask)
 {
     REQUIRE_NO_PROMISES;
 
@@ -24,6 +25,7 @@ KResultOr<int> 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<int> 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<int> 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;
 }

+ 11 - 11
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

+ 1 - 1
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());
     }
 

+ 1 - 1
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;

+ 2 - 2
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);
 }
 

+ 14 - 12
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);

+ 32 - 3
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;
+        } });
+
+    auto print_types = [] {
+        outln();
+        outln("Event type can be one of: sample and context_switch.");
+    };
 
-    args_parser.parse(argc, argv);
+    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 <PID> 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<char**>(cmd_argv.data())) < 0) {
         perror("execv");
         return 1;