mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:27:43 +00:00
Profiler: Add a "Signposts" tab next to the "Samples" tab
This tab provides a filtered listing of all the signpost events in the currently selected time range.
This commit is contained in:
parent
9a334ebb3a
commit
8f590cbeb8
6 changed files with 178 additions and 1 deletions
|
@ -12,6 +12,7 @@ set(SOURCES
|
||||||
Profile.cpp
|
Profile.cpp
|
||||||
ProfileModel.cpp
|
ProfileModel.cpp
|
||||||
SamplesModel.cpp
|
SamplesModel.cpp
|
||||||
|
SignpostsModel.cpp
|
||||||
TimelineContainer.cpp
|
TimelineContainer.cpp
|
||||||
TimelineHeader.cpp
|
TimelineHeader.cpp
|
||||||
TimelineTrack.cpp
|
TimelineTrack.cpp
|
||||||
|
|
|
@ -45,6 +45,7 @@ Profile::Profile(Vector<Process> processes, Vector<Event> events)
|
||||||
|
|
||||||
m_model = ProfileModel::create(*this);
|
m_model = ProfileModel::create(*this);
|
||||||
m_samples_model = SamplesModel::create(*this);
|
m_samples_model = SamplesModel::create(*this);
|
||||||
|
m_signposts_model = SignpostsModel::create(*this);
|
||||||
|
|
||||||
rebuild_tree();
|
rebuild_tree();
|
||||||
}
|
}
|
||||||
|
@ -59,6 +60,11 @@ GUI::Model& Profile::samples_model()
|
||||||
return *m_samples_model;
|
return *m_samples_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GUI::Model& Profile::signposts_model()
|
||||||
|
{
|
||||||
|
return *m_signposts_model;
|
||||||
|
}
|
||||||
|
|
||||||
void Profile::rebuild_tree()
|
void Profile::rebuild_tree()
|
||||||
{
|
{
|
||||||
Vector<NonnullRefPtr<ProfileNode>> roots;
|
Vector<NonnullRefPtr<ProfileNode>> roots;
|
||||||
|
@ -92,6 +98,7 @@ void Profile::rebuild_tree()
|
||||||
});
|
});
|
||||||
|
|
||||||
m_filtered_event_indices.clear();
|
m_filtered_event_indices.clear();
|
||||||
|
m_filtered_signpost_indices.clear();
|
||||||
|
|
||||||
for (size_t event_index = 0; event_index < m_events.size(); ++event_index) {
|
for (size_t event_index = 0; event_index < m_events.size(); ++event_index) {
|
||||||
auto& event = m_events.at(event_index);
|
auto& event = m_events.at(event_index);
|
||||||
|
@ -105,8 +112,10 @@ void Profile::rebuild_tree()
|
||||||
if (!process_filter_contains(event.pid, event.serial))
|
if (!process_filter_contains(event.pid, event.serial))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (event.data.has<Event::SignpostData>())
|
if (event.data.has<Event::SignpostData>()) {
|
||||||
|
m_filtered_signpost_indices.append(event_index);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
m_filtered_event_indices.append(event_index);
|
m_filtered_event_indices.append(event_index);
|
||||||
|
|
||||||
|
@ -435,6 +444,7 @@ void Profile::set_timestamp_filter_range(u64 start, u64 end)
|
||||||
|
|
||||||
rebuild_tree();
|
rebuild_tree();
|
||||||
m_samples_model->invalidate();
|
m_samples_model->invalidate();
|
||||||
|
m_signposts_model->invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile::clear_timestamp_filter_range()
|
void Profile::clear_timestamp_filter_range()
|
||||||
|
@ -444,6 +454,7 @@ void Profile::clear_timestamp_filter_range()
|
||||||
m_has_timestamp_filter_range = false;
|
m_has_timestamp_filter_range = false;
|
||||||
rebuild_tree();
|
rebuild_tree();
|
||||||
m_samples_model->invalidate();
|
m_samples_model->invalidate();
|
||||||
|
m_signposts_model->invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile::add_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid)
|
void Profile::add_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid)
|
||||||
|
@ -457,6 +468,7 @@ void Profile::add_process_filter(pid_t pid, EventSerialNumber start_valid, Event
|
||||||
if (m_disassembly_model)
|
if (m_disassembly_model)
|
||||||
m_disassembly_model->invalidate();
|
m_disassembly_model->invalidate();
|
||||||
m_samples_model->invalidate();
|
m_samples_model->invalidate();
|
||||||
|
m_signposts_model->invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile::remove_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid)
|
void Profile::remove_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid)
|
||||||
|
@ -472,6 +484,7 @@ void Profile::remove_process_filter(pid_t pid, EventSerialNumber start_valid, Ev
|
||||||
if (m_disassembly_model)
|
if (m_disassembly_model)
|
||||||
m_disassembly_model->invalidate();
|
m_disassembly_model->invalidate();
|
||||||
m_samples_model->invalidate();
|
m_samples_model->invalidate();
|
||||||
|
m_signposts_model->invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile::clear_process_filter()
|
void Profile::clear_process_filter()
|
||||||
|
@ -483,6 +496,7 @@ void Profile::clear_process_filter()
|
||||||
if (m_disassembly_model)
|
if (m_disassembly_model)
|
||||||
m_disassembly_model->invalidate();
|
m_disassembly_model->invalidate();
|
||||||
m_samples_model->invalidate();
|
m_samples_model->invalidate();
|
||||||
|
m_signposts_model->invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Profile::process_filter_contains(pid_t pid, EventSerialNumber serial)
|
bool Profile::process_filter_contains(pid_t pid, EventSerialNumber serial)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Profile.h"
|
#include "Profile.h"
|
||||||
#include "ProfileModel.h"
|
#include "ProfileModel.h"
|
||||||
#include "SamplesModel.h"
|
#include "SamplesModel.h"
|
||||||
|
#include "SignpostsModel.h"
|
||||||
#include <AK/Bitmap.h>
|
#include <AK/Bitmap.h>
|
||||||
#include <AK/FlyString.h>
|
#include <AK/FlyString.h>
|
||||||
#include <AK/JsonArray.h>
|
#include <AK/JsonArray.h>
|
||||||
|
@ -142,6 +143,7 @@ public:
|
||||||
|
|
||||||
GUI::Model& model();
|
GUI::Model& model();
|
||||||
GUI::Model& samples_model();
|
GUI::Model& samples_model();
|
||||||
|
GUI::Model& signposts_model();
|
||||||
GUI::Model* disassembly_model();
|
GUI::Model* disassembly_model();
|
||||||
|
|
||||||
const Process* find_process(pid_t pid, EventSerialNumber serial) const
|
const Process* find_process(pid_t pid, EventSerialNumber serial) const
|
||||||
|
@ -219,6 +221,7 @@ public:
|
||||||
|
|
||||||
Vector<Event> const& events() const { return m_events; }
|
Vector<Event> const& events() const { return m_events; }
|
||||||
const Vector<size_t>& filtered_event_indices() const { return m_filtered_event_indices; }
|
const Vector<size_t>& filtered_event_indices() const { return m_filtered_event_indices; }
|
||||||
|
const Vector<size_t>& filtered_signpost_indices() const { return m_filtered_signpost_indices; }
|
||||||
|
|
||||||
u64 length_in_ms() const { return m_last_timestamp - m_first_timestamp; }
|
u64 length_in_ms() const { return m_last_timestamp - m_first_timestamp; }
|
||||||
u64 first_timestamp() const { return m_first_timestamp; }
|
u64 first_timestamp() const { return m_first_timestamp; }
|
||||||
|
@ -274,6 +277,7 @@ private:
|
||||||
|
|
||||||
RefPtr<ProfileModel> m_model;
|
RefPtr<ProfileModel> m_model;
|
||||||
RefPtr<SamplesModel> m_samples_model;
|
RefPtr<SamplesModel> m_samples_model;
|
||||||
|
RefPtr<SignpostsModel> m_signposts_model;
|
||||||
RefPtr<DisassemblyModel> m_disassembly_model;
|
RefPtr<DisassemblyModel> m_disassembly_model;
|
||||||
|
|
||||||
GUI::ModelIndex m_disassembly_index;
|
GUI::ModelIndex m_disassembly_index;
|
||||||
|
@ -286,6 +290,7 @@ private:
|
||||||
Vector<Process> m_processes;
|
Vector<Process> m_processes;
|
||||||
Vector<Event> m_events;
|
Vector<Event> m_events;
|
||||||
Vector<size_t> m_signpost_indices;
|
Vector<size_t> m_signpost_indices;
|
||||||
|
Vector<size_t> m_filtered_signpost_indices;
|
||||||
|
|
||||||
bool m_has_timestamp_filter_range { false };
|
bool m_has_timestamp_filter_range { false };
|
||||||
u64 m_timestamp_filter_range_start { 0 };
|
u64 m_timestamp_filter_range_start { 0 };
|
||||||
|
|
95
Userland/DevTools/Profiler/SignpostsModel.cpp
Normal file
95
Userland/DevTools/Profiler/SignpostsModel.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SignpostsModel.h"
|
||||||
|
#include "Profile.h"
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
|
|
||||||
|
namespace Profiler {
|
||||||
|
|
||||||
|
SignpostsModel::SignpostsModel(Profile& profile)
|
||||||
|
: m_profile(profile)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SignpostsModel::~SignpostsModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int SignpostsModel::row_count(const GUI::ModelIndex&) const
|
||||||
|
{
|
||||||
|
return m_profile.filtered_signpost_indices().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SignpostsModel::column_count(const GUI::ModelIndex&) const
|
||||||
|
{
|
||||||
|
return Column::__Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
String SignpostsModel::column_name(int column) const
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case Column::SignpostIndex:
|
||||||
|
return "#";
|
||||||
|
case Column::Timestamp:
|
||||||
|
return "Timestamp";
|
||||||
|
case Column::ProcessID:
|
||||||
|
return "PID";
|
||||||
|
case Column::ThreadID:
|
||||||
|
return "TID";
|
||||||
|
case Column::ExecutableName:
|
||||||
|
return "Executable";
|
||||||
|
case Column::SignpostString:
|
||||||
|
return "String";
|
||||||
|
case Column::SignpostArgument:
|
||||||
|
return "Argument";
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI::Variant SignpostsModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||||
|
{
|
||||||
|
u32 event_index = m_profile.filtered_signpost_indices()[index.row()];
|
||||||
|
auto& event = m_profile.events().at(event_index);
|
||||||
|
|
||||||
|
if (role == GUI::ModelRole::Custom) {
|
||||||
|
return event_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == GUI::ModelRole::Display) {
|
||||||
|
if (index.column() == Column::SignpostIndex)
|
||||||
|
return event_index;
|
||||||
|
|
||||||
|
if (index.column() == Column::ProcessID)
|
||||||
|
return event.pid;
|
||||||
|
|
||||||
|
if (index.column() == Column::ThreadID)
|
||||||
|
return event.tid;
|
||||||
|
|
||||||
|
if (index.column() == Column::ExecutableName) {
|
||||||
|
if (auto* process = m_profile.find_process(event.pid, event.serial))
|
||||||
|
return process->executable;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() == Column::Timestamp) {
|
||||||
|
return (u32)event.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() == Column::SignpostString) {
|
||||||
|
return event.data.get<Profile::Event::SignpostData>().string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() == Column::SignpostArgument) {
|
||||||
|
return event.data.get<Profile::Event::SignpostData>().arg;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
Userland/DevTools/Profiler/SignpostsModel.h
Normal file
47
Userland/DevTools/Profiler/SignpostsModel.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/Model.h>
|
||||||
|
|
||||||
|
namespace Profiler {
|
||||||
|
|
||||||
|
class Profile;
|
||||||
|
|
||||||
|
class SignpostsModel final : public GUI::Model {
|
||||||
|
public:
|
||||||
|
static NonnullRefPtr<SignpostsModel> create(Profile& profile)
|
||||||
|
{
|
||||||
|
return adopt_ref(*new SignpostsModel(profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Column {
|
||||||
|
SignpostIndex,
|
||||||
|
Timestamp,
|
||||||
|
ProcessID,
|
||||||
|
ThreadID,
|
||||||
|
ExecutableName,
|
||||||
|
SignpostString,
|
||||||
|
SignpostArgument,
|
||||||
|
__Count
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~SignpostsModel() override;
|
||||||
|
|
||||||
|
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||||
|
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||||
|
virtual String column_name(int) const override;
|
||||||
|
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||||
|
virtual bool is_column_sortable(int) const override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit SignpostsModel(Profile&);
|
||||||
|
|
||||||
|
Profile& m_profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -176,6 +176,21 @@ int main(int argc, char** argv)
|
||||||
individual_sample_view.set_model(move(model));
|
individual_sample_view.set_model(move(model));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto& signposts_tab = tab_widget.add_tab<GUI::Widget>("Signposts");
|
||||||
|
signposts_tab.set_layout<GUI::VerticalBoxLayout>();
|
||||||
|
signposts_tab.layout()->set_margins({ 4, 4, 4, 4 });
|
||||||
|
|
||||||
|
auto& signposts_splitter = signposts_tab.add<GUI::HorizontalSplitter>();
|
||||||
|
auto& signposts_table_view = signposts_splitter.add<GUI::TableView>();
|
||||||
|
signposts_table_view.set_model(profile->signposts_model());
|
||||||
|
|
||||||
|
auto& individual_signpost_view = signposts_splitter.add<GUI::TableView>();
|
||||||
|
signposts_table_view.on_selection_change = [&] {
|
||||||
|
const auto& index = signposts_table_view.selection().first();
|
||||||
|
auto model = IndividualSampleModel::create(*profile, index.data(GUI::ModelRole::Custom).to_integer<size_t>());
|
||||||
|
individual_signpost_view.set_model(move(model));
|
||||||
|
};
|
||||||
|
|
||||||
const u64 start_of_trace = profile->first_timestamp();
|
const u64 start_of_trace = profile->first_timestamp();
|
||||||
const u64 end_of_trace = start_of_trace + profile->length_in_ms();
|
const u64 end_of_trace = start_of_trace + profile->length_in_ms();
|
||||||
const auto clamp_timestamp = [start_of_trace, end_of_trace](u64 timestamp) -> u64 {
|
const auto clamp_timestamp = [start_of_trace, end_of_trace](u64 timestamp) -> u64 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue