ProfileViewer: Add basic support for loading "perfcore" files

"perfcore" is the file that the kernel generates after a process that
was recording performance events has exited.

This patch teaches ProfileViewer how to load (and symbolicate!) those
files so that we can look at them. This will need a bunch more work
to make it truly useful.
This commit is contained in:
Andreas Kling 2020-02-02 20:29:55 +01:00
parent fa97ff1c83
commit 266d7ca268
Notes: sideshowbarker 2024-07-19 09:41:17 +09:00
3 changed files with 74 additions and 1 deletions

View file

@ -26,8 +26,10 @@
#include "Profile.h"
#include "ProfileModel.h"
#include <AK/MappedFile.h>
#include <AK/QuickSort.h>
#include <LibCore/CFile.h>
#include <LibELF/ELFLoader.h>
#include <stdio.h>
static void sort_profile_nodes(Vector<NonnullRefPtr<ProfileNode>>& nodes)
@ -50,6 +52,7 @@ Profile::Profile(const JsonArray& json)
m_samples.ensure_capacity(m_json.size());
for (auto& sample_value : m_json.values()) {
auto& sample_object = sample_value.as_object();
Sample sample;
@ -155,6 +158,69 @@ void Profile::rebuild_tree()
m_model->update();
}
OwnPtr<Profile> Profile::load_from_perfcore_file(const StringView& path)
{
auto file = Core::File::construct(path);
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Unable to open %s, error: %s\n", String(path).characters(), file->error_string());
return nullptr;
}
auto json = JsonValue::from_string(file->read_all());
if (!json.is_object()) {
fprintf(stderr, "Invalid perfcore format (not a JSON object)\n");
return nullptr;
}
auto& object = json.as_object();
auto executable_path = object.get("executable").to_string();
MappedFile elf_file(executable_path);
if (!elf_file.is_valid()) {
fprintf(stderr, "Unable to open executable '%s' for symbolication.\n", executable_path.characters());
return nullptr;
}
auto elf_loader = make<ELFLoader>(static_cast<const u8*>(elf_file.data()), elf_file.size());
auto events_value = object.get("events");
if (!events_value.is_array())
return nullptr;
auto& perf_events = events_value.as_array();
if (perf_events.is_empty())
return nullptr;
JsonArray profile_events;
for (auto& perf_event_value : perf_events.values()) {
auto& perf_event = perf_event_value.as_object();
JsonObject object;
object.set("timestamp", perf_event.get("timestamp"));
JsonArray frames_array;
auto stack_array = perf_event.get("stack").as_array();
for (auto& frame : stack_array.values()) {
auto ptr = frame.to_number<u32>();
u32 offset = 0;
auto symbol = elf_loader->symbolicate(ptr, &offset);
JsonObject frame_object;
frame_object.set("address", ptr);
frame_object.set("symbol", symbol);
frame_object.set("offset", offset);
frames_array.append(move(frame_object));
}
object.set("frames", move(frames_array));
profile_events.append(move(object));
}
return NonnullOwnPtr<Profile>(NonnullOwnPtr<Profile>::Adopt, *new Profile(move(profile_events)));
}
OwnPtr<Profile> Profile::load_from_file(const StringView& path)
{
auto file = Core::File::construct(path);

View file

@ -105,6 +105,7 @@ private:
class Profile {
public:
static OwnPtr<Profile> load_from_file(const StringView& path);
static OwnPtr<Profile> load_from_perfcore_file(const StringView& path);
~Profile();
GUI::Model& model();

View file

@ -43,8 +43,14 @@ int main(int argc, char** argv)
}
const char* path = argv[1];
OwnPtr<Profile> profile;
if (!strcmp(path, "perfcore")) {
profile = Profile::load_from_perfcore_file(path);
} else {
profile = Profile::load_from_file(path);
}
auto profile = Profile::load_from_file(path);
if (!profile) {
fprintf(stderr, "Unable to load profile '%s'\n", path);
return 1;