mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:37:34 +00:00
Profiler: Use AK::Variant for type-specific data in Profile::Event
Each event has a different set of data depending on the event type.
This commit is contained in:
parent
f51f5e135a
commit
f5db92448d
3 changed files with 108 additions and 67 deletions
|
@ -76,11 +76,15 @@ void Profile::rebuild_tree()
|
||||||
|
|
||||||
HashTable<FlatPtr> live_allocations;
|
HashTable<FlatPtr> live_allocations;
|
||||||
|
|
||||||
for_each_event_in_filter_range([&](auto& event) {
|
for_each_event_in_filter_range([&](Event const& event) {
|
||||||
if (event.type == Event::Type::Malloc)
|
event.data.visit(
|
||||||
live_allocations.set(event.ptr);
|
[&](Event::MallocData const& data) {
|
||||||
else if (event.type == Event::Type::Free)
|
live_allocations.set(data.ptr);
|
||||||
live_allocations.remove(event.ptr);
|
},
|
||||||
|
[&](Event::FreeData const& data) {
|
||||||
|
live_allocations.remove(data.ptr);
|
||||||
|
},
|
||||||
|
[&](auto&) {});
|
||||||
});
|
});
|
||||||
|
|
||||||
m_filtered_event_indices.clear();
|
m_filtered_event_indices.clear();
|
||||||
|
@ -99,10 +103,10 @@ void Profile::rebuild_tree()
|
||||||
|
|
||||||
m_filtered_event_indices.append(event_index);
|
m_filtered_event_indices.append(event_index);
|
||||||
|
|
||||||
if (event.type == Event::Type::Malloc && !live_allocations.contains(event.ptr))
|
if (auto* malloc_data = event.data.get_pointer<Event::MallocData>(); malloc_data && !live_allocations.contains(malloc_data->ptr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (event.type == Event::Type::Free)
|
if (event.data.has<Event::FreeData>())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto for_each_frame = [&]<typename Callback>(Callback callback) {
|
auto for_each_frame = [&]<typename Callback>(Callback callback) {
|
||||||
|
@ -245,43 +249,55 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
auto type_string = perf_event.get("type").to_string();
|
auto type_string = perf_event.get("type").to_string();
|
||||||
|
|
||||||
if (type_string == "sample"sv) {
|
if (type_string == "sample"sv) {
|
||||||
event.type = Event::Type::Sample;
|
event.data = Event::SampleData {};
|
||||||
} else if (type_string == "malloc"sv) {
|
} else if (type_string == "malloc"sv) {
|
||||||
event.type = Event::Type::Malloc;
|
event.data = Event::MallocData {
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
.ptr = perf_event.get("ptr"sv).to_number<FlatPtr>(),
|
||||||
event.size = perf_event.get("size").to_number<size_t>();
|
.size = perf_event.get("size"sv).to_number<size_t>(),
|
||||||
|
};
|
||||||
} else if (type_string == "free"sv) {
|
} else if (type_string == "free"sv) {
|
||||||
event.type = Event::Type::Free;
|
event.data = Event::FreeData {
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
.ptr = perf_event.get("ptr"sv).to_number<FlatPtr>(),
|
||||||
|
};
|
||||||
} else if (type_string == "signpost"sv) {
|
} else if (type_string == "signpost"sv) {
|
||||||
event.type = Event::Type::Signpost;
|
auto string_id = perf_event.get("arg1"sv).to_number<FlatPtr>();
|
||||||
auto string_id = perf_event.get("arg1").to_number<FlatPtr>();
|
event.data = Event::SignpostData {
|
||||||
event.signpost_string = profile_strings.get(string_id).value_or(String::formatted("Signpost #{}", string_id));
|
.string = profile_strings.get(string_id).value_or(String::formatted("Signpost #{}", string_id)),
|
||||||
event.arg2 = perf_event.get("arg2").to_number<FlatPtr>();
|
.arg = perf_event.get("arg2"sv).to_number<FlatPtr>(),
|
||||||
|
};
|
||||||
} else if (type_string == "mmap"sv) {
|
} else if (type_string == "mmap"sv) {
|
||||||
event.type = Event::Type::Mmap;
|
auto ptr = perf_event.get("ptr"sv).to_number<FlatPtr>();
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
auto size = perf_event.get("size"sv).to_number<size_t>();
|
||||||
event.size = perf_event.get("size").to_number<size_t>();
|
auto name = perf_event.get("name"sv).to_string();
|
||||||
event.name = perf_event.get("name").to_string();
|
|
||||||
|
event.data = Event::MmapData {
|
||||||
|
.ptr = ptr,
|
||||||
|
.size = size,
|
||||||
|
.name = name,
|
||||||
|
};
|
||||||
|
|
||||||
auto it = current_processes.find(event.pid);
|
auto it = current_processes.find(event.pid);
|
||||||
if (it != current_processes.end())
|
if (it != current_processes.end())
|
||||||
it->value->library_metadata.handle_mmap(event.ptr, event.size, event.name);
|
it->value->library_metadata.handle_mmap(ptr, size, name);
|
||||||
continue;
|
continue;
|
||||||
} else if (type_string == "munmap"sv) {
|
} else if (type_string == "munmap"sv) {
|
||||||
event.type = Event::Type::Munmap;
|
event.data = Event::MunmapData {
|
||||||
event.ptr = perf_event.get("ptr").to_number<FlatPtr>();
|
.ptr = perf_event.get("ptr"sv).to_number<FlatPtr>(),
|
||||||
event.size = perf_event.get("size").to_number<size_t>();
|
.size = perf_event.get("size"sv).to_number<size_t>(),
|
||||||
|
};
|
||||||
continue;
|
continue;
|
||||||
} else if (type_string == "process_create"sv) {
|
} else if (type_string == "process_create"sv) {
|
||||||
event.type = Event::Type::ProcessCreate;
|
auto parent_pid = perf_event.get("parent_pid"sv).to_number<pid_t>();
|
||||||
event.parent_pid = perf_event.get("parent_pid").to_number<FlatPtr>();
|
auto executable = perf_event.get("executable"sv).to_string();
|
||||||
event.executable = perf_event.get("executable").to_string();
|
event.data = Event::ProcessCreateData {
|
||||||
|
.parent_pid = parent_pid,
|
||||||
|
.executable = executable,
|
||||||
|
};
|
||||||
|
|
||||||
auto sampled_process = adopt_own(*new Process {
|
auto sampled_process = adopt_own(*new Process {
|
||||||
.pid = event.pid,
|
.pid = event.pid,
|
||||||
.executable = event.executable,
|
.executable = executable,
|
||||||
.basename = LexicalPath::basename(event.executable),
|
.basename = LexicalPath::basename(executable),
|
||||||
.start_valid = event.serial,
|
.start_valid = event.serial,
|
||||||
.end_valid = {},
|
.end_valid = {},
|
||||||
});
|
});
|
||||||
|
@ -290,8 +306,10 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
all_processes.append(move(sampled_process));
|
all_processes.append(move(sampled_process));
|
||||||
continue;
|
continue;
|
||||||
} else if (type_string == "process_exec"sv) {
|
} else if (type_string == "process_exec"sv) {
|
||||||
event.type = Event::Type::ProcessExec;
|
auto executable = perf_event.get("executable"sv).to_string();
|
||||||
event.executable = perf_event.get("executable").to_string();
|
event.data = Event::ProcessExecData {
|
||||||
|
.executable = executable,
|
||||||
|
};
|
||||||
|
|
||||||
auto old_process = current_processes.get(event.pid).value();
|
auto old_process = current_processes.get(event.pid).value();
|
||||||
old_process->end_valid = event.serial;
|
old_process->end_valid = event.serial;
|
||||||
|
@ -300,8 +318,8 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
|
|
||||||
auto sampled_process = adopt_own(*new Process {
|
auto sampled_process = adopt_own(*new Process {
|
||||||
.pid = event.pid,
|
.pid = event.pid,
|
||||||
.executable = event.executable,
|
.executable = executable,
|
||||||
.basename = LexicalPath::basename(event.executable),
|
.basename = LexicalPath::basename(executable),
|
||||||
.start_valid = event.serial,
|
.start_valid = event.serial,
|
||||||
.end_valid = {},
|
.end_valid = {},
|
||||||
});
|
});
|
||||||
|
@ -310,21 +328,21 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
all_processes.append(move(sampled_process));
|
all_processes.append(move(sampled_process));
|
||||||
continue;
|
continue;
|
||||||
} else if (type_string == "process_exit"sv) {
|
} else if (type_string == "process_exit"sv) {
|
||||||
event.type = Event::Type::ProcessExit;
|
|
||||||
auto old_process = current_processes.get(event.pid).value();
|
auto old_process = current_processes.get(event.pid).value();
|
||||||
old_process->end_valid = event.serial;
|
old_process->end_valid = event.serial;
|
||||||
|
|
||||||
current_processes.remove(event.pid);
|
current_processes.remove(event.pid);
|
||||||
continue;
|
continue;
|
||||||
} else if (type_string == "thread_create"sv) {
|
} else if (type_string == "thread_create"sv) {
|
||||||
event.type = Event::Type::ThreadCreate;
|
auto parent_tid = perf_event.get("parent_tid"sv).to_number<pid_t>();
|
||||||
event.parent_tid = perf_event.get("parent_tid").to_i32();
|
event.data = Event::ThreadCreateData {
|
||||||
|
.parent_tid = parent_tid,
|
||||||
|
};
|
||||||
auto it = current_processes.find(event.pid);
|
auto it = current_processes.find(event.pid);
|
||||||
if (it != current_processes.end())
|
if (it != current_processes.end())
|
||||||
it->value->handle_thread_create(event.tid, event.serial);
|
it->value->handle_thread_create(event.tid, event.serial);
|
||||||
continue;
|
continue;
|
||||||
} else if (type_string == "thread_exit"sv) {
|
} else if (type_string == "thread_exit"sv) {
|
||||||
event.type = Event::Type::ThreadExit;
|
|
||||||
auto it = current_processes.find(event.pid);
|
auto it = current_processes.find(event.pid);
|
||||||
if (it != current_processes.end())
|
if (it != current_processes.end())
|
||||||
it->value->handle_thread_exit(event.tid, event.serial);
|
it->value->handle_thread_exit(event.tid, event.serial);
|
||||||
|
@ -375,7 +393,9 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
|
||||||
FlatPtr innermost_frame_address = event.frames.at(1).address;
|
FlatPtr innermost_frame_address = event.frames.at(1).address;
|
||||||
event.in_kernel = maybe_kernel_base.has_value() && innermost_frame_address >= maybe_kernel_base.value();
|
event.in_kernel = maybe_kernel_base.has_value() && innermost_frame_address >= maybe_kernel_base.value();
|
||||||
|
|
||||||
if (event.type == Event::Type::Signpost)
|
dbgln("size: {}", sizeof(Event));
|
||||||
|
|
||||||
|
if (event.data.has<Event::SignpostData>())
|
||||||
signposts.append(move(event));
|
signposts.append(move(event));
|
||||||
else
|
else
|
||||||
events.append(move(event));
|
events.append(move(event));
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <AK/NonnullRefPtrVector.h>
|
#include <AK/NonnullRefPtrVector.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/Result.h>
|
#include <AK/Result.h>
|
||||||
|
#include <AK/Variant.h>
|
||||||
#include <LibELF/Image.h>
|
#include <LibELF/Image.h>
|
||||||
#include <LibGUI/Forward.h>
|
#include <LibGUI/Forward.h>
|
||||||
#include <LibGUI/ModelIndex.h>
|
#include <LibGUI/ModelIndex.h>
|
||||||
|
@ -163,38 +164,57 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
enum class Type {
|
|
||||||
Free,
|
|
||||||
Malloc,
|
|
||||||
Mmap,
|
|
||||||
Munmap,
|
|
||||||
ProcessCreate,
|
|
||||||
ProcessExec,
|
|
||||||
ProcessExit,
|
|
||||||
Sample,
|
|
||||||
Signpost,
|
|
||||||
ThreadCreate,
|
|
||||||
ThreadExit,
|
|
||||||
};
|
|
||||||
Type type {};
|
|
||||||
EventSerialNumber serial;
|
|
||||||
u64 timestamp { 0 };
|
u64 timestamp { 0 };
|
||||||
FlatPtr ptr { 0 };
|
EventSerialNumber serial;
|
||||||
size_t size { 0 };
|
pid_t pid { 0 };
|
||||||
String name;
|
pid_t tid { 0 };
|
||||||
int parent_pid { 0 };
|
|
||||||
int parent_tid { 0 };
|
|
||||||
String executable;
|
|
||||||
int pid { 0 };
|
|
||||||
int tid { 0 };
|
|
||||||
u32 lost_samples { 0 };
|
u32 lost_samples { 0 };
|
||||||
bool in_kernel { false };
|
bool in_kernel { false };
|
||||||
|
|
||||||
// FIXME: Put event type-specific arguments in a union to save memory.
|
|
||||||
String signpost_string;
|
|
||||||
FlatPtr arg2 {};
|
|
||||||
|
|
||||||
Vector<Frame> frames;
|
Vector<Frame> frames;
|
||||||
|
|
||||||
|
struct SampleData {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MallocData {
|
||||||
|
FlatPtr ptr {};
|
||||||
|
size_t size {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FreeData {
|
||||||
|
FlatPtr ptr {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SignpostData {
|
||||||
|
String string;
|
||||||
|
FlatPtr arg {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MmapData {
|
||||||
|
FlatPtr ptr {};
|
||||||
|
size_t size {};
|
||||||
|
String name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MunmapData {
|
||||||
|
FlatPtr ptr {};
|
||||||
|
size_t size {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProcessCreateData {
|
||||||
|
pid_t parent_pid { 0 };
|
||||||
|
String executable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProcessExecData {
|
||||||
|
String executable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadCreateData {
|
||||||
|
pid_t parent_tid {};
|
||||||
|
};
|
||||||
|
|
||||||
|
Variant<std::nullptr_t, SampleData, MallocData, FreeData, SignpostData, MmapData, MunmapData, ProcessCreateData, ProcessExecData, ThreadCreateData> data { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
const Vector<Event>& events() const { return m_events; }
|
const Vector<Event>& events() const { return m_events; }
|
||||||
|
|
|
@ -135,7 +135,8 @@ void TimelineTrack::mousemove_event(GUI::MouseEvent& event)
|
||||||
constexpr int hoverable_padding = 2;
|
constexpr int hoverable_padding = 2;
|
||||||
Gfx::IntRect hoverable_rect { x - hoverable_padding, frame_thickness(), hoverable_padding * 2, height() - frame_thickness() * 2 };
|
Gfx::IntRect hoverable_rect { x - hoverable_padding, frame_thickness(), hoverable_padding * 2, height() - frame_thickness() * 2 };
|
||||||
if (hoverable_rect.contains_horizontally(event.x())) {
|
if (hoverable_rect.contains_horizontally(event.x())) {
|
||||||
GUI::Application::the()->show_tooltip_immediately(String::formatted("{}, {}", signpost.signpost_string, signpost.arg2), this);
|
auto const& data = signpost.data.template get<Profile::Event::SignpostData>();
|
||||||
|
GUI::Application::the()->show_tooltip_immediately(String::formatted("{}, {}", data.string, data.arg), this);
|
||||||
hovering_a_signpost = true;
|
hovering_a_signpost = true;
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue