diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 7de3e2f401..84237d4d9e 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -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 procfs$modules(InodeIdentifier) return builder.build(); } +Optional 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 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 }; diff --git a/Kernel/Makefile b/Kernel/Makefile index 932bcfef42..987a9a14db 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -76,6 +76,7 @@ CXX_OBJS = \ PCI.o \ Process.o \ ProcessTracer.o \ + Profiling.o \ RTC.o \ Scheduler.o \ SharedBuffer.o \ diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 83184cce94..6680bcac0c 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 53e3974dc3..8db6c109a6 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -44,6 +44,9 @@ public: static Vector all_pids(); static Vector 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 m_executable; RefPtr m_cwd; diff --git a/Kernel/Profiling.cpp b/Kernel/Profiling.cpp new file mode 100644 index 0000000000..44834220fe --- /dev/null +++ b/Kernel/Profiling.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include + +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(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 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 callback) +{ + for (size_t i = 0; i < s_next_slot_index; ++i) { + auto& sample = sample_slot(i); + callback(sample); + } +} + +} diff --git a/Kernel/Profiling.h b/Kernel/Profiling.h new file mode 100644 index 0000000000..54cb7a0cc8 --- /dev/null +++ b/Kernel/Profiling.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +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); + +} diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 5771456e42..f8849af97a 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index fe8de7babf..366ce1e999 100644 --- a/Kernel/Syscall.h +++ b/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 { diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index dbf30bb9eb..1d82218d35 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -707,6 +707,19 @@ String Thread::backtrace_impl() const return builder.to_string(); } +Vector Thread::raw_backtrace(u32 ebp) const +{ + auto& process = const_cast(this->process()); + ProcessPagingScope paging_scope(process); + Vector 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) { size_t thread_specific_region_alignment = max(process().m_master_tls_alignment, alignof(ThreadSpecificData)); diff --git a/Kernel/Thread.h b/Kernel/Thread.h index afc7de0c0f..6af40eed46 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -72,6 +72,7 @@ public: const Process& process() const { return m_process; } String backtrace(ProcessInspectionHandle&) const; + Vector raw_backtrace(u32 ebp) const; const String& name() const { return m_name; } void set_name(StringView s) { m_name = s; } diff --git a/Libraries/LibC/serenity.cpp b/Libraries/LibC/serenity.cpp index 22756fe6a9..4d26facc60 100644 --- a/Libraries/LibC/serenity.cpp +++ b/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); +} } diff --git a/Libraries/LibC/serenity.h b/Libraries/LibC/serenity.h index 26e0ebc5b6..6aa6026185 100644 --- a/Libraries/LibC/serenity.h +++ b/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 diff --git a/Userland/profile.cpp b/Userland/profile.cpp new file mode 100644 index 0000000000..b8d50820c3 --- /dev/null +++ b/Userland/profile.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 3) { + printf("usage: profile \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; +}