gvl-tracing 0.1.1 → 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 +4 -4
- data/README.adoc +18 -2
- data/examples/example2.json.gz +0 -0
- data/examples/example2.rb +14 -0
- data/examples/example3.json.gz +0 -0
- data/examples/example3.rb +14 -0
- data/examples/gc.json.gz +0 -0
- data/examples/gc.rb +13 -0
- data/ext/gvl_tracing_native_extension/gvl_tracing.c +52 -8
- data/lib/gvl_tracing/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1adc64642ec1cb371df66afa9f928cafe106349f981a3b8ec643232a0618b769
|
4
|
+
data.tar.gz: 9b250c7a07df1f9a1f907100554fa74eba043edb639ad1cc777b33ba705d98f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5016d9734e5eeef9578fc83211bb68cbc26f68f1b93b41c3ff0fbd9f5a3b93745cb7de23c69cc228a903b17db565ee369ec3d41f7d9575e3107a04a9bf6368a0
|
7
|
+
data.tar.gz: 821d89427465c6496e09a773a1277d35388d4205fd5e0e5f054fd8da8d1a46d902e4f464764a0e5891bb9ebb63451295ea2bf1986c085616b2ff60926dcbde7a
|
data/README.adoc
CHANGED
@@ -8,7 +8,7 @@ A Ruby gem for getting a timeline view of Global VM Lock usage in your Ruby app
|
|
8
8
|
|
9
9
|
image::preview.png[]
|
10
10
|
|
11
|
-
See my blog post
|
11
|
+
See my blog post https://ivoanjo.me/blog/2022/07/17/tracing-ruby-global-vm-lock/[tracing ruby's (global) vm lock] for more details!
|
12
12
|
|
13
13
|
NOTE: This gem only works on Ruby 3.2 and above because it depends on the new https://github.com/ruby/ruby/pull/5500[GVL Instrumentation API]. See below for an easy way to run Ruby 3.2 inside docker.
|
14
14
|
|
@@ -45,7 +45,23 @@ To do so:
|
|
45
45
|
|
46
46
|
== Quick start using docker
|
47
47
|
|
48
|
-
|
48
|
+
The `gvl-tracing` gem requires Ruby 3.2, which is still under development. If you have docker installed on your machine, you can use the https://hub.docker.com/r/rubylang/ruby[ruby-lang development images] to try it out.
|
49
|
+
|
50
|
+
Here's how you can use them:
|
51
|
+
|
52
|
+
[source,bash]
|
53
|
+
----
|
54
|
+
$ cd my_ruby_app/
|
55
|
+
$ docker run -v $(pwd):/app -it rubylang/ruby:master-focal
|
56
|
+
root@0e0b07edf906:/# cd app/
|
57
|
+
root@0e0b07edf906:/app# ruby -v
|
58
|
+
ruby 3.2.0dev (2022-07-23T12:42:05Z master 721d154e2f) [x86_64-linux]
|
59
|
+
root@0e0b07edf906:/app# gem install gvl-tracing
|
60
|
+
Building native extensions. This could take a while...
|
61
|
+
Successfully installed gvl-tracing-0.1.1
|
62
|
+
1 gem installed
|
63
|
+
root@0e0b07edf906:/app# ruby <your app>
|
64
|
+
----
|
49
65
|
|
50
66
|
== Installation
|
51
67
|
|
Binary file
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "gvl-tracing"
|
2
|
+
|
3
|
+
def fib(n)
|
4
|
+
return n if n <= 1
|
5
|
+
fib(n - 1) + fib(n - 2)
|
6
|
+
end
|
7
|
+
|
8
|
+
GvlTracing.start("example2.json")
|
9
|
+
|
10
|
+
other_thread = Thread.new { fib(37) } # runs in other thread
|
11
|
+
fib(37) # runs in main thread
|
12
|
+
|
13
|
+
other_thread.join
|
14
|
+
GvlTracing.stop
|
Binary file
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "gvl-tracing"
|
2
|
+
|
3
|
+
def fib(n)
|
4
|
+
return n if n <= 1
|
5
|
+
fib(n - 1) + fib(n - 2)
|
6
|
+
end
|
7
|
+
|
8
|
+
GvlTracing.start("example3.json")
|
9
|
+
|
10
|
+
other_ractor = Ractor.new { fib(37) } # runs in other ractor
|
11
|
+
fib(37) # runs in main thread
|
12
|
+
|
13
|
+
other_ractor.take
|
14
|
+
GvlTracing.stop
|
data/examples/gc.json.gz
ADDED
Binary file
|
data/examples/gc.rb
ADDED
@@ -24,31 +24,49 @@
|
|
24
24
|
// SOFTWARE.
|
25
25
|
|
26
26
|
#include <ruby/ruby.h>
|
27
|
+
#include <ruby/debug.h>
|
27
28
|
#include <ruby/thread.h>
|
29
|
+
#include <ruby/atomic.h>
|
28
30
|
#include <errno.h>
|
29
31
|
#include <stdbool.h>
|
30
32
|
#include <sys/types.h>
|
31
|
-
#include <unistd.h>
|
32
33
|
|
33
34
|
static VALUE tracing_start(VALUE _self, VALUE output_path);
|
34
35
|
static VALUE tracing_stop(VALUE _self);
|
35
36
|
static double timestamp_microseconds(void);
|
36
37
|
static void render_event(const char *event_name);
|
37
|
-
static void
|
38
|
+
static void on_thread_event(rb_event_flag_t event, const rb_internal_thread_event_data_t *_unused1, void *_unused2);
|
39
|
+
static void on_gc_event(VALUE tpval, void *_unused1);
|
40
|
+
|
41
|
+
// Thread-local state
|
42
|
+
static _Thread_local bool current_thread_serial_set = false;
|
43
|
+
static _Thread_local unsigned int current_thread_serial = 0;
|
38
44
|
|
39
45
|
// Global mutable state
|
46
|
+
static rb_atomic_t thread_serial = 0;
|
40
47
|
static FILE *output_file = NULL;
|
41
48
|
static rb_internal_thread_event_hook_t *current_hook = NULL;
|
42
49
|
static double started_tracing_at_microseconds = 0;
|
43
50
|
static pid_t process_id = 0;
|
51
|
+
static VALUE gc_tracepoint = Qnil;
|
44
52
|
|
45
53
|
void Init_gvl_tracing_native_extension(void) {
|
54
|
+
rb_global_variable(&gc_tracepoint);
|
55
|
+
|
46
56
|
VALUE gvl_tracing_module = rb_define_module("GvlTracing");
|
47
57
|
|
48
58
|
rb_define_singleton_method(gvl_tracing_module, "start", tracing_start, 1);
|
49
59
|
rb_define_singleton_method(gvl_tracing_module, "stop", tracing_stop, 0);
|
50
60
|
}
|
51
61
|
|
62
|
+
static inline unsigned int current_thread_id(void) {
|
63
|
+
if (!current_thread_serial_set) {
|
64
|
+
current_thread_serial_set = true;
|
65
|
+
current_thread_serial = RUBY_ATOMIC_FETCH_ADD(thread_serial, 1);
|
66
|
+
}
|
67
|
+
return (unsigned int)current_thread_serial;
|
68
|
+
}
|
69
|
+
|
52
70
|
static VALUE tracing_start(VALUE _self, VALUE output_path) {
|
53
71
|
Check_Type(output_path, T_STRING);
|
54
72
|
|
@@ -63,7 +81,7 @@ static VALUE tracing_start(VALUE _self, VALUE output_path) {
|
|
63
81
|
render_event("started_tracing");
|
64
82
|
|
65
83
|
current_hook = rb_internal_thread_add_event_hook(
|
66
|
-
|
84
|
+
on_thread_event,
|
67
85
|
(
|
68
86
|
RUBY_INTERNAL_THREAD_EVENT_READY |
|
69
87
|
RUBY_INTERNAL_THREAD_EVENT_RESUMED |
|
@@ -74,6 +92,17 @@ static VALUE tracing_start(VALUE _self, VALUE output_path) {
|
|
74
92
|
NULL
|
75
93
|
);
|
76
94
|
|
95
|
+
gc_tracepoint = rb_tracepoint_new(
|
96
|
+
0,
|
97
|
+
(
|
98
|
+
RUBY_INTERNAL_EVENT_GC_ENTER |
|
99
|
+
RUBY_INTERNAL_EVENT_GC_EXIT
|
100
|
+
),
|
101
|
+
on_gc_event,
|
102
|
+
(void *) NULL
|
103
|
+
);
|
104
|
+
rb_tracepoint_enable(gc_tracepoint);
|
105
|
+
|
77
106
|
return Qtrue;
|
78
107
|
}
|
79
108
|
|
@@ -81,6 +110,8 @@ static VALUE tracing_stop(VALUE _self) {
|
|
81
110
|
if (output_file == NULL) rb_raise(rb_eRuntimeError, "Tracing not running");
|
82
111
|
|
83
112
|
rb_internal_thread_remove_event_hook(current_hook);
|
113
|
+
rb_tracepoint_disable(gc_tracepoint);
|
114
|
+
gc_tracepoint = Qnil;
|
84
115
|
|
85
116
|
render_event("stopped_tracing");
|
86
117
|
fprintf(output_file, "]\n");
|
@@ -103,11 +134,14 @@ static double timestamp_microseconds(void) {
|
|
103
134
|
static void render_event(const char *event_name) {
|
104
135
|
// Event data
|
105
136
|
double now_microseconds = timestamp_microseconds() - started_tracing_at_microseconds;
|
106
|
-
|
137
|
+
unsigned int thread_id = current_thread_id();
|
107
138
|
|
108
139
|
// Each event is converted into two events in the output: one that signals the end of the previous event
|
109
140
|
// (whatever it was), and one that signals the start of the actual event we're processing.
|
110
|
-
// Yes this
|
141
|
+
// Yes, this seems to be slightly bending the intention of the output format, but it seemed easier to do this way.
|
142
|
+
|
143
|
+
// Important note: We've observed some rendering issues in perfetto if the tid or pid are numbers that are "too big",
|
144
|
+
// see https://github.com/ivoanjo/gvl-tracing/pull/4#issuecomment-1196463364 for an example.
|
111
145
|
|
112
146
|
fprintf(output_file,
|
113
147
|
// Finish previous duration
|
@@ -121,14 +155,24 @@ static void render_event(const char *event_name) {
|
|
121
155
|
);
|
122
156
|
}
|
123
157
|
|
124
|
-
static void
|
158
|
+
static void on_thread_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *_unused1, void *_unused2) {
|
125
159
|
const char* event_name = "bug_unknown_event";
|
126
160
|
switch (event_id) {
|
127
161
|
case RUBY_INTERNAL_THREAD_EVENT_READY: event_name = "ready"; break;
|
128
|
-
case RUBY_INTERNAL_THREAD_EVENT_RESUMED: event_name = "
|
129
|
-
case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED: event_name = "
|
162
|
+
case RUBY_INTERNAL_THREAD_EVENT_RESUMED: event_name = "running"; break;
|
163
|
+
case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED: event_name = "waiting"; break;
|
130
164
|
case RUBY_INTERNAL_THREAD_EVENT_STARTED: event_name = "started"; break;
|
131
165
|
case RUBY_INTERNAL_THREAD_EVENT_EXITED: event_name = "exited"; break;
|
132
166
|
};
|
133
167
|
render_event(event_name);
|
134
168
|
}
|
169
|
+
|
170
|
+
static void on_gc_event(VALUE tpval, void *_unused1) {
|
171
|
+
const char* event_name = "bug_unknown_event";
|
172
|
+
switch (rb_tracearg_event_flag(rb_tracearg_from_tracepoint(tpval))) {
|
173
|
+
case RUBY_INTERNAL_EVENT_GC_ENTER: event_name = "gc"; break;
|
174
|
+
// TODO: is it possible the thread wasn't running? Might need to save the last state.
|
175
|
+
case RUBY_INTERNAL_EVENT_GC_EXIT: event_name = "running"; break;
|
176
|
+
}
|
177
|
+
render_event(event_name);
|
178
|
+
}
|
data/lib/gvl_tracing/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gvl-tracing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivo Anjo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-07-
|
11
|
+
date: 2022-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -26,6 +26,12 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- examples/example1.json.gz
|
28
28
|
- examples/example1.rb
|
29
|
+
- examples/example2.json.gz
|
30
|
+
- examples/example2.rb
|
31
|
+
- examples/example3.json.gz
|
32
|
+
- examples/example3.rb
|
33
|
+
- examples/gc.json.gz
|
34
|
+
- examples/gc.rb
|
29
35
|
- ext/gvl_tracing_native_extension/extconf.rb
|
30
36
|
- ext/gvl_tracing_native_extension/gvl_tracing.c
|
31
37
|
- gems.rb
|