Skip to content

Commit a607f13

Browse files
gunnarbeutnerlinusg
authored andcommitted
Profiler: Use sequential serial numbers for profiling events
Previously Profiler was using timestamps to distinguish processes. However it is possible that separate processes with the same PID exist at the exact same timestamp (e.g. for execve). This changes Profiler to use unique serial numbers for each event instead.
1 parent af72b5e commit a607f13

File tree

8 files changed

+125
-47
lines changed

8 files changed

+125
-47
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <AK/Assertions.h>
10+
#include <AK/NumericLimits.h>
11+
#include <AK/Types.h>
12+
13+
namespace Profiler {
14+
15+
// DistinctNumeric's constructor is non-explicit which makes accidentally turning
16+
// an unrelated u64 into a serial number all too easy.
17+
class EventSerialNumber {
18+
public:
19+
constexpr EventSerialNumber() = default;
20+
21+
void increment()
22+
{
23+
m_serial++;
24+
}
25+
26+
size_t to_number() const
27+
{
28+
return m_serial;
29+
}
30+
31+
static EventSerialNumber max_valid_serial()
32+
{
33+
return EventSerialNumber { NumericLimits<size_t>::max() };
34+
}
35+
36+
bool operator==(EventSerialNumber const& rhs) const
37+
{
38+
return m_serial == rhs.m_serial;
39+
}
40+
41+
bool operator<(EventSerialNumber const& rhs) const
42+
{
43+
return m_serial < rhs.m_serial;
44+
}
45+
46+
bool operator>(EventSerialNumber const& rhs) const
47+
{
48+
return m_serial > rhs.m_serial;
49+
}
50+
51+
bool operator<=(EventSerialNumber const& rhs) const
52+
{
53+
return m_serial <= rhs.m_serial;
54+
}
55+
56+
bool operator>=(EventSerialNumber const& rhs) const
57+
{
58+
return m_serial >= rhs.m_serial;
59+
}
60+
61+
private:
62+
size_t m_serial { 0 };
63+
64+
constexpr explicit EventSerialNumber(size_t serial)
65+
: m_serial(serial)
66+
{
67+
}
68+
};
69+
70+
}

Userland/DevTools/Profiler/Process.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,36 @@
88

