Skip to content

Commit 226a33d

Browse files
committed
fb-py-spy: log thread ids and names in a perfetto compatible way
Summary: This modifies py-spy to use a sequential thread ID counter for each event and instead log a thread_name metadata field that includes the full thread ID as well as the thread name. Perfetto doesn't support u64 thread ids so we lose thread information and the py-spy chrometrace format doesn't include thread names. Upstream PR: benfred#681 Test Plan: Launch a process that uses subprocesses and threads ``` buck2 run fbsource//third-party/py-spy:py-spy -- record --format chrometrace --nonblocking --output ~/test-pyspy.json.gz --subprocesses -- torchx run -s local_cwd dist.ddp -j2x2 -m hi ``` {F1866175191} Perfetto: {F1866171613} Chrome Tracing: {F1866174988} Reviewers: agallagher, kunalb, egl Reviewed By: kunalb, egl Differential Revision: https://phabricator.intern.facebook.com/D62666434
1 parent 254ea36 commit 226a33d

File tree

1 file changed

+45
-2
lines changed

1 file changed

+45
-2
lines changed

src/chrometrace.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use crate::stack_trace::StackTrace;
2020
struct Args<'a> {
2121
pub filename: &'a str,
2222
pub line: Option<u32>,
23+
#[serde(skip_serializing_if = "Option::is_none")]
24+
pub name: Option<String>,
2325
}
2426

2527
#[derive(Clone, Debug, Serialize)]
@@ -29,7 +31,7 @@ struct Event<'a> {
2931
pub name: &'a str,
3032
pub ph: &'a str,
3133
pub pid: u64,
32-
pub tid: u64,
34+
pub tid: u32,
3335
pub ts: u64,
3436
}
3537

@@ -71,6 +73,10 @@ pub struct Chrometrace {
7173
start_ts: Instant,
7274
prev_traces: HashMap<u64, StackTrace>,
7375
show_linenumbers: bool,
76+
// We remap the thread IDs to be in the uint32 space, so they show up
77+
// correctly in Perfetto instead of being dropped.
78+
// https://github.com/google/perfetto/issues/886
79+
thread_ids: HashMap<u64, u32>,
7480
}
7581

7682
impl Chrometrace {
@@ -80,6 +86,7 @@ impl Chrometrace {
8086
start_ts: Instant::now(),
8187
prev_traces: HashMap::new(),
8288
show_linenumbers,
89+
thread_ids: HashMap::new(),
8390
})
8491
}
8592

@@ -97,7 +104,7 @@ impl Chrometrace {
97104
ts: u64,
98105
) -> Event<'a> {
99106
Event {
100-
tid: trace.thread_id,
107+
tid: self.get_thread_id(trace),
101108
pid: trace.pid as u64,
102109
name: frame.name.as_str(),
103110
cat: "py-spy",
@@ -110,10 +117,16 @@ impl Chrometrace {
110117
} else {
111118
None
112119
},
120+
name: None,
113121
},
114122
}
115123
}
116124

125+
fn get_thread_id(&self, trace: &StackTrace) -> u32 {
126+
let thread_id = trace.thread_id;
127+
self.thread_ids[&thread_id]
128+
}
129+
117130
fn record_events(
118131
&mut self,
119132
now: u64,
@@ -146,6 +159,34 @@ impl Chrometrace {
146159
Ok(())
147160
}
148161

162+
fn record_new_thread(&mut self, trace: &StackTrace) -> Result<(), Error> {
163+
if !self.thread_ids.contains_key(&trace.thread_id) {
164+
let thread_id = trace.thread_id;
165+
166+
// remap IDs to be in the uint32 space
167+
let remapped_id = self.thread_ids.len() as u32;
168+
self.thread_ids.insert(thread_id, remapped_id);
169+
170+
let name =
171+
thread_id.to_string() + ": " + trace.thread_name.as_deref().unwrap_or_default();
172+
173+
self.writer.write(Event {
174+
args: Args {
175+
filename: "",
176+
line: None,
177+
name: Some(name),
178+
},
179+
cat: "py-spy",
180+
name: "thread_name",
181+
ph: "M",
182+
pid: trace.pid as u64,
183+
tid: remapped_id,
184+
ts: 0,
185+
})?;
186+
}
187+
Ok(())
188+
}
189+
149190
pub fn increment(&mut self, traces: Vec<StackTrace>) -> Result<(), Error> {
150191
let now = self.start_ts.elapsed().as_micros() as u64;
151192

@@ -154,6 +195,8 @@ impl Chrometrace {
154195

155196
// Process each new trace.
156197
for trace in traces.into_iter() {
198+
self.record_new_thread(&trace)?;
199+
157200
let prev_trace = self.prev_traces.remove(&trace.thread_id);
158201
self.record_events(now, &trace, prev_trace)?;
159202
new_prev_traces.insert(trace.thread_id, trace);

0 commit comments

Comments
 (0)