Explorar el Código

Kernel: Implement a simple process time profiler

The kernel now supports basic profiling of all the threads in a process
by calling profiling_enable(pid_t). You finish the profiling by calling
profiling_disable(pid_t).

This all works by recording thread stacks when the timer interrupt
fires and the current thread is in a process being profiled.
Note that symbolication is deferred until profiling_disable() to avoid
adding more noise than necessary to the profile.

A simple "/bin/profile" command is included here that can be used to
start/stop profiling like so:

    $ profile 10 on
    ... wait ...
    $ profile 10 off

After a profile has been recorded, it can be fetched in /proc/profile

There are various limits (or "bugs") on this mechanism at the moment:

- Only one process can be profiled at a time.
- We allocate 8MB for the samples, if you use more space, things will
  not work, and probably break a bit.
- Things will probably fall apart if the profiled process dies during
  profiling, or while extracing /proc/profile
Andreas Kling hace 5 años
padre
commit
b32e961a84

+ 28 - 0
Kernel/FileSystem/ProcFS.cpp

@@ -23,6 +23,7 @@
 #include <Kernel/Net/TCPSocket.h>
 #include <Kernel/Net/UDPSocket.h>
 #include <Kernel/PCI.h>
+#include <Kernel/Profiling.h>
 #include <Kernel/VM/MemoryManager.h>
 #include <Kernel/VM/PurgeableVMObject.h>
 #include <LibC/errno_numbers.h>
@@ -55,6 +56,7 @@ enum ProcFileType {
     FI_Root_uptime,
     FI_Root_cmdline,
     FI_Root_modules,
+    FI_Root_profile,
     FI_Root_self, // symlink
     FI_Root_sys,  // directory
     FI_Root_net,  // directory
@@ -353,6 +355,31 @@ Optional<KBuffer> procfs$modules(InodeIdentifier)
     return builder.build();
 }
 
+Optional<KBuffer> procfs$profile(InodeIdentifier)
+{
+    InterruptDisabler disabler;
+    KBufferBuilder builder;
+    JsonArraySerializer array(builder);
+    Profiling::for_each_sample([&](auto& sample) {
+        auto object = array.add_object();
+        object.add("pid", sample.pid);
+        object.add("tid", sample.tid);
+        object.add("timestamp", sample.timestamp);
+        auto sample_array = object.add_array("samples");
+        for (size_t i = 0; i < Profiling::max_stack_frame_count; ++i) {
+            if (sample.frames[i] == 0)
+                break;
+            auto frame_object = sample_array.add_object();
+            frame_object.add("address", JsonValue((u32)sample.frames[i]));
+            frame_object.add("symbol", sample.symbolicated_frames[i]);
+            frame_object.finish();
+        }
+        sample_array.finish();
+    });
+    array.finish();
+    return builder.build();
+}
+
 Optional<KBuffer> procfs$net_adapters(InodeIdentifier)
 {
     KBufferBuilder builder;
@@ -1333,6 +1360,7 @@ ProcFS::ProcFS()
     m_entries[FI_Root_uptime] = { "uptime", FI_Root_uptime, procfs$uptime };
     m_entries[FI_Root_cmdline] = { "cmdline", FI_Root_cmdline, procfs$cmdline };
     m_entries[FI_Root_modules] = { "modules", FI_Root_modules, procfs$modules };
+    m_entries[FI_Root_profile] = { "profile", FI_Root_profile, procfs$profile };
     m_entries[FI_Root_sys] = { "sys", FI_Root_sys };
     m_entries[FI_Root_net] = { "net", FI_Root_net };
 

+ 1 - 0
Kernel/Makefile

@@ -76,6 +76,7 @@ CXX_OBJS = \
     PCI.o \
     Process.o \
     ProcessTracer.o \
+    Profiling.o \
     RTC.o \
     Scheduler.o \
     SharedBuffer.o \

+ 27 - 0
Kernel/Process.cpp

@@ -28,6 +28,7 @@
 #include <Kernel/Net/Socket.h>
 #include <Kernel/Process.h>
 #include <Kernel/ProcessTracer.h>
+#include <Kernel/Profiling.h>
 #include <Kernel/RTC.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/SharedBuffer.h>
@@ -3701,3 +3702,29 @@ int Process::sys$module_unload(const char* name, size_t name_length)
     g_modules->remove(it);
     return 0;
 }