99
namespace Profiler {
1010

11-
Thread* Process::find_thread(pid_t tid, u64 timestamp)
11+
Thread* Process::find_thread(pid_t tid, EventSerialNumber serial)
1212
{
1313
auto it = threads.find(tid);
1414
if (it == threads.end())
1515
return nullptr;
1616
for (auto& thread : it->value) {
17-
if (thread.start_valid < timestamp && (thread.end_valid == 0 || thread.end_valid > timestamp))
17+
if (thread.start_valid < serial && (thread.end_valid == EventSerialNumber {} || thread.end_valid > serial))
1818
return &thread;
1919
}
2020
return nullptr;
2121
}
2222

23-
void Process::handle_thread_create(pid_t tid, u64 timestamp)
23+
void Process::handle_thread_create(pid_t tid, EventSerialNumber serial)
2424
{
2525
auto it = threads.find(tid);
2626
if (it == threads.end()) {
2727
threads.set(tid, {});
2828
it = threads.find(tid);
2929
}
3030

31-
auto thread = Thread { tid, timestamp, 0 };
31+
auto thread = Thread { tid, serial, {} };
3232
it->value.append(move(thread));
3333
}
3434

35-
void Process::handle_thread_exit(pid_t tid, u64 timestamp)
35+
void Process::handle_thread_exit(pid_t tid, EventSerialNumber serial)
3636
{
37-
auto* thread = find_thread(tid, timestamp);
37+
auto* thread = find_thread(tid, serial);
3838
if (!thread)
3939
return;
40-
thread->end_valid = timestamp;
40+
thread->end_valid = serial;
4141
}
4242

4343
HashMap<String, OwnPtr<MappedObject>> g_mapped_object_cache;

Userland/DevTools/Profiler/Process.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#pragma once
88

9+
#include "EventSerialNumber.h"
910
#include <AK/HashMap.h>
1011
#include <AK/MappedFile.h>
1112
#include <AK/OwnPtr.h>
@@ -42,12 +43,12 @@ class LibraryMetadata {
4243

4344
struct Thread {
4445
pid_t tid;
45-
u64 start_valid;
46-
u64 end_valid { 0 };
46+
EventSerialNumber start_valid;
47+
EventSerialNumber end_valid;
4748

48-
bool valid_at(u64 timestamp) const
49+
bool valid_at(EventSerialNumber serial) const
4950
{
50-
return timestamp >= start_valid && (end_valid == 0 || timestamp <= end_valid);
51+
return serial >= start_valid && (end_valid == EventSerialNumber {} || serial <= end_valid);
5152
}
5253
};
5354

@@ -57,16 +58,16 @@ struct Process {
5758
String basename;
5859
HashMap<int, Vector<Thread>> threads {};
5960
LibraryMetadata library_metadata {};
60-
u64 start_valid;
61-
u64 end_valid { 0 };
61+
EventSerialNumber start_valid;
62+
EventSerialNumber end_valid;
6263

63-
Thread* find_thread(pid_t tid, u64 timestamp);
64-
void handle_thread_create(pid_t tid, u64 timestamp);
65-
void handle_thread_exit(pid_t tid, u64 timestamp);
64+
Thread* find_thread(pid_t tid, EventSerialNumber serial);
65+
void handle_thread_create(pid_t tid, EventSerialNumber serial);
66+
void handle_thread_exit(pid_t tid, EventSerialNumber serial);
6667

67-
bool valid_at(u64 timestamp) const
68+
bool valid_at(EventSerialNumber serial) const
6869
{
69-
return timestamp >= start_valid && (end_valid == 0 || timestamp <= end_valid);
70+
return serial >= start_valid && (end_valid == EventSerialNumber {} || serial <= end_valid);
7071
}
7172
};
7273

Userland/DevTools/Profiler/Profile.cpp

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ void Profile::rebuild_tree()
6161
{
6262
Vector<NonnullRefPtr<ProfileNode>> roots;
6363

64-
auto find_or_create_process_node = [this, &roots](pid_t pid, u64 timestamp) -> ProfileNode& {
65-
auto* process = find_process(pid, timestamp);
64+
auto find_or_create_process_node = [this, &roots](pid_t pid, EventSerialNumber serial) -> ProfileNode& {
65+
auto* process = find_process(pid, serial);
6666
if (!process) {
67-
dbgln("Profile contains event for unknown process with pid={}, timestamp={}", pid, timestamp);
67+
dbgln("Profile contains event for unknown process with pid={}, serial={}", pid, serial.to_number());
6868
VERIFY_NOT_REACHED();
6969
}
7070
for (auto root : roots) {
@@ -96,7 +96,7 @@ void Profile::rebuild_tree()
9696
continue;
9797
}
9898

99-
if (!process_filter_contains(event.pid, event.timestamp))
99+
if (!process_filter_contains(event.pid, event.serial))
100100
continue;
101101

102102
m_filtered_event_indices.append(event_index);
@@ -123,7 +123,7 @@ void Profile::rebuild_tree()
123123

124124
if (!m_show_top_functions) {
125125
ProfileNode* node = nullptr;
126-
auto& process_node = find_or_create_process_node(event.pid, event.timestamp);
126+
auto& process_node = find_or_create_process_node(event.pid, event.serial);
127127
process_node.increment_event_count();
128128
for_each_frame([&](const Frame& frame, bool is_innermost_frame) {
129129
auto& object_name = frame.object_name;
@@ -147,7 +147,7 @@ void Profile::rebuild_tree()
147147
return IterationDecision::Continue;
148148
});
149149
} else {
150-
auto& process_node = find_or_create_process_node(event.pid, event.timestamp);
150+
auto& process_node = find_or_create_process_node(event.pid, event.serial);
151151
process_node.increment_event_count();
152152
for (size_t i = 0; i < event.frames.size(); ++i) {
153153
ProfileNode* node = nullptr;
@@ -163,7 +163,7 @@ void Profile::rebuild_tree()
163163

164164
// FIXME: More PID/TID mixing cheats here:
165165
if (!node) {
166-
node = &find_or_create_process_node(event.pid, event.timestamp);
166+
node = &find_or_create_process_node(event.pid, event.serial);
167167
node = &node->find_or_create_child(object_name, symbol, address, offset, event.timestamp, event.pid);
168168
root = node;
169169
root->will_track_seen_events(m_events.size());
@@ -219,12 +219,15 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
219219
NonnullOwnPtrVector<Process> all_processes;
220220
HashMap<pid_t, Process*> current_processes;
221221
Vector<Event> events;
222+
EventSerialNumber next_serial;
222223

223224
for (auto& perf_event_value : perf_events.values()) {
224225
auto& perf_event = perf_event_value.as_object();
225226

226227
Event event;
227228

229+
event.serial = next_serial;
230+
next_serial.increment();
228231
event.timestamp = perf_event.get("timestamp").to_number<u64>();
229232
event.lost_samples = perf_event.get("lost_samples").to_number<u32>();
230233
event.type = perf_event.get("type").to_string();
@@ -257,7 +260,8 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
257260
.pid = event.pid,
258261
.executable = event.executable,
259262
.basename = LexicalPath(event.executable).basename(),
260-
.start_valid = event.timestamp,
263+
.start_valid = event.serial,
264+
.end_valid = {},
261265
});
262266

263267
current_processes.set(sampled_process->pid, sampled_process);
@@ -267,35 +271,37 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
267271
event.executable = perf_event.get("executable").to_string();
268272

269273
auto old_process = current_processes.get(event.pid).value();
270-
old_process->end_valid = event.timestamp;
274+
old_process->end_valid = event.serial;
271275

272276
current_processes.remove(event.pid);
273277

274278
auto sampled_process = adopt_own(*new Process {
275279
.pid = event.pid,
276280
.executable = event.executable,
277281
.basename = LexicalPath(event.executable).basename(),
278-
.start_valid = event.timestamp });
282+
.start_valid = event.serial,
283+
.end_valid = {},
284+
});
279285

280286
current_processes.set(sampled_process->pid, sampled_process);
281287
all_processes.append(move(sampled_process));
282288
continue;
283289
} else if (event.type == "process_exit"sv) {
284290
auto old_process = current_processes.get(event.pid).value();
285-
old_process->end_valid = event.timestamp;
291+
old_process->end_valid = event.serial;
286292

287293
current_processes.remove(event.pid);
288294
continue;
289295
} else if (event.type == "thread_create"sv) {
290296
event.parent_tid = perf_event.get("parent_tid").to_i32();
291297
auto it = current_processes.find(event.pid);
292298
if (it != current_processes.end())
293-
it->value->handle_thread_create(event.tid, event.timestamp);
299+
it->value->handle_thread_create(event.tid, event.serial);
294300
continue;
295301
} else if (event.type == "thread_exit"sv) {
296302
auto it = current_processes.find(event.pid);
297303
if (it != current_processes.end())
298-
it->value->handle_thread_exit(event.tid, event.timestamp);
304+
it->value->handle_thread_exit(event.tid, event.serial);
299305
continue;
300306
}
301307

@@ -385,7 +391,7 @@ void Profile::clear_timestamp_filter_range()
385391
m_samples_model->update();
386392
}
387393

388-
void Profile::add_process_filter(pid_t pid, u64 start_valid, u64 end_valid)
394+
void Profile::add_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid)
389395
{
390396
auto filter = ProcessFilter { pid, start_valid, end_valid };
391397
if (m_process_filters.contains_slow(filter))
@@ -398,7 +404,7 @@ void Profile::add_process_filter(pid_t pid, u64 start_valid, u64 end_valid)
398404
m_samples_model->update();
399405
}
400406

401-
void Profile::remove_process_filter(pid_t pid, u64 start_valid, u64 end_valid)
407+
void Profile::remove_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid)
402408
{
403409
auto filter = ProcessFilter { pid, start_valid, end_valid };
404410
if (!m_process_filters.contains_slow(filter))
@@ -424,13 +430,13 @@ void Profile::clear_process_filter()
424430
m_samples_model->update();
425431
}
426432

427-
bool Profile::process_filter_contains(pid_t pid, u32 timestamp)
433+
bool Profile::process_filter_contains(pid_t pid, EventSerialNumber serial)
428434
{
429435
if (!has_process_filter())
430436
return true;
431437

432438
for (auto const& process_filter : m_process_filters)
433-
if (pid == process_filter.pid && timestamp >= process_filter.start_valid && timestamp <= process_filter.end_valid)
439+
if (pid == process_filter.pid && serial >= process_filter.start_valid && serial <= process_filter.end_valid)
434440
return true;
435441

436442
return false;

Userland/DevTools/Profiler/Profile.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ class ProfileNode : public RefCounted<ProfileNode> {
126126

127127
struct ProcessFilter {
128128
pid_t pid { 0 };
129-
u64 start_valid { 0 };
130-
u64 end_valid { 0 };
129+
EventSerialNumber start_valid;
130+
EventSerialNumber end_valid;
131131

132132
bool operator==(ProcessFilter const& rhs) const
133133
{
@@ -143,10 +143,10 @@ class Profile {
143143
GUI::Model& samples_model();
144144
GUI::Model* disassembly_model();
145145

146-
const Process* find_process(pid_t pid, u64 timestamp) const
146+
const Process* find_process(pid_t pid, EventSerialNumber serial) const
147147
{
148-
auto it = m_processes.find_if([&](auto& entry) {
149-
return entry.pid == pid && entry.valid_at(timestamp);
148+
auto it = m_processes.find_if([&pid, &serial](auto& entry) {
149+
return entry.pid == pid && entry.valid_at(serial);
150150
});
151151
return it.is_end() ? nullptr : &(*it);
152152
}
@@ -163,6 +163,7 @@ class Profile {
163163
};
164164

165165
struct Event {
166+
EventSerialNumber serial;
166167
u64 timestamp { 0 };
167168
String type;
168169
FlatPtr ptr { 0 };
@@ -190,11 +191,11 @@ class Profile {
190191
void clear_timestamp_filter_range();
191192
bool has_timestamp_filter_range() const { return m_has_timestamp_filter_range; }
192193

193-
void add_process_filter(pid_t pid, u64 start_valid, u64 end_valid);
194-
void remove_process_filter(pid_t pid, u64 start_valid, u64 end_valid);
194+
void add_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid);
195+
void remove_process_filter(pid_t pid, EventSerialNumber start_valid, EventSerialNumber end_valid);
195196
void clear_process_filter();
196197
bool has_process_filter() const { return !m_process_filters.is_empty(); }
197-
bool process_filter_contains(pid_t pid, u32 timestamp);
198+
bool process_filter_contains(pid_t pid, EventSerialNumber serial);
198199

199200
bool is_inverted() const { return m_inverted; }
200201
void set_inverted(bool);

Userland/DevTools/Profiler/SamplesModel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ GUI::Variant SamplesModel::data(const GUI::ModelIndex& index, GUI::ModelRole rol
7474
return event.tid;
7575

7676
if (index.column() == Column::ExecutableName) {
77-
if (auto* process = m_profile.find_process(event.pid, event.timestamp))
77+
if (auto* process = m_profile.find_process(event.pid, event.serial))
7878
return process->executable;
7979
return "";
8080
}

Userland/DevTools/Profiler/TimelineTrack.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void TimelineTrack::paint_event(GUI::PaintEvent& event)
7373
if (event.pid != m_process.pid)
7474
continue;
7575

76-
if (!m_process.valid_at(event.timestamp))
76+
if (!m_process.valid_at(event.serial))
7777
continue;
7878

7979
auto& histogram = event.in_kernel ? kernel_histogram : usermode_histogram;

0 commit comments

Comments
 (0)