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
This commit is contained in:
Andreas Kling 2019-12-11 20:36:56 +01:00
parent adb1870628
commit b32e961a84
Notes: sideshowbarker 2024-07-19 10:53:01 +09:00
13 changed files with 388 additions and 135 deletions

View file

@ -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 };

View file

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

View file

@ -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;
}

View file

@ -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
Kernel/Profiling.cpp Normal file
View file

@ -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
Kernel/Profiling.h Normal file
View file

@ -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&)>);
}

View file

@ -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;

View file

@ -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 {

View file

@ -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));

View file

@ -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; }

View file

@ -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);
}
}

View file

@ -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
Userland/profile.cpp Normal file
View file

@ -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;
}