+
+int Process::sys$profiling_enable(pid_t pid)
+{
+    InterruptDisabler disabler;
+    auto* process = Process::from_pid(pid);
+    if (!process)
+        return -ESRCH;
+    if (!is_superuser() && process->uid() != m_uid)
+        return -EPERM;
+    Profiling::start(*process);
+    process->set_profiling(true);
+    return 0;
+}
+
+int Process::sys$profiling_disable(pid_t pid)
+{
+    InterruptDisabler disabler;
+    auto* process = Process::from_pid(pid);
+    if (!process)
+        return -ESRCH;
+    if (!is_superuser() && process->uid() != m_uid)
+        return -EPERM;
+    process->set_profiling(false);
+    Profiling::stop();
+    return 0;
+}

+ 6 - 0
Kernel/Process.h

@@ -44,6 +44,9 @@ public:
     static Vector<pid_t> all_pids();
     static Vector<Process*> all_processes();
 
+    bool is_profiling() const { return m_profiling; }
+    void set_profiling(bool profiling) { m_profiling = profiling; }
+
     enum RingLevel : u8 {
         Ring0 = 0,
         Ring3 = 3,
@@ -228,6 +231,8 @@ public:
     int sys$setkeymap(char* map, char* shift_map, char* alt_map);
     int sys$module_load(const char* path, size_t path_length);
     int sys$module_unload(const char* name, size_t name_length);
+    int sys$profiling_enable(pid_t);
+    int sys$profiling_disable(pid_t);
 
     static void initialize();
 
@@ -352,6 +357,7 @@ private:
 
     bool m_being_inspected { false };
     bool m_dead { false };
+    bool m_profiling { false };
 
     RefPtr<Custody> m_executable;
     RefPtr<Custody> m_cwd;

+ 94 - 0
Kernel/Profiling.cpp

@@ -0,0 +1,94 @@
+#include <AK/Demangle.h>
+#include <AK/StringBuilder.h>
+#include <Kernel/FileSystem/Custody.h>
+#include <Kernel/KBuffer.h>
+#include <Kernel/Process.h>
+#include <Kernel/Profiling.h>
+#include <LibELF/ELFLoader.h>
+
+namespace Profiling {
+
+static KBufferImpl* s_profiling_buffer;
+static size_t s_slot_count;
+static size_t s_next_slot_index;
+static Process* s_process;
+
+void start(Process& process)
+{
+    s_process = &process;
+
+    if (!s_profiling_buffer) {
+        s_profiling_buffer = RefPtr<KBufferImpl>(KBuffer::create_with_size(8 * MB).impl()).leak_ref();
+        s_slot_count = s_profiling_buffer->size() / sizeof(Sample);
+    }
+
+    s_next_slot_index = 0;
+}
+
+static Sample& sample_slot(size_t index)
+{
+    return ((Sample*)s_profiling_buffer->data())[index];
+}
+
+Sample& next_sample_slot()
+{
+    auto& slot = sample_slot(s_next_slot_index++);
+    if (s_next_slot_index >= s_slot_count)
+        s_next_slot_index = 0;
+    return slot;
+}
+
+static void symbolicate(Sample& stack)
+{
+    auto& process = *s_process;
+    ProcessPagingScope paging_scope(process);
+    struct RecognizedSymbol {
+        u32 address;
+        const KSym* ksym;
+    };
+    Vector<RecognizedSymbol, max_stack_frame_count> recognized_symbols;
+    for (size_t i = 1; i < max_stack_frame_count; ++i) {
+        if (stack.frames[i] == 0)
+            break;
+        recognized_symbols.append({ stack.frames[i], ksymbolicate(stack.frames[i]) });
+    }
+
+    size_t i = 1;
+    for (auto& symbol : recognized_symbols) {
+        if (!symbol.address)
+            break;
+        auto& symbol_string_slot = stack.symbolicated_frames[i++];
+        if (!symbol.ksym) {
+            if (!Scheduler::is_active() && process.elf_loader() && process.elf_loader()->has_symbols())
+                symbol_string_slot = String::format("%s", process.elf_loader()->symbolicate(symbol.address).characters());
+            else
+                symbol_string_slot = String::empty();
+            continue;
+        }
+        unsigned offset = symbol.address - symbol.ksym->address;
+        if (symbol.ksym->address == ksym_highest_address && offset > 4096)
+            symbol_string_slot = String::empty();
+        else
+            symbol_string_slot = String::format("%s +%u", demangle(symbol.ksym->name).characters(), offset);
+    }
+}
+
+void stop()
+{
+    for (size_t i = 0; i < s_next_slot_index; ++i) {
+        auto& stack = sample_slot(i);
+        symbolicate(stack);
+    }
+
+    s_process = nullptr;
+}
+
+void for_each_sample(Function<void(Sample&)> callback)
+{
+    for (size_t i = 0; i < s_next_slot_index; ++i) {
+        auto& sample = sample_slot(i);
+        callback(sample);
+    }
+}
+
+}

+ 26 - 0
Kernel/Profiling.h

@@ -0,0 +1,26 @@
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/String.h>
+#include <AK/Types.h>
+
+class Process;
+
+namespace Profiling {
+
+constexpr size_t max_stack_frame_count = 30;
+
+struct Sample {
+    i32 pid;
+    i32 tid;
+    u64 timestamp;
+    u32 frames[max_stack_frame_count];
+    String symbolicated_frames[max_stack_frame_count];
+};
+
+Sample& next_sample_slot();
+void start(Process&);
+void stop();
+void for_each_sample(Function<void(Sample&)>);
+
+}

+ 12 - 0
Kernel/Scheduler.cpp

@@ -3,6 +3,7 @@
 #include <Kernel/Devices/PCSpeaker.h>
 #include <Kernel/FileSystem/FileDescription.h>
 #include <Kernel/Process.h>
+#include <Kernel/Profiling.h>
 #include <Kernel/RTC.h>
 #include <Kernel/Scheduler.h>
 
@@ -552,6 +553,17 @@ void Scheduler::timer_tick(RegisterDump& regs)
         s_beep_timeout = 0;
     }
 
