Skip to content

Commit

Permalink
ProfileViewer: Convert the JSON samples into a more efficient format
Browse files Browse the repository at this point in the history
Only do the conversion from JSON once. This makes it much faster to do
time range filtering with the timeline widget. :^)
  • Loading branch information
awesomekling committed Dec 15, 2019
1 parent d4a570d commit 063fef3
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 33 deletions.
55 changes: 34 additions & 21 deletions DevTools/ProfileViewer/Profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,30 @@ Profile::Profile(const JsonArray& json)
m_last_timestamp = m_json.at(m_json.size() - 1).as_object().get("timestamp").to_number<u64>();

m_model = ProfileModel::create(*this);
rebuild_tree();

m_sample_data.ensure_capacity(m_json.size());
m_json.for_each([&](const JsonValue& sample) {
u64 timestamp = sample.as_object().get("timestamp").to_number<u64>() - m_first_timestamp;
bool in_kernel = sample.as_object().get("frames").as_array().at(1).as_object().get("address").to_number<u32>() < (8 * MB);
m_sample_data.append({ timestamp, in_kernel });
});
m_samples.ensure_capacity(m_json.size());
for (auto& sample_value : m_json.values()) {
auto& sample_object = sample_value.as_object();

Sample sample;
sample.timestamp = sample_object.get("timestamp").to_number<u64>();
sample.in_kernel = sample_object.get("frames").as_array().at(1).as_object().get("address").to_number<u32>() < (8 * MB);

auto frames_value = sample_object.get("frames");
auto& frames_array = frames_value.as_array();
for (int i = frames_array.size() - 1; i >= 0; --i) {
auto& frame_value = frames_array.at(i);
auto& frame_object = frame_value.as_object();
Frame frame;
frame.symbol = frame_object.get("symbol").as_string_or({});
frame.address = frame_object.get("address").as_u32();
frame.offset = frame_object.get("offset").as_u32();
sample.frames.append(move(frame));
};
m_samples.append(move(sample));
}

rebuild_tree();
}

Profile::~Profile()
Expand Down Expand Up @@ -46,35 +62,32 @@ void Profile::rebuild_tree()
return new_root;
};

m_json.for_each([&](const JsonValue& sample) {
for (auto& sample : m_samples) {
if (has_timestamp_filter_range()) {
auto timestamp = sample.as_object().get("timestamp").to_number<u64>();
auto timestamp = sample.timestamp;
if (timestamp < m_timestamp_filter_range_start || timestamp > m_timestamp_filter_range_end)
return;
continue;
}

auto frames_value = sample.as_object().get("frames");
auto& frames = frames_value.as_array();
ProfileNode* node = nullptr;
for (int i = frames.size() - 1; i >= 0; --i) {
auto& frame = frames.at(i);
for (int i = 0; i < sample.frames.size(); ++i) {
auto& frame = sample.frames.at(i);

auto symbol = frame.as_object().get("symbol").as_string_or({});
auto address = frame.as_object().get("address").as_u32();
auto offset = frame.as_object().get("offset").as_u32();
auto timestamp = frame.as_object().get("timestamp").to_number<u64>();
auto& symbol = frame.symbol;
auto& address = frame.address;
auto& offset = frame.offset;

if (symbol.is_empty())
break;

if (!node)
node = &find_or_create_root(symbol, address, offset, timestamp);
node = &find_or_create_root(symbol, address, offset, sample.timestamp);
else
node = &node->find_or_create_child(symbol, address, offset, timestamp);
node = &node->find_or_create_child(symbol, address, offset, sample.timestamp);

node->increment_sample_count();
}
});
}

for (auto& root : roots) {
root.sort_children();
Expand Down
19 changes: 9 additions & 10 deletions DevTools/ProfileViewer/Profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,19 @@ class Profile {

const Vector<NonnullRefPtr<ProfileNode>>& roots() const { return m_roots; }

template<typename Callback>
void for_each_sample(Callback callback)
{
m_json.for_each([&](auto& value) {
callback(value.as_object());
});
}
struct Frame {
String symbol;
u32 address { 0 };
u32 offset { 0 };
};

struct SampleData {
struct Sample {
u64 timestamp { 0 };
bool in_kernel { false };
Vector<Frame> frames;
};

const Vector<SampleData>& sample_data() const { return m_sample_data; }
const Vector<Sample>& samples() const { return m_samples; }

u64 length_in_ms() const { return m_last_timestamp - m_first_timestamp; }
u64 first_timestamp() const { return m_first_timestamp; }
Expand All @@ -116,7 +115,7 @@ class Profile {
u64 m_first_timestamp { 0 };
u64 m_last_timestamp { 0 };

Vector<SampleData> m_sample_data;
Vector<Sample> m_samples;

bool m_has_timestamp_filter_range { false };
u64 m_timestamp_filter_range_start { 0 };
Expand Down
4 changes: 2 additions & 2 deletions DevTools/ProfileViewer/ProfileTimelineWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ void ProfileTimelineWidget::paint_event(GPaintEvent& event)

float column_width = (float)frame_inner_rect().width() / (float)m_profile.length_in_ms();

for (auto& sample : m_profile.sample_data()) {
u64 t = sample.timestamp;
for (auto& sample : m_profile.samples()) {
u64 t = sample.timestamp - m_profile.first_timestamp();
int x = (int)((float)t * column_width);
int cw = max(1, (int)column_width);

Expand Down

0 comments on commit 063fef3

Please sign in to comment.