rrtrace 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/trace_state.rs CHANGED
@@ -1,7 +1,9 @@
1
1
  use crate::ringbuffer::{RRTraceEvent, RRTraceEventType};
2
2
  use smallvec::SmallVec;
3
- use std::convert;
3
+ use std::collections::HashMap;
4
+ use std::collections::hash_map::Entry;
4
5
  use std::fmt::Debug;
6
+ use std::{iter, mem};
5
7
 
6
8
  #[repr(C)]
7
9
  #[derive(Copy, Default, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
@@ -12,6 +14,43 @@ pub struct CallBox {
12
14
  depth: u32,
13
15
  }
14
16
 
17
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
18
+ pub struct ThreadLine {
19
+ start_time: [u32; 2],
20
+ end_time: [u32; 2],
21
+ }
22
+
23
+ impl ThreadLine {
24
+ pub fn start_time(&self) -> [u32; 2] {
25
+ self.start_time
26
+ }
27
+
28
+ pub fn end_time(&self) -> [u32; 2] {
29
+ self.end_time
30
+ }
31
+ }
32
+
33
+ #[derive(Debug, Clone)]
34
+ pub struct ThreadData {
35
+ thread_id: u32,
36
+ call_boxes: Vec<CallBox>,
37
+ thread_line: ThreadLine,
38
+ }
39
+
40
+ impl ThreadData {
41
+ pub fn thread_id(&self) -> u32 {
42
+ self.thread_id
43
+ }
44
+
45
+ pub fn call_boxes(&self) -> &[CallBox] {
46
+ &self.call_boxes
47
+ }
48
+
49
+ pub fn thread_line(&self) -> ThreadLine {
50
+ self.thread_line
51
+ }
52
+ }
53
+
15
54
  pub const VISIBLE_DURATION: u64 = 1_000_000_000 * 5;
16
55
 
17
56
  pub fn encode_time(time: u64) -> [u32; 2] {
@@ -21,69 +60,191 @@ pub fn encode_time(time: u64) -> [u32; 2] {
21
60
  ]
22
61
  }
23
62
 
63
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
64
+ enum ThreadId {
65
+ None,
66
+ Initial,
67
+ Id(u32),
68
+ }
69
+
70
+ #[derive(Debug, Clone, Default)]
71
+ struct StackState {
72
+ unmarked_returns: SmallVec<[u64; 2]>,
73
+ stack: SmallVec<[u64; 16]>,
74
+ exited: bool,
75
+ }
76
+
77
+ impl StackState {
78
+ fn new() -> StackState {
79
+ StackState::default()
80
+ }
81
+
82
+ #[inline(always)]
83
+ fn call(&mut self, method_id: u64) {
84
+ self.stack.push(method_id);
85
+ }
86
+
87
+ #[inline(always)]
88
+ fn ret(&mut self, method_id: u64) {
89
+ loop {
90
+ match self.stack.pop() {
91
+ None => {
92
+ self.unmarked_returns.push(method_id);
93
+ break;
94
+ }
95
+ Some(m) if m == method_id => break,
96
+ Some(_) => {}
97
+ }
98
+ }
99
+ }
100
+
101
+ #[inline(always)]
102
+ fn exit(&mut self) {
103
+ self.exited = true;
104
+ }
105
+
106
+ fn drop_unmarked_returns(&mut self) {
107
+ self.unmarked_returns.clear();
108
+ }
109
+
110
+ fn merge_into(&self, other: &mut Self) {
111
+ let additional_push_stack = mem::replace(&mut other.stack, self.stack.clone());
112
+ let unmarked_returns =
113
+ mem::replace(&mut other.unmarked_returns, self.unmarked_returns.clone());
114
+ for method_id in unmarked_returns {
115
+ other.ret(method_id);
116
+ }
117
+ for method_id in additional_push_stack {
118
+ other.call(method_id);
119
+ }
120
+ }
121
+ }
122
+
24
123
  #[derive(Debug, Clone)]
25
124
  pub struct FastTrace {
26
- thread_stacks: SmallVec<[(u32, SmallVec<[u64; 4]>); 1]>,
27
- current_thread: u32,
125
+ thread_stacks: HashMap<u32, StackState>,
126
+ initial_thread_stack: StackState,
127
+ current_thread: ThreadId,
28
128
  in_gc: bool,
29
129
  }
