rrtrace 0.1.0-x86_64-linux

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/ringbuffer.rs ADDED
@@ -0,0 +1,134 @@
1
+ use std::sync::atomic::{self, AtomicU64};
2
+
3
+ #[repr(C)]
4
+ #[derive(Copy, Clone, Default)]
5
+ pub struct RRTraceEvent {
6
+ timestamp_and_event_type: u64,
7
+ data: u64,
8
+ }
9
+
10
+ const EVENT_TYPE_MASK: u64 = 0xF000000000000000;
11
+
12
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13
+ pub enum RRTraceEventType {
14
+ Call,
15
+ Return,
16
+ GCStart,
17
+ GCEnd,
18
+ ThreadStart,
19
+ ThreadReady,
20
+ ThreadSuspended,
21
+ ThreadResume,
22
+ ThreadExit,
23
+ }
24
+
25
+ impl RRTraceEvent {
26
+ pub fn timestamp(&self) -> u64 {
27
+ self.timestamp_and_event_type & !EVENT_TYPE_MASK
28
+ }
29
+
30
+ pub fn event_type(&self) -> RRTraceEventType {
31
+ match self.timestamp_and_event_type & EVENT_TYPE_MASK {
32
+ 0x0000000000000000 => RRTraceEventType::Call,
33
+ 0x1000000000000000 => RRTraceEventType::Return,
34
+ 0x2000000000000000 => RRTraceEventType::GCStart,
35
+ 0x3000000000000000 => RRTraceEventType::GCEnd,
36
+ 0x4000000000000000 => RRTraceEventType::ThreadStart,
37
+ 0x5000000000000000 => RRTraceEventType::ThreadReady,
38
+ 0x6000000000000000 => RRTraceEventType::ThreadSuspended,
39
+ 0x7000000000000000 => RRTraceEventType::ThreadResume,
40
+ 0x8000000000000000 => RRTraceEventType::ThreadExit,
41
+ _ => unreachable!(),
42
+ }
43
+ }
44
+
45
+ pub fn data(&self) -> u64 {
46
+ self.data
47
+ }
48
+ }
49
+
50
+ pub const SIZE: usize = 65_536;
51
+ pub const MASK: usize = SIZE - 1;
52
+
53
+ #[repr(C, align(64))]
54
+ struct RRTraceEventRingBufferWriter {
55
+ write_index: AtomicU64,
56
+ read_index_cache: u64,
57
+ }
58
+
59
+ #[repr(C, align(64))]
60
+ struct RRTraceEventRingBufferReader {
61
+ read_index: AtomicU64,
62
+ write_index_cache: u64,
63
+ }
64
+
65
+ #[repr(C)]
66
+ pub struct RRTraceEventRingBuffer {
67
+ buffer: [RRTraceEvent; SIZE],
68
+ writer: RRTraceEventRingBufferWriter,
69
+ reader: RRTraceEventRingBufferReader,
70
+ }
71
+
72
+ impl RRTraceEventRingBuffer {
73
+ unsafe fn read(this: *mut Self, buffer: &mut [RRTraceEvent]) -> usize {
74
+ unsafe {
75
+ let read_index = (*this).reader.read_index.load(atomic::Ordering::Acquire);
76
+ let write_index = (*this).reader.write_index_cache;
77
+ let available = (write_index - read_index) as usize;
78
+ let available = if available == 0 {
79
+ (*this).reader.write_index_cache =
80
+ (*this).writer.write_index.load(atomic::Ordering::Acquire);
81
+ let write_index = (*this).reader.write_index_cache;
82
+ (write_index - read_index) as usize
83
+ } else {
84
+ available
85
+ };
86
+ let read_len = available.min(buffer.len());
87
+ let buffer = &mut buffer[..read_len];
88
+
89
+ let first_part_len = (&(*this).buffer)[read_index as usize & MASK..].len();
90
+ if read_len <= first_part_len {
91
+ buffer
92
+ .copy_from_slice(&(&(*this).buffer)[read_index as usize & MASK..][..read_len]);
93
+ } else {
94
+ buffer[..first_part_len]
95
+ .copy_from_slice(&(&(*this).buffer)[read_index as usize & MASK..]);
96
+ buffer[first_part_len..]
97
+ .copy_from_slice(&(&(*this).buffer)[0..read_len - first_part_len]);
98
+ }
99
+
100
+ (*this)
101
+ .reader
102
+ .read_index
103
+ .store(read_index + read_len as u64, atomic::Ordering::Release);
104
+ read_len
105
+ }
106
+ }
107
+ }
108
+
109
+ pub struct EventRingBuffer {
110
+ ringbuffer: *mut RRTraceEventRingBuffer,
111
+ drop: Option<Box<dyn FnOnce()>>,
112
+ }
113
+
114
+ impl EventRingBuffer {
115
+ pub unsafe fn new(
116
+ ringbuffer: *mut RRTraceEventRingBuffer,
117
+ drop: impl FnOnce() + 'static,
118
+ ) -> Self {
119
+ EventRingBuffer {
120
+ ringbuffer,
121
+ drop: Some(Box::new(drop)),
122
+ }
123
+ }
124
+
125
+ pub fn read(&mut self, buffer: &mut [RRTraceEvent]) -> usize {
126
+ unsafe { RRTraceEventRingBuffer::read(self.ringbuffer, buffer) }
127
+ }
128
+ }
129
+
130
+ impl Drop for EventRingBuffer {
131
+ fn drop(&mut self) {
132
+ self.drop.take().unwrap()();
133
+ }
134
+ }
data/src/shader.wgsl ADDED
@@ -0,0 +1,115 @@
1
+ struct Vertex {
2
+ @location(0) position: vec3<f32>,
3
+ }
4
+
5
+ struct CallBox {
6
+ @location(1) start_time: vec2<u32>,
7
+ @location(2) end_time: vec2<u32>,
8
+ @location(3) method_id: u32,
9
+ @location(4) depth: u32,
10
+ }
11
+
12
+ struct GCBox {
13
+ @location(1) time: vec2<u32>,
14
+ }
15
+
16
+ struct CameraUniform {
17
+ view_proj: mat4x4<f32>,
18
+ base_time: vec2<u32>, // x: lo, y: hi
19
+ max_depth: u32,
20
+ num_threads: u32,
21
+ }
22
+
23
+ struct ThreadInfo {
24
+ lane_id: u32,
25
+ }
26
+
27
+ @group(0) @binding(0)
28
+ var<uniform> camera: CameraUniform;
29
+
30
+ @group(0) @binding(1)
31
+ var<uniform> thread_info: ThreadInfo;
32
+
33
+ struct VertexOutput {
34
+ @builtin(position) clip_position: vec4<f32>,
35
+ @location(0) color: vec4<f32>,
36
+ }
37
+
38
+ fn sub64(a: vec2<u32>, b: vec2<u32>) -> vec2<u32> {
39
+ if (a.x < b.x) {
40
+ let lo = a.x + 0x80000000u - b.x;
41
+ let hi = a.y - 1 - b.y;
42
+ return vec2<u32>(lo, hi);
43
+ } else {
44
+ let lo = a.x - b.x;
45
+ let hi = a.y - b.y;
46
+ return vec2<u32>(lo, hi);
47
+ }
48
+ }
49
+
50
+ fn u64tof32(v: vec2<u32>) -> f32 {
51
+ return f32(v.y) * 2147483648.0 + f32(v.x);
52
+ }
53
+
54
+ fn get_color(method_id: u32) -> vec4<f32> {
55
+ let m = method_id;
56
+ let r = f32((m * 123u) % 255u) / 255.0;
57
+ let g = f32((m * 456u) % 255u) / 255.0;
58
+ let b = f32((m * 789u) % 255u) / 255.0;
59
+ return vec4<f32>(r, g, b, 1.0);
60
+ }
61
+
62
+ @vertex
63
+ fn vs_main(
64
+ v: Vertex,
65
+ call: CallBox,
66
+ ) -> VertexOutput {
67
+ var end_time: vec2<u32>;
68
+ if (call.end_time.y == 0xffffffffu) {
69
+ end_time = vec2<u32>(0, 0);
70
+ } else {
71
+ end_time = sub64(camera.base_time, call.end_time);
72
+ }
73
+ let start_time = sub64(camera.base_time, call.start_time);
74
+
75
+ let x = select(start_time, end_time, v.position.x > 0.5);
76
+
77
+ let world_pos = vec3<f32>(
78
+ u64tof32(x) / 500000000.0,
79
+ (f32(call.depth) + v.position.y) / f32(camera.max_depth),
80
+ (f32(thread_info.lane_id) + v.position.z) / f32(camera.num_threads),
81
+ );
82
+
83
+ var out: VertexOutput;
84
+ out.color = get_color(call.method_id);
85
+ out.clip_position = camera.view_proj * vec4<f32>(world_pos, 1.0);
86
+ return out;
87
+ }
88
+
89
+ @fragment
90
+ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
91
+ return in.color;
92
+ }
93
+
94
+ struct GCVertex {
95
+ @location(0) position: vec2<f32>,
96
+ }
97
+
98
+ @vertex
99
+ fn vs_gc(
100
+ v: GCVertex,
101
+ gc: GCBox,
102
+ ) -> VertexOutput {
103
+ let time = sub64(camera.base_time, gc.time);
104
+
105
+ let world_pos = vec3<f32>(
106
+ u64tof32(time) / 500000000.0,
107
+ v.position.x,
108
+ v.position.y,
109
+ );
110
+
111
+ var out: VertexOutput;
112
+ out.color = vec4<f32>(1.0, 0.5, 0.0, 0.1);
113
+ out.clip_position = camera.view_proj * vec4<f32>(world_pos, 1.0);
114
+ return out;
115
+ }
data/src/shm_unix.rs ADDED
@@ -0,0 +1,47 @@
1
+ use std::ffi::CString;
2
+
3
+ pub struct SharedMemory {
4
+ ptr: *mut u8,
5
+ name: CString,
6
+ size: usize,
7
+ }
8
+
9
+ impl SharedMemory {
10
+ pub unsafe fn open(name: CString, size: usize) -> SharedMemory {
11
+ let fd = unsafe { libc::shm_open(name.as_ptr(), libc::O_RDWR, 0) };
12
+ if fd < 0 {
13
+ panic!("shm_open failed");
14
+ }
15
+ let memory = unsafe {
16
+ libc::mmap(
17
+ std::ptr::null_mut(),
18
+ size,
19
+ libc::PROT_READ | libc::PROT_WRITE,
20
+ libc::MAP_SHARED,
21
+ fd,
22
+ 0,
23
+ )
24
+ };
25
+ if memory == libc::MAP_FAILED {
26
+ panic!("mmap failed");
27
+ }
28
+ SharedMemory {
29
+ ptr: memory as *mut u8,
30
+ name,
31
+ size,
32
+ }
33
+ }
34
+
35
+ pub fn as_ptr<T>(&self) -> *mut T {
36
+ self.ptr.cast()
37
+ }
38
+ }
39
+
40
+ impl Drop for SharedMemory {
41
+ fn drop(&mut self) {
42
+ unsafe {
43
+ libc::munmap(self.ptr as *mut libc::c_void, self.size);
44
+ libc::shm_unlink(self.name.as_ptr());
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,44 @@
1
+ use std::ffi::CString;
2
+ use windows_sys::Win32::Foundation::{CloseHandle, GetLastError, HANDLE};
3
+ use windows_sys::Win32::System::Memory::{
4
+ FILE_MAP_ALL_ACCESS, MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile, OpenFileMappingA,
5
+ UnmapViewOfFile,
6
+ };
7
+
8
+ pub struct SharedMemory {
9
+ ptr: MEMORY_MAPPED_VIEW_ADDRESS,
10
+ handle: HANDLE,
11
+ }
12
+
13
+ impl SharedMemory {
14
+ pub unsafe fn open(name: CString, size: usize) -> SharedMemory {
15
+ let handle =
16
+ unsafe { OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, name.as_ptr() as *const u8) };
17
+ if handle.is_null() {
18
+ panic!("OpenFileMappingA failed with error {}", unsafe {
19
+ GetLastError()
20
+ });
21
+ }
22
+
23
+ let ptr = unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size) };
24
+ if ptr.Value.is_null() {
25
+ unsafe { CloseHandle(handle) };
26
+ panic!("MapViewOfFile failed");
27
+ }
28
+
29
+ SharedMemory { ptr, handle }
30
+ }
31
+
32
+ pub fn as_ptr<T>(&self) -> *mut T {
33
+ self.ptr.Value.cast()
34
+ }
35
+ }
36
+
37
+ impl Drop for SharedMemory {
38
+ fn drop(&mut self) {
39
+ unsafe {
40
+ UnmapViewOfFile(self.ptr);
41
+ CloseHandle(self.handle);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,275 @@
1
+ use crate::ringbuffer::{RRTraceEvent, RRTraceEventType};
2
+ use smallvec::SmallVec;
3
+ use std::convert;
4
+ use std::fmt::Debug;
5
+
6
+ #[repr(C)]
7
+ #[derive(Copy, Default, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
8
+ pub struct CallBox {
9
+ start_time: [u32; 2],
10
+ end_time: [u32; 2],
11
+ method_id: u32,
12
+ depth: u32,
13
+ }
14
+
15
+ pub const VISIBLE_DURATION: u64 = 1_000_000_000 * 5;
16
+
17
+ pub fn encode_time(time: u64) -> [u32; 2] {
18
+ [
19
+ (time & 0x7fffffff) as u32,
20
+ ((time >> 31) & 0xffffffff) as u32,
21
+ ]
22
+ }
23
+
24
+ #[derive(Debug, Clone)]
25
+ pub struct FastTrace {
26
+ thread_stacks: SmallVec<[(u32, SmallVec<[u64; 4]>); 1]>,
27
+ current_thread: u32,
28
+ in_gc: bool,
29
+ }
30
+
31
+ impl FastTrace {
32
+ pub fn new() -> FastTrace {
33
+ FastTrace {
34
+ thread_stacks: smallvec::smallvec![(0, SmallVec::new())],
35
+ current_thread: 0,
36
+ in_gc: false,
37
+ }
38
+ }
39
+ pub fn process_events(&mut self, events: &[RRTraceEvent]) {
40
+ for &event in events {
41
+ match event.event_type() {
42
+ RRTraceEventType::Call => {
43
+ let method_id = event.data();
44
+ self.thread_stacks[self.current_thread as usize]
45
+ .1
46
+ .push(method_id);
47
+ }
48
+ RRTraceEventType::Return => {
49
+ 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) {}
52
+ }
53
+ RRTraceEventType::ThreadSuspended => {
54
+ self.current_thread = u32::MAX;
55
+ }
56
+ RRTraceEventType::ThreadResume => {
57
+ 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;
70
+ }
71
+ RRTraceEventType::ThreadExit => {
72
+ 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);
78
+ };
79
+ }
80
+ RRTraceEventType::GCStart
81
+ | RRTraceEventType::GCEnd
82
+ | RRTraceEventType::ThreadStart
83
+ | RRTraceEventType::ThreadReady => {}
84
+ }
85
+ }
86
+ self.in_gc = events.last().unwrap().event_type() == RRTraceEventType::GCStart;
87
+ }
88
+ }
89
+
90
+ #[derive(Debug, Clone)]
91
+ struct CallStackEntry {
92
+ vertex_index: usize,
93
+ method_id: u64,
94
+ }
95
+
96
+ pub struct SlowTrace {
97
+ data: Vec<(u32, Vec<CallStackEntry>, Vec<CallBox>)>,
98
+ max_depth: u32,
99
+ end_time: u64,
100
+ gc_events: Vec<u64>,
101
+ }
102
+
103
+ impl SlowTrace {
104
+ pub fn trace(start_time: u64, fast_trace: FastTrace, events: &[RRTraceEvent]) -> SlowTrace {
105
+ let end_time = events.last().unwrap().timestamp();
106
+ let mut max_depth = 0;
107
+ let FastTrace {
108
+ thread_stacks,
109
+ current_thread,
110
+ in_gc,
111
+ } = fast_trace;
112
+ let mut gc_events = Vec::new();
113
+ 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
+ )
127
+ })
128
+ .collect::<Vec<_>>();
129
+ if !in_gc && let Some((_, stack, vertices)) = call_stack.get_mut(current_thread as usize) {
130
+ vertices.reserve(stack.len());
131
+ for (depth, entry) in stack.iter_mut().enumerate() {
132
+ entry.vertex_index = vertices.len();
133
+ let depth = depth as u32;
134
+ max_depth = max_depth.max(depth);
135
+ vertices.push(CallBox {
136
+ start_time: encode_time(start_time),
137
+ end_time: encode_time(end_time),
138
+ method_id: entry.method_id as u32,
139
+ depth,
140
+ });
141
+ }
142
+ }
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
+ });
150
+ for event in events {
151
+ match event.event_type() {
152
+ 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);
166
+ }
167
+ RRTraceEventType::Return => {
168
+ while let Some(CallStackEntry {
169
+ vertex_index,
170
+ method_id,
171
+ }) = current_stack.pop()
172
+ {
173
+ current_vertices[vertex_index].end_time = encode_time(event.timestamp());
174
+ if method_id == event.data() {
175
+ break;
176
+ }
177
+ }
178
+ }
179
+ RRTraceEventType::GCStart => {
180
+ 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;
184
+ }
185
+ }
186
+ RRTraceEventType::GCEnd => {
187
+ 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()
195
+ {
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,
202
+ depth,
203
+ });
204
+ max_depth = max_depth.max(depth);
205
+ *vertex_index = new_index;
206
+ }
207
+ }
208
+ 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;
212
+ }
213
+ }
214
+ RRTraceEventType::ThreadResume => {
215
+ 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)];
222
+
223
+ for (
224
+ depth,
225
+ &mut CallStackEntry {
226
+ ref mut vertex_index,
227
+ method_id,
228
+ },
229
+ ) in new_stack.iter_mut().enumerate()
230
+ {
231
+ let new_index = new_vertices.len();
232
+ let depth = depth as u32;
233
+ new_vertices.push(CallBox {
234
+ start_time: encode_time(event.timestamp()),
235
+ end_time: encode_time(end_time),
236
+ method_id: method_id as u32,
237
+ depth,
238
+ });
239
+ max_depth = max_depth.max(depth);
240
+ *vertex_index = new_index;
241
+ }
242
+
243
+ (current_stack, current_vertices) = (new_stack, new_vertices);
244
+ }
245
+ RRTraceEventType::ThreadExit
246
+ | RRTraceEventType::ThreadStart
247
+ | RRTraceEventType::ThreadReady => {}
248
+ }
249
+ }
250
+ SlowTrace {
251
+ data: call_stack,
252
+ max_depth,
253
+ end_time,
254
+ gc_events,
255
+ }
256
+ }
257
+
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()))
262
+ }
263
+
264
+ pub fn gc_events(&self) -> &[u64] {
265
+ &self.gc_events
266
+ }
267
+
268
+ pub fn end_time(&self) -> u64 {
269
+ self.end_time
270
+ }
271
+
272
+ pub fn max_depth(&self) -> u32 {
273
+ self.max_depth
274
+ }
275
+ }
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rrtrace
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: x86_64-linux
6
+ authors:
7
+ - White-Green
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rake-compiler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Rrtrace is a profiling tool that captures Ruby method calls and visualizes
27
+ them using a high-performance Rust renderer.
28
+ email:
29
+ - 43771790+White-Green@users.noreply.github.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".github/workflows/release.yml"
35
+ - Cargo.lock
36
+ - Cargo.toml
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - ext/rrtrace/extconf.rb
41
+ - ext/rrtrace/process_manager_posix.h
42
+ - ext/rrtrace/process_manager_windows.h
43
+ - ext/rrtrace/rrtrace.c
44
+ - ext/rrtrace/rrtrace.h
45
+ - ext/rrtrace/rrtrace_event.h
46
+ - ext/rrtrace/rrtrace_event_ringbuffer.h
47
+ - ext/rrtrace/rust_build_helper.rb
48
+ - ext/rrtrace/shared_memory_posix.h
49
+ - ext/rrtrace/shared_memory_windows.h
50
+ - lib/rrtrace.rb
51
+ - lib/rrtrace/rrtrace.so
52
+ - lib/rrtrace/version.rb
53
+ - mise.toml
54
+ - sig/rrtrace.rbs
55
+ - src/main.rs
56
+ - src/renderer.rs
57
+ - src/renderer/vertex_arena.rs
58
+ - src/ringbuffer.rs
59
+ - src/shader.wgsl
60
+ - src/shm_unix.rs
61
+ - src/shm_windows.rs
62
+ - src/trace_state.rs
63
+ homepage: https://github.com/White-Green/rrtrace
64
+ licenses:
65
+ - MIT
66
+ metadata:
67
+ homepage_uri: https://github.com/White-Green/rrtrace
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
76
+ - - "<"
77
+ - !ruby/object:Gem::Version
78
+ version: 4.1.dev
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 4.0.3
86
+ specification_version: 4
87
+ summary: A Ruby trace tool with Rust-based visualizer
88
+ test_files: []