mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 20:15:17 +00:00
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:
parent
adb1870628
commit
b32e961a84
Notes:
sideshowbarker
2024-07-19 10:53:01 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/b32e961a844
13 changed files with 388 additions and 135 deletions
|
@ -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 };
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ CXX_OBJS = \
|
|||
PCI.o \
|
||||
Process.o \
|
||||
ProcessTracer.o \
|
||||
Profiling.o \
|
||||
RTC.o \
|
||||
Scheduler.o \
|
||||
SharedBuffer.o \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
94
Kernel/Profiling.cpp
Normal 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
26
Kernel/Profiling.h
Normal 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&)>);
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
272
Kernel/Syscall.h
272
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 {
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
29
Userland/profile.cpp
Normal 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;
|
||||
}
|
Loading…
Add table
Reference in a new issue