30
130
 
31
- impl FastTrace {
32
- pub fn new() -> FastTrace {
131
+ impl Default for FastTrace {
132
+ fn default() -> Self {
33
133
  FastTrace {
34
- thread_stacks: smallvec::smallvec![(0, SmallVec::new())],
35
- current_thread: 0,
134
+ thread_stacks: HashMap::new(),
135
+ initial_thread_stack: StackState::new(),
136
+ current_thread: ThreadId::Id(0),
36
137
  in_gc: false,
37
138
  }
38
139
  }
39
- pub fn process_events(&mut self, events: &[RRTraceEvent]) {
140
+ }
141
+
142
+ impl FastTrace {
143
+ pub fn from_events(events: &[RRTraceEvent]) -> FastTrace {
144
+ let mut thread_stacks = HashMap::<u32, StackState>::new();
145
+ let mut initial_thread_stack = StackState::new();
146
+ let mut current_thread = ThreadId::Initial;
147
+
148
+ let mut current_thread_stack = &mut initial_thread_stack;
40
149
  for &event in events {
41
150
  match event.event_type() {
42
151
  RRTraceEventType::Call => {
43
152
  let method_id = event.data();
44
- self.thread_stacks[self.current_thread as usize]
45
- .1
46
- .push(method_id);
153
+ current_thread_stack.call(method_id);
47
154
  }
48
155
  RRTraceEventType::Return => {
49
156
  let method_id = event.data();
50
- let stack = &mut self.thread_stacks[self.current_thread as usize].1;
51
- while stack.pop().is_some_and(|m| m != method_id) {}
157
+ current_thread_stack.ret(method_id);
52
158
  }
53
159
  RRTraceEventType::ThreadSuspended => {
54
- self.current_thread = u32::MAX;
160
+ current_thread = ThreadId::None;
55
161
  }
56
162
  RRTraceEventType::ThreadResume => {
57
163
  let thread_id = event.data() as u32;
58
- let index = match self
59
- .thread_stacks
60
- .binary_search_by_key(&thread_id, |&(thread_id, _)| thread_id)
61
- {
62
- Ok(index) => index,
63
- Err(index) => {
64
- self.thread_stacks
65
- .insert(index, (thread_id, SmallVec::new()));
66
- index
67
- }
68
- };
69
- self.current_thread = index as u32;
164
+ current_thread = ThreadId::Id(thread_id);
165
+ current_thread_stack = thread_stacks.entry(thread_id).or_default();
70
166
  }
71
- RRTraceEventType::ThreadExit => {
167
+ RRTraceEventType::ThreadStart => {
72
168
  let thread_id = event.data() as u32;
73
- if let Ok(index) = self
74
- .thread_stacks
75
- .binary_search_by_key(&thread_id, |&(thread_id, _)| thread_id)
76
- {
77
- self.thread_stacks.remove(index);
169
+ thread_stacks.insert(thread_id, StackState::new());
170
+ current_thread_stack = if let ThreadId::Id(current_thread_id) = current_thread {
171
+ thread_stacks.entry(current_thread_id).or_default()
172
+ } else {
173
+ &mut initial_thread_stack
78
174
  };
79
175
  }
176
+ RRTraceEventType::ThreadExit => {
177
+ current_thread_stack.exit();
178
+ }
80
179
  RRTraceEventType::GCStart
81
180
  | RRTraceEventType::GCEnd
82
- | RRTraceEventType::ThreadStart
83
181
  | RRTraceEventType::ThreadReady => {}
84
182
  }
85
183
  }
86
- self.in_gc = events.last().unwrap().event_type() == RRTraceEventType::GCStart;
184
+ let in_gc = events.last().unwrap().event_type() == RRTraceEventType::GCStart;
185
+ FastTrace {
186
+ thread_stacks,
187
+ initial_thread_stack,
188
+ current_thread,
189
+ in_gc,
190
+ }
191
+ }
192
+
193
+ pub fn mark_as_first(&mut self) {
194
+ self.thread_stacks
195
+ .values_mut()
196
+ .chain(iter::once(&mut self.initial_thread_stack))
197
+ .for_each(StackState::drop_unmarked_returns);
198
+ if let ThreadId::Initial = self.current_thread {
199
+ self.current_thread = ThreadId::Id(0);
200
+ }
201
+ let initial_thread_stack = mem::take(&mut self.initial_thread_stack);
202
+ match self.thread_stacks.entry(0) {
203
+ Entry::Occupied(mut entry) => {
204
+ initial_thread_stack.merge_into(entry.get_mut());
205
+ }
206
+ Entry::Vacant(entry) => {
207
+ entry.insert(initial_thread_stack);
208
+ }
209
+ }
210
+ }
211
+
212
+ pub fn merge_into(&self, other: &mut Self) {
213
+ match self.current_thread {
214
+ ThreadId::Id(id) => {
215
+ let initial_thread_stack = mem::replace(
216
+ &mut other.initial_thread_stack,
217
+ self.initial_thread_stack.clone(),
218
+ );
219
+ match other.thread_stacks.entry(id) {
220
+ Entry::Occupied(mut entry) => {
221
+ initial_thread_stack.merge_into(entry.get_mut());
222
+ }
223
+ Entry::Vacant(entry) => {
224
+ entry.insert(initial_thread_stack);
225
+ }
226
+ }
227
+ }
228
+ ThreadId::Initial => {
229
+ self.initial_thread_stack
230
+ .merge_into(&mut other.initial_thread_stack);
231
+ }
232
+ ThreadId::None => {}
233
+ }
234
+ if let ThreadId::Initial = other.current_thread {
235
+ other.current_thread = self.current_thread;
236
+ }
237
+ self.thread_stacks.iter().for_each(|(&thread_id, stack)| {
238
+ match other.thread_stacks.entry(thread_id) {
239
+ Entry::Occupied(mut entry) => {
240
+ stack.merge_into(entry.get_mut());
241
+ }
242
+ Entry::Vacant(entry) => {
243
+ entry.insert(stack.clone());
244
+ }
245
+ }
246
+ });
247
+ other.thread_stacks.retain(|_, stack| !stack.exited);
87
248
  }
88
249
  }
89
250
 
@@ -93,46 +254,117 @@ struct CallStackEntry {
93
254
  method_id: u64,
94
255
  }
95
256
 
257
+ #[derive(Debug, Clone)]
258
+ struct ThreadTraceState {
259
+ thread_id: u32,
260
+ stack: Vec<CallStackEntry>,
261
+ call_boxes: Vec<CallBox>,
262
+ thread_line: ThreadLine,
263
+ }
264
+
265
+ impl ThreadTraceState {
266
+ fn from_stack(thread_id: u32, stack: &StackState, start_time: u64, end_time: u64) -> Self {
267
+ Self {
268
+ thread_id,
269
+ stack: stack
270
+ .stack
271
+ .iter()
272
+ .map(|&method_id| CallStackEntry {
273
+ method_id,
274
+ vertex_index: usize::MAX,
275
+ })
276
+ .collect(),
277
+ call_boxes: Vec::new(),
278
+ thread_line: ThreadLine {
279
+ start_time: encode_time(start_time),
280
+ end_time: encode_time(end_time),
281
+ },
282
+ }
283
+ }
284
+
285
+ fn new(thread_id: u32, start_time: u64, end_time: u64) -> Self {
286
+ Self {
287
+ thread_id,
288
+ stack: Vec::new(),
289
+ call_boxes: Vec::new(),
290
+ thread_line: ThreadLine {
291
+ start_time: encode_time(start_time),
292
+ end_time: encode_time(end_time),
293
+ },
294
+ }
295
+ }
296
+
297
+ fn into_thread_data(self) -> ThreadData {
298
+ ThreadData {
299
+ thread_id: self.thread_id,
300
+ call_boxes: self.call_boxes,
301
+ thread_line: self.thread_line,
302
+ }
303
+ }
304
+ }
305
+
306
+ fn find_thread_index(call_stack: &[ThreadTraceState], thread_id: u32) -> Result<usize, usize> {
307
+ call_stack.binary_search_by_key(&thread_id, |state| state.thread_id)
308
+ }
309
+
310
+ fn get_or_insert_thread_state(
311
+ call_stack: &mut Vec<ThreadTraceState>,
312
+ thread_id: u32,
313
+ start_time: u64,
314
+ end_time: u64,
315
+ ) -> usize {
316
+ match find_thread_index(call_stack, thread_id) {
317
+ Ok(index) => index,
318
+ Err(index) => {
319
+ call_stack.insert(
320
+ index,
321
+ ThreadTraceState::new(thread_id, start_time, end_time),
322
+ );
323
+ index
324
+ }
325
+ }
326
+ }
327
+
96
328
  pub struct SlowTrace {
97
- data: Vec<(u32, Vec<CallStackEntry>, Vec<CallBox>)>,
329
+ data: Vec<ThreadData>,
98
330
  max_depth: u32,
99
331
  end_time: u64,
100
332
  gc_events: Vec<u64>,
101
333
  }
102
334
 
103
335
  impl SlowTrace {
104
- pub fn trace(start_time: u64, fast_trace: FastTrace, events: &[RRTraceEvent]) -> SlowTrace {
336
+ pub fn trace(start_time: u64, fast_trace: &FastTrace, events: &[RRTraceEvent]) -> SlowTrace {
105
337
  let end_time = events.last().unwrap().timestamp();
106
338
  let mut max_depth = 0;
107
- let FastTrace {
108
- thread_stacks,
339
+ let &FastTrace {
340
+ ref thread_stacks,
341
+ initial_thread_stack: _,
109
342
  current_thread,
110
343
  in_gc,
111
344
  } = fast_trace;
345
+ let current_thread = match current_thread {
346
+ ThreadId::None => u32::MAX,
347
+ ThreadId::Initial => unreachable!(),
348
+ ThreadId::Id(id) => id,
349
+ };
112
350
  let mut gc_events = Vec::new();
113
351
  let mut call_stack = thread_stacks
114
- .into_iter()
115
- .map(|(thread_id, stack)| {
116
- (
117
- thread_id,
118
- stack
119
- .into_iter()
120
- .map(|method_id| CallStackEntry {
121
- method_id,
122
- vertex_index: usize::MAX,
123
- })
124
- .collect::<Vec<_>>(),
125
- Vec::new(),
126
- )
352
+ .iter()
353
+ .map(|(&thread_id, stack)| {
354
+ ThreadTraceState::from_stack(thread_id, stack, start_time, end_time)
127
355
  })
128
356
  .collect::<Vec<_>>();
129
- if !in_gc && let Some((_, stack, vertices)) = call_stack.get_mut(current_thread as usize) {
130
- vertices.reserve(stack.len());
357
+ call_stack.sort_unstable_by_key(|state| state.thread_id);
358
+ if !in_gc && let Ok(index) = find_thread_index(&call_stack, current_thread) {
359
+ let ThreadTraceState {
360
+ stack, call_boxes, ..
361
+ } = &mut call_stack[index];
362
+ call_boxes.reserve(stack.len());
131
363
  for (depth, entry) in stack.iter_mut().enumerate() {
132
- entry.vertex_index = vertices.len();
364
+ entry.vertex_index = call_boxes.len();
133
365
  let depth = depth as u32;
134
366
  max_depth = max_depth.max(depth);
135
- vertices.push(CallBox {
367
+ call_boxes.push(CallBox {
136
368
  start_time: encode_time(start_time),
137
369
  end_time: encode_time(end_time),
138
370
  method_id: entry.method_id as u32,
@@ -140,85 +372,133 @@ impl SlowTrace {
140
372
  });
141
373
  }
142
374
  }
143
- let mut null_vec1 = Vec::new();
144
- let mut null_vec2 = Vec::new();
145
- let (mut current_stack, mut current_vertices) = call_stack
146
- .get_mut(current_thread as usize)
147
- .map_or((&mut null_vec1, &mut null_vec2), |(_, stack, vertices)| {
148
- (stack, vertices)
149
- });
375
+ let mut current_thread_id = (current_thread != u32::MAX).then_some(current_thread);
150
376
  for event in events {
151
377
  match event.event_type() {
152
378
  RRTraceEventType::Call => {
153
- let vertex_index = current_vertices.len();
154
- let depth = current_stack.len() as u32;
155
- current_stack.push(CallStackEntry {
156
- vertex_index,
157
- method_id: event.data(),
158
- });
159
- current_vertices.push(CallBox {
160
- start_time: encode_time(event.timestamp()),
161
- end_time: encode_time(end_time),
162
- method_id: event.data() as u32,
163
- depth,
164
- });
165
- max_depth = max_depth.max(depth);
379
+ if let Some(index) = current_thread_id
380
+ .and_then(|thread_id| find_thread_index(&call_stack, thread_id).ok())
381
+ {
382
+ let ThreadTraceState {
383
+ stack: current_stack,
384
+ call_boxes: current_vertices,
385
+ ..
386
+ } = &mut call_stack[index];
387
+ let vertex_index = current_vertices.len();
388
+ let depth = current_stack.len() as u32;
389
+ current_stack.push(CallStackEntry {
390
+ vertex_index,
391
+ method_id: event.data(),
392
+ });
393
+ current_vertices.push(CallBox {
394
+ start_time: encode_time(event.timestamp()),
395
+ end_time: encode_time(end_time),
396
+ method_id: event.data() as u32,
397
+ depth,
398
+ });
399
+ max_depth = max_depth.max(depth);
400
+ }
166
401
  }
167
402
  RRTraceEventType::Return => {
168
- while let Some(CallStackEntry {
169
- vertex_index,
170
- method_id,
171
- }) = current_stack.pop()
403
+ if let Some(index) = current_thread_id
404
+ .and_then(|thread_id| find_thread_index(&call_stack, thread_id).ok())
172
405
  {
173
- current_vertices[vertex_index].end_time = encode_time(event.timestamp());
174
- if method_id == event.data() {
175
- break;
406
+ let ThreadTraceState {
407
+ stack: current_stack,
408
+ call_boxes: current_vertices,
409
+ ..
410
+ } = &mut call_stack[index];
411
+ while let Some(CallStackEntry {
412
+ vertex_index,
413
+ method_id,
414
+ }) = current_stack.pop()
415
+ {
416
+ current_vertices[vertex_index].end_time =
417
+ encode_time(event.timestamp());
418
+ if method_id == event.data() {
419
+ break;
420
+ }
176
421
  }
177
422
  }
178
423
  }
179
424
  RRTraceEventType::GCStart => {
180
425
  gc_events.push(event.timestamp());
181
- for CallStackEntry { vertex_index, .. } in current_stack.iter_mut() {
182
- current_vertices[*vertex_index].end_time = encode_time(event.timestamp());
183
- *vertex_index = usize::MAX;
426
+ if let Some(index) = current_thread_id
427
+ .and_then(|thread_id| find_thread_index(&call_stack, thread_id).ok())
428
+ {
429
+ let ThreadTraceState {
430
+ stack: current_stack,
431
+ call_boxes: current_vertices,
432
+ ..
433
+ } = &mut call_stack[index];
434
+ for CallStackEntry { vertex_index, .. } in current_stack.iter_mut() {
435
+ current_vertices[*vertex_index].end_time =
436
+ encode_time(event.timestamp());
437
+ *vertex_index = usize::MAX;
438
+ }
184
439
  }
185
440
  }
186
441
  RRTraceEventType::GCEnd => {
187
442
  gc_events.push(event.timestamp());
188
- for (
189
- depth,
190
- &mut CallStackEntry {
191
- ref mut vertex_index,
192
- method_id,
193
- },
194
- ) in current_stack.iter_mut().enumerate()
443
+ if let Some(index) = current_thread_id
444
+ .and_then(|thread_id| find_thread_index(&call_stack, thread_id).ok())
195
445
  {
196
- let new_index = current_vertices.len();
197
- let depth = depth as u32;
198
- current_vertices.push(CallBox {
199
- start_time: encode_time(event.timestamp()),
200
- end_time: encode_time(end_time),
201
- method_id: method_id as u32,
446
+ let ThreadTraceState {
447
+ stack: current_stack,
448
+ call_boxes: current_vertices,
449
+ ..
450
+ } = &mut call_stack[index];
451
+ for (
202
452
  depth,
203
- });
204
- max_depth = max_depth.max(depth);
205
- *vertex_index = new_index;
453
+ &mut CallStackEntry {
454
+ ref mut vertex_index,
455
+ method_id,
456
+ },
457
+ ) in current_stack.iter_mut().enumerate()
458
+ {
459
+ let new_index = current_vertices.len();
460
+ let depth = depth as u32;
461
+ current_vertices.push(CallBox {
462
+ start_time: encode_time(event.timestamp()),
463
+ end_time: encode_time(end_time),
464
+ method_id: method_id as u32,
465
+ depth,
466
+ });
467
+ max_depth = max_depth.max(depth);
468
+ *vertex_index = new_index;
469
+ }
206
470
  }
207
471
  }
208
472
  RRTraceEventType::ThreadSuspended => {
209
- for CallStackEntry { vertex_index, .. } in current_stack.iter_mut() {
210
- current_vertices[*vertex_index].end_time = encode_time(event.timestamp());
211
- *vertex_index = usize::MAX;
473
+ if let Some(index) = current_thread_id
474
+ .and_then(|thread_id| find_thread_index(&call_stack, thread_id).ok())
475
+ {
476
+ let ThreadTraceState {
477
+ stack: current_stack,
478
+ call_boxes: current_vertices,
479
+ ..
480
+ } = &mut call_stack[index];
481
+ for CallStackEntry { vertex_index, .. } in current_stack.iter_mut() {
482
+ current_vertices[*vertex_index].end_time =
483
+ encode_time(event.timestamp());
484
+ *vertex_index = usize::MAX;
485
+ }
212
486
  }
487
+ current_thread_id = None;
213
488
  }
214
489
  RRTraceEventType::ThreadResume => {
215
490
  let thread_id = event.data() as u32;
216
- let index = call_stack.binary_search_by_key(&thread_id, |&(tid, _, _)| tid);
217
- if let Err(i) = index {
218
- call_stack.insert(i, (thread_id, Vec::new(), Vec::new()));
219
- }
220
- let (_, new_stack, new_vertices) =
221
- &mut call_stack[index.unwrap_or_else(convert::identity)];
491
+ let index = get_or_insert_thread_state(
492
+ &mut call_stack,
493
+ thread_id,
494
+ start_time,
495
+ end_time,
496
+ );
497
+ let ThreadTraceState {
498
+ stack: new_stack,
499
+ call_boxes: new_vertices,
500
+ ..
501
+ } = &mut call_stack[index];
222
502
 
223
503
  for (
224
504
  depth,
@@ -240,25 +520,53 @@ impl SlowTrace {
240
520
  *vertex_index = new_index;
241
521
  }
242
522
 
243
- (current_stack, current_vertices) = (new_stack, new_vertices);
523
+ current_thread_id = Some(thread_id);
244
524
  }
245
- RRTraceEventType::ThreadExit
246
- | RRTraceEventType::ThreadStart
247
- | RRTraceEventType::ThreadReady => {}
525
+ RRTraceEventType::ThreadStart => {
526
+ let thread_id = event.data() as u32;
527
+ let index = get_or_insert_thread_state(
528
+ &mut call_stack,
529
+ thread_id,
530
+ event.timestamp(),
531
+ end_time,
532
+ );
533
+ call_stack[index].thread_line.start_time = encode_time(event.timestamp());
534
+ }
535
+ RRTraceEventType::ThreadExit => {
536
+ let thread_id = event.data() as u32;
537
+ let index = get_or_insert_thread_state(
538
+ &mut call_stack,
539
+ thread_id,
540
+ start_time,
541
+ event.timestamp(),
542
+ );
543
+ let thread_state = &mut call_stack[index];
544
+ thread_state.thread_line.end_time = encode_time(event.timestamp());
545
+ if current_thread_id == Some(thread_id) {
546
+ for CallStackEntry { vertex_index, .. } in thread_state.stack.iter_mut() {
547
+ thread_state.call_boxes[*vertex_index].end_time =
548
+ encode_time(event.timestamp());
549
+ *vertex_index = usize::MAX;
550
+ }
551
+ current_thread_id = None;
552
+ }
553
+ }
554
+ RRTraceEventType::ThreadReady => {}
248
555
  }
249
556
  }
250
557
  SlowTrace {
251
- data: call_stack,
558
+ data: call_stack
559
+ .into_iter()
560
+ .map(ThreadTraceState::into_thread_data)
561
+ .collect(),
252
562
  max_depth,
253
563
  end_time,
254
564
  gc_events,
255
565
  }
256
566
  }
257
567
 
258
- pub fn data(&self) -> impl Iterator<Item = (u32, &[CallBox])> {
259
- self.data
260
- .iter()
261
- .map(|&(thread_id, _, ref call_box)| (thread_id, call_box.as_slice()))
568
+ pub fn data(&self) -> &[ThreadData] {
569
+ &self.data
262
570
  }
263
571
 
264
572
  pub fn gc_events(&self) -> &[u64] {
@@ -273,3 +581,76 @@ impl SlowTrace {
273
581
  self.max_depth
274
582
  }
275
583
  }
584
+
585
+ #[cfg(test)]
586
+ mod tests {
587
+ use super::*;
588
+
589
+ fn event(event_type: RRTraceEventType, timestamp: u64, data: u64) -> RRTraceEvent {
590
+ let event_bits = match event_type {
591
+ RRTraceEventType::Call => 0x0000000000000000,
592
+ RRTraceEventType::Return => 0x1000000000000000,
593
+ RRTraceEventType::GCStart => 0x2000000000000000,
594
+ RRTraceEventType::GCEnd => 0x3000000000000000,
595
+ RRTraceEventType::ThreadStart => 0x4000000000000000,
596
+ RRTraceEventType::ThreadReady => 0x5000000000000000,
597
+ RRTraceEventType::ThreadSuspended => 0x6000000000000000,
598
+ RRTraceEventType::ThreadResume => 0x7000000000000000,
599
+ RRTraceEventType::ThreadExit => 0x8000000000000000,
600
+ };
601
+ unsafe { mem::transmute([timestamp | event_bits, data]) }
602
+ }
603
+
604
+ #[test]
605
+ fn slow_trace_uses_thread_id_instead_of_vector_index() {
606
+ let fast_trace = FastTrace {
607
+ thread_stacks: HashMap::from([(2, StackState::new())]),
608
+ initial_thread_stack: StackState::new(),
609
+ current_thread: ThreadId::Id(2),
610
+ in_gc: false,
611
+ };
612
+
613
+ let trace = SlowTrace::trace(0, &fast_trace, &[event(RRTraceEventType::Call, 10, 42)]);
614
+ let thread_data = trace
615
+ .data()
616
+ .iter()
617
+ .find(|data| data.thread_id() == 2)
618
+ .unwrap();
619
+
620
+ assert_eq!(thread_data.call_boxes().len(), 1);
621
+ assert_eq!(thread_data.call_boxes()[0].method_id, 42);
622
+ }
623
+
624
+ #[test]
625
+ fn thread_start_does_not_switch_current_thread() {
626
+ let fast_trace = FastTrace {
627
+ thread_stacks: HashMap::from([(0, StackState::new())]),
628
+ initial_thread_stack: StackState::new(),
629
+ current_thread: ThreadId::Id(0),
630
+ in_gc: false,
631
+ };
632
+
633
+ let trace = SlowTrace::trace(
634
+ 0,
635
+ &fast_trace,
636
+ &[
637
+ event(RRTraceEventType::ThreadStart, 5, 1),
638
+ event(RRTraceEventType::Call, 10, 42),
639
+ ],
640
+ );
641
+ let main_thread = trace
642
+ .data()
643
+ .iter()
644
+ .find(|data| data.thread_id() == 0)
645
+ .unwrap();
646
+ let child_thread = trace
647
+ .data()
648
+ .iter()
649
+ .find(|data| data.thread_id() == 1)
650
+ .unwrap();
651
+
652
+ assert_eq!(main_thread.call_boxes().len(), 1);
653
+ assert_eq!(main_thread.call_boxes()[0].method_id, 42);
654
+ assert!(child_thread.call_boxes().is_empty());
655
+ }
656
+ }