gvl-tracing 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|