gvl-tracing 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 643b4b237db640d34ecf842459246640517ca9d58118ca891c76a629f1c6ab73
4
- data.tar.gz: 10c2613b6b66681b46f7d32a143e5742ed98b969d9aca00e3ceb6b22c5579f4b
3
+ metadata.gz: d4945e5419655b2e69f310845e6214ff0bd6eb66ae820383f9cbe97855f15cdb
4
+ data.tar.gz: dd95a9431c53c3904b054227a305da7433a789d8828b0340abc2b48864361735
5
5
  SHA512:
6
- metadata.gz: f3265c1719bd2dd7859f4df78a4b9096277b45f26b9a6bd515c5a3222f3bdb98e56461eef274f5987a61cfb7ec331ac44dac58a94fd8dadf7474a51b9033f3d6
7
- data.tar.gz: 40a336509af944962fc2c48ae2f40c2ae40ff133b53b7e006f63f1a453e4106d68f6283e008462088f39afd3c5653556ef064fb6bf312aeffc09cd282ba90ba9
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
- Thread.new { sleep(0.05) while true }
31
+ sleep(0.05)
31
32
 
32
- sleep(0.05)
33
+ 3.times.map { Thread.new { fib(37) } }.map(&:join)
33
34
 
34
- 3.times.map { Thread.new { fib(37) } }.map(&:join)
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
@@ -37,6 +37,14 @@ module GvlTracing
37
37
  def start(file)
38
38
  _start(file)
39
39
  @path = file
40
+
41
+ return unless block_given?
42
+
43
+ begin
44
+ yield
45
+ ensure
46
+ _stop
47
+ end
40
48
  end
41
49
 
42
50
  def stop
@@ -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
@@ -26,5 +26,5 @@
26
26
  # frozen_string_literal: true
27
27
 
28
28
  module GvlTracing
29
- VERSION = "1.2.0"
29
+ VERSION = "1.4.0"
30
30
  end
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.2.0
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-06-06 00:00:00.000000000 Z
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.6
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