Skip to content

Commit 724fec4

Browse files
committed
Catch incorrect uses of Profile::add_allocation_sample.
1 parent 52395c4 commit 724fec4

File tree

2 files changed

+34
-25
lines changed

2 files changed

+34
-25
lines changed

‎fxprof-processed-profile/src/process.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct Process {
1515
pid: String,
1616
name: String,
1717
threads: Vec<ThreadHandle>,
18+
main_thread: Option<ThreadHandle>,
1819
start_time: Timestamp,
1920
end_time: Option<Timestamp>,
2021
libs: LibMappings<LibraryHandle>,
@@ -25,6 +26,7 @@ impl Process {
2526
Self {
2627
pid,
2728
threads: Vec::new(),
29+
main_thread: None,
2830
libs: LibMappings::new(),
2931
start_time,
3032
end_time: None,
@@ -33,7 +35,7 @@ impl Process {
3335
}
3436

3537
pub fn thread_handle_for_allocations(&self) -> Option<ThreadHandle> {
36-
self.threads.first().cloned()
38+
self.main_thread
3739
}
3840

3941
pub fn set_start_time(&mut self, start_time: Timestamp) {
@@ -60,8 +62,11 @@ impl Process {
6062
&self.name
6163
}
6264

63-
pub fn add_thread(&mut self, thread: ThreadHandle) {
65+
pub fn add_thread(&mut self, thread: ThreadHandle, is_main: bool) {
6466
self.threads.push(thread);
67+
if is_main && self.main_thread.is_none() {
68+
self.main_thread = Some(thread)
69+
}
6570
}
6671

6772
pub fn pid(&self) -> &str {

‎fxprof-processed-profile/src/profile.rs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ impl Profile {
481481
let handle = ThreadHandle(self.threads.len());
482482
self.threads
483483
.push(Thread::new(process, tid, start_time, is_main));
484-
self.processes[process.0].add_thread(handle);
484+
self.processes[process.0].add_thread(handle, is_main);
485485
handle
486486
}
487487

@@ -926,8 +926,12 @@ impl Profile {
926926
self.threads[thread.0].add_sample_same_stack_zero_cpu(timestamp, weight);
927927
}
928928

929-
/// Add an allocation or deallocation sample to the given thread. This is used
930-
/// to collect stacks showing where allocations and deallocations happened.
929+
/// Add an allocation or deallocation sample to the *main* thread of the given
930+
/// process. This is used to collect stacks showing where allocations and
931+
/// deallocations happened.
932+
///
933+
/// CANNOT BE CALLED BEFORE THE MAIN THREAD FOR `process` HAS BEEN CREATED.
934+
/// `stack` MUST BE A STACK HANDLE WHICH IS VALID FOR THAT MAIN THREAD.
931935
///
932936
/// When loading profiles with allocation samples in the Firefox Profiler, the
933937
/// UI will display a dropdown above the call tree to switch between regular
@@ -951,40 +955,40 @@ impl Profile {
951955
///
952956
/// To get the stack handle, you can use [`Profile::handle_for_stack`] or
953957
/// [`Profile::handle_for_stack_frames`].
958+
///
959+
/// ## Main thread requirement
960+
///
961+
/// Allocations are per-process, because you can allocate something one one thread
962+
/// and then free it on a different thread, and you'll still want those two operations
963+
/// to be matched up in a view that shows the retained memory.
964+
///
965+
/// Unfortunately, `StackHandle` is currently per-thread. This method will become more
966+
/// ergonomic once the profile format has changed so that `StackHandle`s can be used
967+
/// across all threads of a profile. In the meantime, unfortunately you must manually
968+
/// ensure that you create the stack handle for the main thread of the given process.
954969
pub fn add_allocation_sample(
955970
&mut self,
956-
thread: ThreadHandle,
971+
process: ProcessHandle,
957972
timestamp: Timestamp,
958973
stack: Option<StackHandle>,
959974
allocation_address: u64,
960975
allocation_size: i64,
961976
) {
962-
// The profile format strictly separates sample data from different threads.
963-
// For allocation samples, this separation is a bit unfortunate, especially
964-
// when it comes to the "Retained Memory" panel which shows allocation stacks
965-
// for just objects that haven't been deallocated yet. This panel is per-thread,
966-
// and it needs to know about deallocations even if they happened on a different
967-
// thread from the allocation.
968-
// To resolve this conundrum, for now, we will put all allocation and deallocation
969-
// samples on a single thread per process, regardless of what thread they actually
970-
// happened on.
971-
// The Gecko profiler puts all allocation samples on the main thread, for example.
972-
// Here in fxprof-processed-profile, we just deem the first thread of each process
973-
// as the processes "allocation thread".
974-
let process_handle = self.threads[thread.0].process();
975-
let process = &self.processes[process_handle.0];
976-
let allocation_thread_handle = process.thread_handle_for_allocations().unwrap();
977+
let process = &self.processes[process.0];
978+
let Some(allocation_thread) = process.thread_handle_for_allocations() else {
979+
panic!("Profile::add_allocation_sample called for a thread whose process does not have a main thread");
980+
};
977981
let stack_index = match stack {
978-
Some(StackHandle(stack_thread_handle, stack_index)) => {
982+
Some(StackHandle(stack_thread, stack_index)) => {
979983
assert_eq!(
980-
stack_thread_handle, thread,
981-
"StackHandle from different thread passed to Profile::add_sample"
984+
stack_thread, allocation_thread,
985+
"StackHandle from different thread passed to Profile::add_allocation_sample"
982986
);
983987
Some(stack_index)
984988
}
985989
None => None,
986990
};
987-
self.threads[allocation_thread_handle.0].add_allocation_sample(
991+
self.threads[allocation_thread.0].add_allocation_sample(
988992
timestamp,
989993
stack_index,
990994
allocation_address,

0 commit comments

Comments
 (0)