+    if (current->process().is_profiling()) {
+        auto backtrace = current->raw_backtrace(regs.ebp);
+        auto& sample = Profiling::next_sample_slot();
+        sample.pid = current->pid();
+        sample.tid = current->tid();
+        sample.timestamp = g_uptime;
+        for (size_t i = 0; i < min((size_t)backtrace.size(), Profiling::max_stack_frame_count); ++i) {
+            sample.frames[i] = backtrace[i];
+        }
+    }
+
     if (current->tick())
         return;
 

+ 137 - 135
Kernel/Syscall.h

@@ -13,141 +13,143 @@ struct sockaddr;
 typedef u32 socklen_t;
 }
 
-#define ENUMERATE_SYSCALLS                      \
-    __ENUMERATE_SYSCALL(sleep)                  \
-    __ENUMERATE_SYSCALL(yield)                  \
-    __ENUMERATE_SYSCALL(putch)                  \
-    __ENUMERATE_SYSCALL(open)                   \
-    __ENUMERATE_SYSCALL(close)                  \
-    __ENUMERATE_SYSCALL(read)                   \
-    __ENUMERATE_SYSCALL(lseek)                  \
-    __ENUMERATE_SYSCALL(kill)                   \
-    __ENUMERATE_SYSCALL(getuid)                 \
-    __ENUMERATE_SYSCALL(exit)                   \
-    __ENUMERATE_SYSCALL(getgid)                 \
-    __ENUMERATE_SYSCALL(getpid)                 \
-    __ENUMERATE_SYSCALL(waitpid)                \
-    __ENUMERATE_SYSCALL(mmap)                   \
-    __ENUMERATE_SYSCALL(munmap)                 \
-    __ENUMERATE_SYSCALL(get_dir_entries)        \
-    __ENUMERATE_SYSCALL(lstat)                  \
-    __ENUMERATE_SYSCALL(getcwd)                 \
-    __ENUMERATE_SYSCALL(gettimeofday)           \
-    __ENUMERATE_SYSCALL(gethostname)            \
-    __ENUMERATE_SYSCALL(chdir)                  \
-    __ENUMERATE_SYSCALL(uname)                  \
-    __ENUMERATE_SYSCALL(set_mmap_name)          \
-    __ENUMERATE_SYSCALL(readlink)               \
-    __ENUMERATE_SYSCALL(write)                  \
-    __ENUMERATE_SYSCALL(ttyname_r)              \
-    __ENUMERATE_SYSCALL(stat)                   \
-    __ENUMERATE_SYSCALL(getsid)                 \
-    __ENUMERATE_SYSCALL(setsid)                 \
-    __ENUMERATE_SYSCALL(getpgid)                \
-    __ENUMERATE_SYSCALL(setpgid)                \
-    __ENUMERATE_SYSCALL(getpgrp)                \
-    __ENUMERATE_SYSCALL(fork)                   \
-    __ENUMERATE_SYSCALL(execve)                 \
-    __ENUMERATE_SYSCALL(geteuid)                \
-    __ENUMERATE_SYSCALL(getegid)                \
-    __ENUMERATE_REMOVED_SYSCALL(isatty)         \
-    __ENUMERATE_SYSCALL(getdtablesize)          \
-    __ENUMERATE_SYSCALL(dup)                    \
-    __ENUMERATE_SYSCALL(dup2)                   \
-    __ENUMERATE_SYSCALL(sigaction)              \
-    __ENUMERATE_SYSCALL(getppid)                \
-    __ENUMERATE_SYSCALL(umask)                  \
-    __ENUMERATE_SYSCALL(getgroups)              \
-    __ENUMERATE_SYSCALL(setgroups)              \
-    __ENUMERATE_SYSCALL(sigreturn)              \
-    __ENUMERATE_SYSCALL(sigprocmask)            \
-    __ENUMERATE_SYSCALL(sigpending)             \
-    __ENUMERATE_SYSCALL(pipe)                   \
-    __ENUMERATE_SYSCALL(killpg)                 \
-    __ENUMERATE_SYSCALL(setuid)                 \
-    __ENUMERATE_SYSCALL(setgid)                 \
-    __ENUMERATE_SYSCALL(alarm)                  \
-    __ENUMERATE_SYSCALL(fstat)                  \
-    __ENUMERATE_SYSCALL(access)                 \
-    __ENUMERATE_SYSCALL(fcntl)                  \
-    __ENUMERATE_SYSCALL(ioctl)                  \
-    __ENUMERATE_SYSCALL(mkdir)                  \
-    __ENUMERATE_SYSCALL(times)                  \
-    __ENUMERATE_SYSCALL(utime)                  \
-    __ENUMERATE_SYSCALL(sync)                   \
-    __ENUMERATE_SYSCALL(ptsname_r)              \
-    __ENUMERATE_SYSCALL(select)                 \
-    __ENUMERATE_SYSCALL(unlink)                 \
-    __ENUMERATE_SYSCALL(poll)                   \
-    __ENUMERATE_SYSCALL(read_tsc)               \
-    __ENUMERATE_SYSCALL(rmdir)                  \
-    __ENUMERATE_SYSCALL(chmod)                  \
-    __ENUMERATE_SYSCALL(usleep)                 \
-    __ENUMERATE_SYSCALL(socket)                 \
-    __ENUMERATE_SYSCALL(bind)                   \
-    __ENUMERATE_SYSCALL(accept)                 \
-    __ENUMERATE_SYSCALL(listen)                 \
-    __ENUMERATE_SYSCALL(connect)                \
-    __ENUMERATE_SYSCALL(create_shared_buffer)   \
-    __ENUMERATE_SYSCALL(share_buffer_with)      \
-    __ENUMERATE_SYSCALL(get_shared_buffer)      \
-    __ENUMERATE_SYSCALL(release_shared_buffer)  \
-    __ENUMERATE_SYSCALL(link)                   \
-    __ENUMERATE_SYSCALL(chown)                  \
-    __ENUMERATE_SYSCALL(fchmod)                 \
-    __ENUMERATE_SYSCALL(symlink)                \
-    __ENUMERATE_SYSCALL(restore_signal_mask)    \
-    __ENUMERATE_SYSCALL(get_shared_buffer_size) \
-    __ENUMERATE_SYSCALL(seal_shared_buffer)     \
-    __ENUMERATE_SYSCALL(sendto)                 \
-    __ENUMERATE_SYSCALL(recvfrom)               \
-    __ENUMERATE_SYSCALL(getsockopt)             \
-    __ENUMERATE_SYSCALL(setsockopt)             \
-    __ENUMERATE_SYSCALL(create_thread)          \
-    __ENUMERATE_SYSCALL(gettid)                 \
-    __ENUMERATE_SYSCALL(donate)                 \
-    __ENUMERATE_SYSCALL(rename)                 \
-    __ENUMERATE_SYSCALL(shm_open)               \
-    __ENUMERATE_SYSCALL(shm_unlink)             \
-    __ENUMERATE_SYSCALL(ftruncate)              \
-    __ENUMERATE_SYSCALL(systrace)               \
-    __ENUMERATE_SYSCALL(exit_thread)            \
-    __ENUMERATE_SYSCALL(mknod)                  \
-    __ENUMERATE_SYSCALL(writev)                 \
-    __ENUMERATE_SYSCALL(beep)                   \
-    __ENUMERATE_SYSCALL(getsockname)            \
-    __ENUMERATE_SYSCALL(getpeername)            \
-    __ENUMERATE_SYSCALL(sched_setparam)         \
-    __ENUMERATE_SYSCALL(sched_getparam)         \
-    __ENUMERATE_SYSCALL(fchown)                 \
-    __ENUMERATE_SYSCALL(halt)                   \
-    __ENUMERATE_SYSCALL(reboot)                 \
-    __ENUMERATE_SYSCALL(mount)                  \
-    __ENUMERATE_SYSCALL(umount)                 \
-    __ENUMERATE_SYSCALL(dump_backtrace)         \
-    __ENUMERATE_SYSCALL(dbgputch)               \
-    __ENUMERATE_SYSCALL(dbgputstr)              \
-    __ENUMERATE_SYSCALL(watch_file)             \
-    __ENUMERATE_SYSCALL(share_buffer_globally)  \
-    __ENUMERATE_SYSCALL(set_process_icon)       \
-    __ENUMERATE_SYSCALL(mprotect)               \
-    __ENUMERATE_SYSCALL(realpath)               \
-    __ENUMERATE_SYSCALL(get_process_name)       \
-    __ENUMERATE_SYSCALL(fchdir)                 \
-    __ENUMERATE_SYSCALL(getrandom)              \
-    __ENUMERATE_SYSCALL(setkeymap)              \
-    __ENUMERATE_SYSCALL(clock_gettime)          \
-    __ENUMERATE_SYSCALL(clock_nanosleep)        \
-    __ENUMERATE_SYSCALL(openat)                 \
-    __ENUMERATE_SYSCALL(join_thread)            \
-    __ENUMERATE_SYSCALL(module_load)            \
-    __ENUMERATE_SYSCALL(module_unload)          \
-    __ENUMERATE_SYSCALL(detach_thread)          \
-    __ENUMERATE_SYSCALL(set_thread_name)        \
-    __ENUMERATE_SYSCALL(get_thread_name)        \
-    __ENUMERATE_SYSCALL(madvise)                \
-    __ENUMERATE_SYSCALL(purge)                  \
-    __ENUMERATE_SYSCALL(set_shared_buffer_volatile)
+#define ENUMERATE_SYSCALLS                          \
+    __ENUMERATE_SYSCALL(sleep)                      \
+    __ENUMERATE_SYSCALL(yield)                      \
+    __ENUMERATE_SYSCALL(putch)                      \
+    __ENUMERATE_SYSCALL(open)                       \
+    __ENUMERATE_SYSCALL(close)                      \
+    __ENUMERATE_SYSCALL(read)                       \
+    __ENUMERATE_SYSCALL(lseek)                      \
+    __ENUMERATE_SYSCALL(kill)                       \
+    __ENUMERATE_SYSCALL(getuid)                     \
+    __ENUMERATE_SYSCALL(exit)                       \
+    __ENUMERATE_SYSCALL(getgid)                     \
+    __ENUMERATE_SYSCALL(getpid)                     \
+    __ENUMERATE_SYSCALL(waitpid)                    \
+    __ENUMERATE_SYSCALL(mmap)                       \
+    __ENUMERATE_SYSCALL(munmap)                     \
+    __ENUMERATE_SYSCALL(get_dir_entries)            \
+    __ENUMERATE_SYSCALL(lstat)                      \
+    __ENUMERATE_SYSCALL(getcwd)                     \
+    __ENUMERATE_SYSCALL(gettimeofday)               \
+    __ENUMERATE_SYSCALL(gethostname)                \
+    __ENUMERATE_SYSCALL(chdir)                      \
+    __ENUMERATE_SYSCALL(uname)                      \
+    __ENUMERATE_SYSCALL(set_mmap_name)              \
+    __ENUMERATE_SYSCALL(readlink)                   \
+    __ENUMERATE_SYSCALL(write)                      \
+    __ENUMERATE_SYSCALL(ttyname_r)                  \
+    __ENUMERATE_SYSCALL(stat)                       \
+    __ENUMERATE_SYSCALL(getsid)                     \
+    __ENUMERATE_SYSCALL(setsid)                     \
+    __ENUMERATE_SYSCALL(getpgid)                    \
+    __ENUMERATE_SYSCALL(setpgid)                    \
+    __ENUMERATE_SYSCALL(getpgrp)                    \
+    __ENUMERATE_SYSCALL(fork)                       \
+    __ENUMERATE_SYSCALL(execve)                     \
+    __ENUMERATE_SYSCALL(geteuid)                    \
+    __ENUMERATE_SYSCALL(getegid)                    \
+    __ENUMERATE_REMOVED_SYSCALL(isatty)             \
+    __ENUMERATE_SYSCALL(getdtablesize)              \
+    __ENUMERATE_SYSCALL(dup)                        \
+    __ENUMERATE_SYSCALL(dup2)                       \
+    __ENUMERATE_SYSCALL(sigaction)                  \
+    __ENUMERATE_SYSCALL(getppid)                    \
+    __ENUMERATE_SYSCALL(umask)                      \
+    __ENUMERATE_SYSCALL(getgroups)                  \
+    __ENUMERATE_SYSCALL(setgroups)                  \
+    __ENUMERATE_SYSCALL(sigreturn)                  \
+    __ENUMERATE_SYSCALL(sigprocmask)                \
+    __ENUMERATE_SYSCALL(sigpending)                 \
+    __ENUMERATE_SYSCALL(pipe)                       \
+    __ENUMERATE_SYSCALL(killpg)                     \
+    __ENUMERATE_SYSCALL(setuid)                     \
+    __ENUMERATE_SYSCALL(setgid)                     \
+    __ENUMERATE_SYSCALL(alarm)                      \
+    __ENUMERATE_SYSCALL(fstat)                      \
+    __ENUMERATE_SYSCALL(access)                     \
+    __ENUMERATE_SYSCALL(fcntl)                      \
+    __ENUMERATE_SYSCALL(ioctl)                      \
+    __ENUMERATE_SYSCALL(mkdir)                      \
+    __ENUMERATE_SYSCALL(times)                      \
+    __ENUMERATE_SYSCALL(utime)                      \
+    __ENUMERATE_SYSCALL(sync)                       \
+    __ENUMERATE_SYSCALL(ptsname_r)                  \
+    __ENUMERATE_SYSCALL(select)                     \
+    __ENUMERATE_SYSCALL(unlink)                     \
+    __ENUMERATE_SYSCALL(poll)                       \
+    __ENUMERATE_SYSCALL(read_tsc)                   \
+    __ENUMERATE_SYSCALL(rmdir)                      \
+    __ENUMERATE_SYSCALL(chmod)                      \
+    __ENUMERATE_SYSCALL(usleep)                     \
+    __ENUMERATE_SYSCALL(socket)                     \
+    __ENUMERATE_SYSCALL(bind)                       \
+    __ENUMERATE_SYSCALL(accept)                     \
+    __ENUMERATE_SYSCALL(listen)                     \
+    __ENUMERATE_SYSCALL(connect)                    \
+    __ENUMERATE_SYSCALL(create_shared_buffer)       \
+    __ENUMERATE_SYSCALL(share_buffer_with)          \
+    __ENUMERATE_SYSCALL(get_shared_buffer)          \
+    __ENUMERATE_SYSCALL(release_shared_buffer)      \
+    __ENUMERATE_SYSCALL(link)                       \
+    __ENUMERATE_SYSCALL(chown)                      \
+    __ENUMERATE_SYSCALL(fchmod)                     \
+    __ENUMERATE_SYSCALL(symlink)                    \
+    __ENUMERATE_SYSCALL(restore_signal_mask)        \
+    __ENUMERATE_SYSCALL(get_shared_buffer_size)     \
+    __ENUMERATE_SYSCALL(seal_shared_buffer)         \
+    __ENUMERATE_SYSCALL(sendto)                     \
+    __ENUMERATE_SYSCALL(recvfrom)                   \
+    __ENUMERATE_SYSCALL(getsockopt)                 \
+    __ENUMERATE_SYSCALL(setsockopt)                 \
+    __ENUMERATE_SYSCALL(create_thread)              \
+    __ENUMERATE_SYSCALL(gettid)                     \
+    __ENUMERATE_SYSCALL(donate)                     \
+    __ENUMERATE_SYSCALL(rename)                     \
+    __ENUMERATE_SYSCALL(shm_open)                   \
+    __ENUMERATE_SYSCALL(shm_unlink)                 \
+    __ENUMERATE_SYSCALL(ftruncate)                  \
+    __ENUMERATE_SYSCALL(systrace)                   \
+    __ENUMERATE_SYSCALL(exit_thread)                \
+    __ENUMERATE_SYSCALL(mknod)                      \
+    __ENUMERATE_SYSCALL(writev)                     \
+    __ENUMERATE_SYSCALL(beep)                       \
+    __ENUMERATE_SYSCALL(getsockname)                \
+    __ENUMERATE_SYSCALL(getpeername)                \
+    __ENUMERATE_SYSCALL(sched_setparam)             \
+    __ENUMERATE_SYSCALL(sched_getparam)             \
+    __ENUMERATE_SYSCALL(fchown)                     \
+    __ENUMERATE_SYSCALL(halt)                       \
+    __ENUMERATE_SYSCALL(reboot)                     \
+    __ENUMERATE_SYSCALL(mount)                      \
+    __ENUMERATE_SYSCALL(umount)                     \
+    __ENUMERATE_SYSCALL(dump_backtrace)             \
+    __ENUMERATE_SYSCALL(dbgputch)                   \
+    __ENUMERATE_SYSCALL(dbgputstr)                  \
+    __ENUMERATE_SYSCALL(watch_file)                 \
+    __ENUMERATE_SYSCALL(share_buffer_globally)      \
+    __ENUMERATE_SYSCALL(set_process_icon)           \
+    __ENUMERATE_SYSCALL(mprotect)                   \
+    __ENUMERATE_SYSCALL(realpath)                   \
+    __ENUMERATE_SYSCALL(get_process_name)           \
+    __ENUMERATE_SYSCALL(fchdir)                     \
+    __ENUMERATE_SYSCALL(getrandom)                  \
+    __ENUMERATE_SYSCALL(setkeymap)                  \
+    __ENUMERATE_SYSCALL(clock_gettime)              \
+    __ENUMERATE_SYSCALL(clock_nanosleep)            \
+    __ENUMERATE_SYSCALL(openat)                     \
+    __ENUMERATE_SYSCALL(join_thread)                \
+    __ENUMERATE_SYSCALL(module_load)                \
+    __ENUMERATE_SYSCALL(module_unload)              \
+    __ENUMERATE_SYSCALL(detach_thread)              \
+    __ENUMERATE_SYSCALL(set_thread_name)            \
+    __ENUMERATE_SYSCALL(get_thread_name)            \
+    __ENUMERATE_SYSCALL(madvise)                    \
+    __ENUMERATE_SYSCALL(purge)                      \
+    __ENUMERATE_SYSCALL(set_shared_buffer_volatile) \
+    __ENUMERATE_SYSCALL(profiling_enable)           \
+    __ENUMERATE_SYSCALL(profiling_disable)
 
 namespace Syscall {
 

+ 13 - 0
Kernel/Thread.cpp

@@ -707,6 +707,19 @@ String Thread::backtrace_impl() const
     return builder.to_string();
 }
 
