1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 16:38:10 +00:00

UserspaceEmulator: Optionally generate a Profiler-compatible profile

`ue --profile --profile-file ~/some-file.profile id` can now generate a
full profile (instruction-by-instruction, if needed), at the cost of not
being able to see past the syscall boundary (a.la. callgrind).
This makes it significantly easier to profile seemingly fast userspace
things, like Loader.so :^)
This commit is contained in:
Ali Mohammad Pur 2021-08-05 22:42:31 +04:30 committed by Andreas Kling
parent 3829bf115c
commit 521217735b
4 changed files with 91 additions and 1 deletions

View file

@ -10,6 +10,7 @@
#include "SimpleRegion.h"
#include "SoftCPU.h"
#include <AK/Debug.h>
#include <AK/FileStream.h>
#include <AK/Format.h>
#include <AK/LexicalPath.h>
#include <AK/MappedFile.h>
@ -26,6 +27,10 @@
# pragma GCC optimize("O3")
#endif
extern bool g_dump_profile;
extern unsigned g_profile_instruction_interval;
extern Optional<OutputFileStream> g_profile_stream;
namespace UserspaceEmulator {
static constexpr u32 stack_location = 0x10000000;
@ -218,6 +223,10 @@ int Emulator::exec()
constexpr bool trace = false;
size_t instructions_until_next_profile_dump = g_profile_instruction_interval;
if (g_dump_profile && m_loader_text_size.has_value())
emit_profile_event(*g_profile_stream, "mmap", String::formatted(R"("ptr": {}, "size": {}, "name": "/usr/lib/Loader.so")", *m_loader_text_base, *m_loader_text_size));
while (!m_shutdown) {
if (m_steps_til_pause) [[likely]] {
m_cpu.save_base_eip();
@ -229,6 +238,15 @@ int Emulator::exec()
(m_cpu.*insn.handler())(insn);
if (g_dump_profile) {
if (instructions_until_next_profile_dump == 0) {
instructions_until_next_profile_dump = g_profile_instruction_interval;
emit_profile_sample(*g_profile_stream);
} else {
--instructions_until_next_profile_dump;
}
}
if constexpr (trace) {
m_cpu.dump();
}
@ -445,6 +463,26 @@ void Emulator::dump_backtrace()
dump_backtrace(raw_backtrace());
}
void Emulator::emit_profile_sample(AK::OutputStream& output)
{
StringBuilder builder;
timeval tv {};
gettimeofday(&tv, nullptr);
builder.appendff(R"~(, {{"type": "sample", "pid": {}, "tid": {}, "timestamp": {}, "lost_samples": 0, "stack": [)~", getpid(), gettid(), tv.tv_sec * 1000 + tv.tv_usec / 1000);
builder.join(',', raw_backtrace());
builder.append("]}");
output.write_or_error(builder.string_view().bytes());
}
void Emulator::emit_profile_event(AK::OutputStream& output, StringView event_name, String contents)
{
StringBuilder builder;
timeval tv {};
gettimeofday(&tv, nullptr);
builder.appendff(R"~(, {{"type": "{}", "pid": {}, "tid": {}, "timestamp": {}, "lost_samples": 0, "stack": [], {}}})~", event_name, getpid(), gettid(), tv.tv_sec * 1000 + tv.tv_usec / 1000, contents);
output.write_or_error(builder.string_view().bytes());
}
String Emulator::create_instruction_line(FlatPtr address, X86::Instruction insn)
{
auto minimal = String::formatted("{:p}: {}", (void*)address, insn.to_string(address));