diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-12-11 20:36:56 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-12-11 20:36:56 +0100 |
commit | b32e961a844121ed71f7f1e82011fea2d8e3469f (patch) | |
tree | 35c3e845a047ef980d2ce58f98d38f64c82c6bb1 /Kernel/Profiling.cpp | |
parent | adb1870628b3939f7e8463e016c8cd9e9de0e3d2 (diff) | |
download | serenity-b32e961a844121ed71f7f1e82011fea2d8e3469f.zip |
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
Diffstat (limited to 'Kernel/Profiling.cpp')
-rw-r--r-- | Kernel/Profiling.cpp | 94 |
1 files changed, 94 insertions, 0 deletions
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 <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); + } +} + +} |