+Vector<u32> Thread::raw_backtrace(u32 ebp) const
+{
+    auto& process = const_cast<Process&>(this->process());
+    ProcessPagingScope paging_scope(process);
+    Vector<u32> backtrace;
+    backtrace.append(ebp);
+    for (u32* stack_ptr = (u32*)ebp; process.validate_read_from_kernel(VirtualAddress((u32)stack_ptr)); stack_ptr = (u32*)*stack_ptr) {
+        u32 retaddr = stack_ptr[1];
+        backtrace.append(retaddr);
+    }
+    return backtrace;
+}
+
 void Thread::make_thread_specific_region(Badge<Process>)
 {
     size_t thread_specific_region_alignment = max(process().m_master_tls_alignment, alignof(ThreadSpecificData));

+ 1 - 0
Kernel/Thread.h

@@ -72,6 +72,7 @@ public:
     const Process& process() const { return m_process; }
 
     String backtrace(ProcessInspectionHandle&) const;
+    Vector<u32> raw_backtrace(u32 ebp) const;
 
     const String& name() const { return m_name; }
     void set_name(StringView s) { m_name = s; }

+ 11 - 0
Libraries/LibC/serenity.cpp

@@ -16,4 +16,15 @@ int module_unload(const char* name, size_t name_length)
     __RETURN_WITH_ERRNO(rc, rc, -1);
 }
 
+int profiling_enable(pid_t pid)
+{
+    int rc = syscall(SC_profiling_enable, pid);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
+
+int profiling_disable(pid_t pid)
+{
+    int rc = syscall(SC_profiling_disable, pid);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
 }

+ 3 - 0
Libraries/LibC/serenity.h

@@ -41,4 +41,7 @@ __BEGIN_DECLS
 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_disable(pid_t);
+
 __END_DECLS

+ 29 - 0
Userland/profile.cpp

@@ -0,0 +1,29 @@
+#include <serenity.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char** argv)
+{
+    if (argc != 3) {
+        printf("usage: profile <pid> <on|off>\n");
+        return 0;
+    }
+
+    pid_t pid = atoi(argv[1]);
+    bool enabled = !strcmp(argv[2], "on");
+
+    if (enabled) {
+        if (profiling_enable(pid) < 0) {
+            perror("profiling_enable");
+            return 1;
+        }
+        return 0;
+    }
+
+    if (profiling_disable(pid) < 0) {
+        perror("profiling_disable");
+        return 1;
+    }
+    return 0;
+}