gvl-tracing 1.2.0 → 1.4.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 +7 -9
- data/ext/gvl_tracing_native_extension/gvl_tracing.c +26 -0
- data/lib/gvl-tracing.rb +8 -0
- data/lib/gvl_tracing/sleep_tracking.rb +12 -0
- data/lib/gvl_tracing/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4945e5419655b2e69f310845e6214ff0bd6eb66ae820383f9cbe97855f15cdb
|
4
|
+
data.tar.gz: dd95a9431c53c3904b054227a305da7433a789d8828b0340abc2b48864361735
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eca1b710f5918e887f06c44dfa970100e77c99bbc63318321a72e76dfffc4b44022b055dae4de05992494fc40910a467f4aa3ed045ab891018303a15fe8169e1
|
7
|
+
data.tar.gz: 59deb53b041211513979526345af62c3e94495e73deae38d1f1e7272a11469695ae5c2435331bcf811f1c943c18f9602c4e3199c97fef9963cb3e3e4cd25f5cd
|
data/README.adoc
CHANGED
@@ -25,17 +25,15 @@ def fib(n)
|
|
25
25
|
fib(n - 1) + fib(n - 2)
|
26
26
|
end
|
27
27
|
|
28
|
-
GvlTracing.start("example1.json")
|
28
|
+
GvlTracing.start("example1.json") do
|
29
|
+
Thread.new { sleep(0.05) while true }
|
29
30
|
|
30
|
-
|
31
|
+
sleep(0.05)
|
31
32
|
|
32
|
-
|
33
|
+
3.times.map { Thread.new { fib(37) } }.map(&:join)
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
sleep(0.05)
|
37
|
-
|
38
|
-
GvlTracing.stop
|
35
|
+
sleep(0.05)
|
36
|
+
end
|
39
37
|
----
|
40
38
|
|
41
39
|
To do so:
|
@@ -65,7 +63,7 @@ Use `require "gvl-tracing"` to load the gem.
|
|
65
63
|
|
66
64
|
This gem only provides a single module (`GvlTracing`) with methods:
|
67
65
|
|
68
|
-
* `start(filename)`: Starts tracing, writing the results to the provided filename
|
66
|
+
* `start(filename, &block)`: Starts tracing, writing the results to the provided filename. When a block is passed, yields the block and calls stop.
|
69
67
|
* `stop`: Stops tracing
|
70
68
|
|
71
69
|
The resulting traces can be analyzed by going to https://ui.perfetto.dev/[Perfetto UI].
|
@@ -56,11 +56,14 @@ static void set_native_thread_id(void);
|
|
56
56
|
static void render_event(const char *event_name);
|
57
57
|
static void on_thread_event(rb_event_flag_t event, const rb_internal_thread_event_data_t *_unused1, void *_unused2);
|
58
58
|
static void on_gc_event(VALUE tpval, void *_unused1);
|
59
|
+
static VALUE mark_sleeping(VALUE _self);
|
59
60
|
|
60
61
|
// Thread-local state
|
61
62
|
static _Thread_local bool current_thread_seen = false;
|
62
63
|
static _Thread_local unsigned int current_thread_serial = 0;
|
63
64
|
static _Thread_local uint64_t thread_id = 0;
|
65
|
+
static _Thread_local rb_event_flag_t previous_state = 0; // Used to coalesce similar events
|
66
|
+
static _Thread_local bool sleeping = false; // Used to track when a thread is sleeping
|
64
67
|
|
65
68
|
// Global mutable state
|
66
69
|
static rb_atomic_t thread_serial = 0;
|
@@ -77,6 +80,7 @@ void Init_gvl_tracing_native_extension(void) {
|
|
77
80
|
|
78
81
|
rb_define_singleton_method(gvl_tracing_module, "_start", tracing_start, 1);
|
79
82
|
rb_define_singleton_method(gvl_tracing_module, "_stop", tracing_stop, 0);
|
83
|
+
rb_define_singleton_method(gvl_tracing_module, "mark_sleeping", mark_sleeping, 0);
|
80
84
|
}
|
81
85
|
|
82
86
|
static inline void initialize_thread_id(void) {
|
@@ -204,6 +208,23 @@ static void render_event(const char *event_name) {
|
|
204
208
|
}
|
205
209
|
|
206
210
|
static void on_thread_event(rb_event_flag_t event_id, UNUSED_ARG const rb_internal_thread_event_data_t *_unused1, UNUSED_ARG void *_unused2) {
|
211
|
+
// In some cases, Ruby seems to even multiple suspended events for the same thread in a row (e.g. when multiple threads)
|
212
|
+
// are waiting on a Thread::ConditionVariable.new that gets signaled. We coalesce these events to make the resulting
|
213
|
+
// timeline easier to see.
|
214
|
+
//
|
215
|
+
// I haven't observed other situations where we'd want to coalesce events, but we may apply this to all events in the
|
216
|
+
// future. One annoying thing to remember when generalizing this is how to reset the `previous_state` across multiple
|
217
|
+
// start/stop calls to GvlTracing.
|
218
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED && event_id == previous_state) return;
|
219
|
+
previous_state = event_id;
|
220
|
+
|
221
|
+
if (event_id == RUBY_INTERNAL_THREAD_EVENT_SUSPENDED && sleeping) {
|
222
|
+
render_event("sleeping");
|
223
|
+
return;
|
224
|
+
} else {
|
225
|
+
sleeping = false;
|
226
|
+
}
|
227
|
+
|
207
228
|
const char* event_name = "bug_unknown_event";
|
208
229
|
switch (event_id) {
|
209
230
|
case RUBY_INTERNAL_THREAD_EVENT_READY: event_name = "wants_gvl"; break;
|
@@ -224,3 +245,8 @@ static void on_gc_event(VALUE tpval, UNUSED_ARG void *_unused1) {
|
|
224
245
|
}
|
225
246
|
render_event(event_name);
|
226
247
|
}
|
248
|
+
|
249
|
+
static VALUE mark_sleeping(VALUE _self) {
|
250
|
+
sleeping = true;
|
251
|
+
return Qnil;
|
252
|
+
}
|
data/lib/gvl-tracing.rb
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Experimental: This monkey patch when loaded introduces a new state -- "sleeping" -- which is more specific than the
|
2
|
+
# regular "waiting". This can be useful to distinguish when waiting is happening based on time, vs for some event to
|
3
|
+
# happen.
|
4
|
+
|
5
|
+
module GvlTracing::SleepTracking
|
6
|
+
def sleep(...)
|
7
|
+
GvlTracing.mark_sleeping
|
8
|
+
super(...)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
include GvlTracing::SleepTracking
|
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: 1.
|
4
|
+
version: 1.4.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: 2023-
|
11
|
+
date: 2023-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -30,6 +30,7 @@ files:
|
|
30
30
|
- gems.rb
|
31
31
|
- gvl-tracing.gemspec
|
32
32
|
- lib/gvl-tracing.rb
|
33
|
+
- lib/gvl_tracing/sleep_tracking.rb
|
33
34
|
- lib/gvl_tracing/version.rb
|
34
35
|
- preview.png
|
35
36
|
homepage: https://github.com/ivoanjo/gvl-tracing
|
@@ -52,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
53
|
- !ruby/object:Gem::Version
|
53
54
|
version: '0'
|
54
55
|
requirements: []
|
55
|
-
rubygems_version: 3.4.
|
56
|
+
rubygems_version: 3.4.1
|
56
57
|
signing_key:
|
57
58
|
specification_version: 4
|
58
59
|
summary: Get a timeline view of Global VM Lock usage in your Ruby app
|