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:
parent
3829bf115c
commit
521217735b
4 changed files with 91 additions and 1 deletions
|
@ -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));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue