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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a124628bbb1039ea27c48968665bf6959431debeeba3068fca7525e7a4e47c2
4
- data.tar.gz: 8b1e21f4b384326c82f59e6f43739a4be18c3463a0f461b7f4375523696488ae
3
+ metadata.gz: 4770d1ed4c3dfd4e968190d28c300e613b4fe660407a481f85a567a74139c2b6
4
+ data.tar.gz: b867a6b6d82c1069b0c3a5892a4020a17594d1711094d2880fc3b4e268a37461
5
5
  SHA512:
6
- metadata.gz: 32a443232b6937c43efd666a635347484da22c5d67da40c7c1ed23b31ccfb08a750af48f49a2182061acb842bdf837c0df763b120f9f550726d739731f7669d0
7
- data.tar.gz: d73f389d094a12cad5d73c34ec8727d939ec98f6c5b2fe5b6a74f98bc25021fccae41c561a30c787a09eea90241c8812d32cc743f4ceccd825864695753616b5
6
+ metadata.gz: f94c33853d5c3bd8c145659cee3248f36d874d5abb36b483c0d4a3b760edf8ac387a174e3624e055fe93db18fd41bb91beb7a2ba160c9959add8fcc4165daa20
7
+ data.tar.gz: e87f11308160e5b2ab3c7effc25dbb5958ba537f79521c3e1d8cd16ff30f95ba02f4db6e06c90fbc1eb0c4c6afd6d75be6f5f31c4e7561462bc09ff9c4d17107
@@ -0,0 +1,36 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "cargo"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
7
+ cooldown:
8
+ default-days: 3
9
+ groups:
10
+ semver-compatible:
11
+ applies-to: version-updates
12
+ update-types:
13
+ - "minor"
14
+ - "patch"
15
+ - package-ecosystem: "bundler"
16
+ directory: "/"
17
+ schedule:
18
+ interval: "daily"
19
+ cooldown:
20
+ default-days: 3
21
+ groups:
22
+ semver-compatible:
23
+ applies-to: version-updates
24
+ update-types:
25
+ - "minor"
26
+ - "patch"
27
+ - package-ecosystem: "github-actions"
28
+ directory: "/"
29
+ schedule:
30
+ interval: "daily"
31
+ cooldown:
32
+ default-days: 3
33
+ groups:
34
+ default:
35
+ patterns:
36
+ - "*"
@@ -0,0 +1,94 @@
1
+ name: CI
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ rustfmt:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v6
10
+
11
+ - uses: dtolnay/rust-toolchain@stable
12
+
13
+ - run: cargo fmt --all --check
14
+
15
+ clippy:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+
20
+ - uses: dtolnay/rust-toolchain@stable
21
+ with:
22
+ components: clippy
23
+
24
+ - uses: actions/cache@v5
25
+ with:
26
+ path: |
27
+ ~/.cargo/bin/
28
+ ~/.cargo/registry/index/
29
+ ~/.cargo/registry/cache/
30
+ ~/.cargo/git/db/
31
+ key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
32
+ restore-keys: ${{ github.ref_name != github.event.repository.default_branch && format('{0}-cargo-', runner.os) }}
33
+
34
+ - uses: actions/cache@v5
35
+ with:
36
+ path: target
37
+ key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('Cargo.lock') }}
38
+ restore-keys: ${{ github.ref_name != github.event.repository.default_branch && format('{0}-cargo-clippy-', runner.os) }}
39
+
40
+ - run: cargo fetch --locked
41
+
42
+ - run: cargo clippy --tests -- -D warnings
43
+
44
+ test:
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v6
48
+
49
+ - uses: dtolnay/rust-toolchain@stable
50
+
51
+ - uses: actions/cache@v5
52
+ with:
53
+ path: |
54
+ ~/.cargo/bin/
55
+ ~/.cargo/registry/index/
56
+ ~/.cargo/registry/cache/
57
+ ~/.cargo/git/db/
58
+ key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
59
+ restore-keys: ${{ github.ref_name != github.event.repository.default_branch && format('{0}-cargo-', runner.os) }}
60
+
61
+ - uses: actions/cache@v5
62
+ with:
63
+ path: target
64
+ key: ${{ runner.os }}-cargo-test-${{ hashFiles('Cargo.lock') }}
65
+ restore-keys: ${{ github.ref_name != github.event.repository.default_branch && format('{0}-cargo-test-', runner.os) }}
66
+
67
+ - run: cargo fetch --locked
68
+
69
+ - run: cargo test --locked
70
+
71
+ taplo:
72
+ runs-on: ubuntu-latest
73
+ steps:
74
+ - uses: actions/checkout@v6
75
+
76
+ - id: latest-taplo-version
77
+ run: |
78
+ V=$(curl https://index.crates.io/ta/pl/taplo-cli | jq -r 'select(.yanked == false) | .vers' | grep --extended-regexp '^[0-9]+\.[0-9]+\.[0-9]+$' | sort --reverse --version-sort | head --lines=1)
79
+ echo "Latest version of taplo-cli: $V"
80
+ echo "version=$V" >> $GITHUB_OUTPUT
81
+
82
+ - id: taplo-bin-cache
83
+ uses: actions/cache@v5
84
+ with:
85
+ path: ~/.cargo/bin/taplo
86
+ key: ${{ runner.os }}-taplo-cli-${{ steps.latest-taplo-version.outputs.version }}
87
+
88
+ - if: steps.taplo-bin-cache.outputs.cache-hit != 'true'
89
+ uses: dtolnay/rust-toolchain@stable
90
+
91
+ - if: steps.taplo-bin-cache.outputs.cache-hit != 'true'
92
+ run: cargo install taplo-cli@${{ steps.latest-taplo-version.outputs.version }} --locked
93
+
94
+ - run: ~/.cargo/bin/taplo fmt --check
@@ -12,7 +12,7 @@ jobs:
12
12
  os: [ ubuntu-latest, macos-latest, windows-latest ]
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
 
17
17
  - name: Set up Ruby
18
18
  uses: ruby/setup-ruby@v1
@@ -46,7 +46,7 @@ jobs:
46
46
  mv rrtrace-${{ steps.get_version.outputs.version }}.gem pkg/
47
47
 
48
48
  - name: Upload artifacts
49
- uses: actions/upload-artifact@v4
49
+ uses: actions/upload-artifact@v7
50
50
  with:
51
51
  name: gems-${{ matrix.os }}
52
52
  path: pkg/*.gem
@@ -60,7 +60,7 @@ jobs:
60
60
  id-token: write
61
61
  contents: write
62
62
  steps:
63
- - uses: actions/checkout@v4
63
+ - uses: actions/checkout@v6
64
64
 
65
65
  - name: Set up Ruby
66
66
  uses: ruby/setup-ruby@v1
@@ -69,7 +69,7 @@ jobs:
69
69
  bundler-cache: true
70
70
 
71
71
  - name: Download artifacts
72
- uses: actions/download-artifact@v4
72
+ uses: actions/download-artifact@v8
73
73
  with:
74
74
  path: pkg
75
75
  merge-multiple: true
data/Cargo.lock CHANGED
@@ -100,6 +100,16 @@ dependencies = [
100
100
  "libloading",
101
101
  ]
102
102
 
103
+ [[package]]
104
+ name = "atomic-wait"
105
+ version = "1.1.0"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "a55b94919229f2c42292fd71ffa4b75e83193bffdd77b1e858cd55fd2d0b0ea8"
108
+ dependencies = [
109
+ "libc",
110
+ "windows-sys 0.42.0",
111
+ ]
112
+
103
113
  [[package]]
104
114
  name = "atomic-waker"
105
115
  version = "1.1.2"
@@ -333,25 +343,6 @@ dependencies = [
333
343
  "libc",
334
344
  ]
335
345
 
336
- [[package]]
337
- name = "crossbeam-deque"
338
- version = "0.8.6"
339
- source = "registry+https://github.com/rust-lang/crates.io-index"
340
- checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
341
- dependencies = [
342
- "crossbeam-epoch",
343
- "crossbeam-utils",
344
- ]
345
-
346
- [[package]]
347
- name = "crossbeam-epoch"
348
- version = "0.9.18"
349
- source = "registry+https://github.com/rust-lang/crates.io-index"
350
- checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
351
- dependencies = [
352
- "crossbeam-utils",
353
- ]
354
-
355
346
  [[package]]
356
347
  name = "crossbeam-queue"
357
348
  version = "0.3.12"
@@ -1290,16 +1281,6 @@ version = "0.6.2"
1290
1281
  source = "registry+https://github.com/rust-lang/crates.io-index"
1291
1282
  checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
1292
1283
 
1293
- [[package]]
1294
- name = "rayon-core"
1295
- version = "1.13.0"
1296
- source = "registry+https://github.com/rust-lang/crates.io-index"
1297
- checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
1298
- dependencies = [
1299
- "crossbeam-deque",
1300
- "crossbeam-utils",
1301
- ]
1302
-
1303
1284
  [[package]]
1304
1285
  name = "redox_syscall"
1305
1286
  version = "0.4.1"
@@ -1337,12 +1318,12 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
1337
1318
  name = "rrtrace"
1338
1319
  version = "0.1.0"
1339
1320
  dependencies = [
1321
+ "atomic-wait",
1340
1322
  "bytemuck",
1341
1323
  "crossbeam-queue",
1342
1324
  "glam",
1343
1325
  "libc",
1344
1326
  "pollster",
1345
- "rayon-core",
1346
1327
  "smallvec",
1347
1328
  "wgpu",
1348
1329
  "windows-sys 0.61.2",
@@ -2160,6 +2141,21 @@ dependencies = [
2160
2141
  "windows-link",
2161
2142
  ]
2162
2143
 
2144
+ [[package]]
2145
+ name = "windows-sys"
2146
+ version = "0.42.0"
2147
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2148
+ checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
2149
+ dependencies = [
2150
+ "windows_aarch64_gnullvm 0.42.2",
2151
+ "windows_aarch64_msvc 0.42.2",
2152
+ "windows_i686_gnu 0.42.2",
2153
+ "windows_i686_msvc 0.42.2",
2154
+ "windows_x86_64_gnu 0.42.2",
2155
+ "windows_x86_64_gnullvm 0.42.2",
2156
+ "windows_x86_64_msvc 0.42.2",
2157
+ ]
2158
+
2163
2159
  [[package]]
2164
2160
  name = "windows-sys"
2165
2161
  version = "0.45.0"
data/Cargo.toml CHANGED
@@ -1,17 +1,17 @@
1
1
  [package]
2
+ edition = "2024"
2
3
  name = "rrtrace"
3
4
  version = "0.1.0"
4
- edition = "2024"
5
5
 
6
6
  [profile.release]
7
7
  overflow-checks = true
8
8
 
9
9
  [dependencies]
10
- bytemuck = { version = "1.24.0", features = ["derive"] }
10
+ atomic-wait = "1.1.0"
11
+ bytemuck = { features = ["derive"], version = "1.24.0" }
11
12
  crossbeam-queue = "0.3.12"
12
13
  glam = "0.30.9"
13
14
  pollster = "0.4.0"
14
- rayon-core = "1.13.0"
15
15
  smallvec = "1.15.1"
16
16
  wgpu = "28.0.0"
17
17
  winit = "0.30.12"
@@ -20,4 +20,8 @@ winit = "0.30.12"
20
20
  libc = "0.2.178"
21
21
 
22
22
  [target.'cfg(windows)'.dependencies]
23
- windows-sys = { version = "0.61.2", features = ["Win32_System_Memory", "Win32_Foundation", "Win32_System_Diagnostics_Debug"] }
23
+ windows-sys = { features = [
24
+ "Win32_Foundation",
25
+ "Win32_System_Diagnostics_Debug",
26
+ "Win32_System_Memory",
27
+ ], version = "0.61.2" }
data/README.md CHANGED
@@ -1,39 +1,100 @@
1
- # Rrtrace
1
+ # rrtrace
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
3
+ `rrtrace` is a Ruby tracing tool with a Rust-based visualizer.
4
+ It records Ruby method calls, returns, GC transitions, and thread lifecycle events, then streams them to a separate GPU renderer process for real-time visualization.
4
5
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rrtrace`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+ ## Features
6
7
 
7
- ## Installation
8
+ - Trace Ruby `call` / `return` and C-level `c_call` / `c_return` events
9
+ - Capture GC enter / exit events
10
+ - Capture thread start / ready / suspend / resume / exit events
11
+ - Stream events through shared memory to a separate visualizer process
12
+ - Render the trace with a native Rust application built on `wgpu` and `winit`
8
13
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
14
+ ## How It Works
10
15
 
11
- Install the gem and add to the application's Gemfile by executing:
16
+ `rrtrace` consists of two parts:
17
+
18
+ 1. A Ruby native extension written in C
19
+ - Installs Ruby tracepoints and internal thread event hooks
20
+ - Writes trace events into a shared-memory ring buffer
21
+ - Launches the visualizer process
22
+ 2. A Rust visualizer
23
+ - Reads events from shared memory
24
+ - Builds trace state in background threads
25
+ - Renders the result in a desktop window using the GPU
26
+
27
+ At a high level, the data flow is:
28
+
29
+ `Ruby VM` -> `C extension hooks` -> `shared-memory ring buffer` -> `Rust visualizer` -> `window`
30
+
31
+ ## Usage
32
+
33
+ ### CLI
34
+
35
+ Run a Ruby script under `rrtrace`:
12
36
 
13
37
  ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
38
+ rrtrace path/to/script.rb
15
39
  ```
16
40
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
41
+ You can also use the repository executable directly during development:
18
42
 
19
43
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
44
+ bundle exec exe/rrtrace path/to/script.rb
21
45
  ```
22
46
 
23
- ## Usage
47
+ The command starts the visualizer process, opens a window, loads the target Ruby file, and stops tracing when the program exits.
48
+
49
+ ### Ruby API
50
+
51
+ For manual control:
52
+
53
+ ```ruby
54
+ require "rrtrace"
55
+
56
+ Rrtrace.start
57
+
58
+ # your Ruby code here
24
59
 
25
- TODO: Write usage instructions here
60
+ Rrtrace.stop
61
+ ```
62
+
63
+ Available methods:
64
+
65
+ - `Rrtrace.start`
66
+ - `Rrtrace.stop`
67
+ - `Rrtrace.started?`
68
+ - `Rrtrace.visualizer_path`
69
+
70
+ `Rrtrace.stop` is also registered with `at_exit`, so tracing is cleaned up automatically when the process exits normally.
26
71
 
27
72
  ## Development
28
73
 
29
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
74
+ Install dependencies:
75
+
76
+ ```bash
77
+ bin/setup
78
+ ```
30
79
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
80
+ Build the extension and visualizer:
32
81
 
33
- ## Contributing
82
+ ```bash
83
+ bundle exec rake build
84
+ ```
85
+
86
+ Useful commands:
87
+
88
+ ```bash
89
+ bundle exec rake compile
90
+ bundle exec rake standard
91
+ cargo test --locked
92
+ cargo fmt --all
93
+ cargo clippy --tests -- -D warnings
94
+ ```
34
95
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rrtrace.
96
+ The Rust visualizer built during extension compilation is copied to `libexec/rrtrace` (or `libexec/rrtrace.exe` on Windows).
36
97
 
37
98
  ## License
38
99
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
100
+ This project is licensed under the MIT License. See [LICENSE.txt](LICENSE.txt).
data/exe/rrtrace ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rrtrace"
5
+
6
+ if ARGV.empty?
7
+ $stderr.puts "Usage: rrtrace <executable.rb> [args...]"
8
+ exit 1
9
+ end
10
+
11
+ executable = ARGV.shift
12
+
13
+ $0 = executable
14
+ Rrtrace.start
15
+ load executable
16
+ Rrtrace.stop
@@ -4,9 +4,14 @@
4
4
  #include <sys/types.h>
5
5
  #include <sys/wait.h>
6
6
  #include <spawn.h>
7
+ #include <signal.h>
7
8
 
8
9
  typedef pid_t process_id;
9
10
 
11
+ static inline process_id invalid_process_id(void) {
12
+ return 0;
13
+ }
14
+
10
15
  extern char **environ;
11
16
 
12
17
  static inline process_id spawn_process(const char *cmd, char * const* restrict argv) {
@@ -17,9 +22,20 @@ static inline process_id spawn_process(const char *cmd, char * const* restrict a
17
22
  }
18
23
 
19
24
  static inline int is_process_running(process_id pid) {
25
+ if (pid == 0) return 0;
20
26
  int status;
21
- waitpid(pid, &status, WNOHANG);
22
- return !WIFEXITED(status);
27
+ pid_t result = waitpid(pid, &status, WNOHANG);
28
+ return result == 0;
29
+ }
30
+
31
+ static inline void terminate_process(process_id pid) {
32
+ if (pid == 0) return;
33
+ kill(pid, SIGTERM);
34
+ waitpid(pid, NULL, 0);
35
+ }
36
+
37
+ static inline void close_process(process_id pid) {
38
+ (void)pid;
23
39
  }
24
40
 
25
41
  #endif /* PROCESS_MANAGER_POSIX_H */
@@ -6,6 +6,10 @@
6
6
 
7
7
  typedef HANDLE process_id;
8
8
 
9
+ static inline process_id invalid_process_id(void) {
10
+ return NULL;
11
+ }
12
+
9
13
  static inline process_id spawn_process(const char *cmd, char * const* argv) {
10
14
  STARTUPINFOA si;
11
15
  PROCESS_INFORMATION pi;
@@ -37,4 +41,14 @@ static inline int is_process_running(process_id pid) {
37
41
  return 0;
38
42
  }
39
43
 
44
+ static inline void terminate_process(process_id pid) {
45
+ if (pid == NULL) return;
46
+ TerminateProcess(pid, 1);
47
+ }
48
+
49
+ static inline void close_process(process_id pid) {
50
+ if (pid == NULL) return;
51
+ CloseHandle(pid);
52
+ }
53
+
40
54
  #endif /* PROCESS_MANAGER_WIN_H */