gvl-tracing 0.2.0 → 1.1.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: 1adc64642ec1cb371df66afa9f928cafe106349f981a3b8ec643232a0618b769
4
- data.tar.gz: 9b250c7a07df1f9a1f907100554fa74eba043edb639ad1cc777b33ba705d98f3
3
+ metadata.gz: 1eedec6bc1503bdc8fabde77ac69167de69a9fe1cc8c211720ce7fa34d5fd66a
4
+ data.tar.gz: 6e504dfe9f06f97b3247e00cf5cbc0fa17ffec39136474dfc5e9ff9914a10351
5
5
  SHA512:
6
- metadata.gz: 5016d9734e5eeef9578fc83211bb68cbc26f68f1b93b41c3ff0fbd9f5a3b93745cb7de23c69cc228a903b17db565ee369ec3d41f7d9575e3107a04a9bf6368a0
7
- data.tar.gz: 821d89427465c6496e09a773a1277d35388d4205fd5e0e5f054fd8da8d1a46d902e4f464764a0e5891bb9ebb63451295ea2bf1986c085616b2ff60926dcbde7a
6
+ metadata.gz: 34fcfe11c2c1dca8307442e026420dea3a630b19c652164c9095143414226227e0198ae69b44f6e53ca3cf5325cd832e8a5450ce824ba7c5a99f37cb9e604300
7
+ data.tar.gz: 01dc10ae34c5398889d531313f3faceda12bbe37890193cfd64560cd99f352149e3afea3d6ebb553e0d3116aa202bb34b9c7d403cbc0c368e272ed7053e6daad
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.2.0
data/README.adoc CHANGED
@@ -10,7 +10,7 @@ image::preview.png[]
10
10
 
11
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
- 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.
13
+ NOTE: This gem only works on Ruby 3.2 and above because it depends on the https://github.com/ruby/ruby/pull/5500[GVL Instrumentation API].
14
14
 
15
15
  == Quickest start
16
16
 
@@ -43,26 +43,6 @@ To do so:
43
43
  1. Download link:https://github.com/ivoanjo/gvl-tracing/blob/master/examples/example1.json.gz?raw=true[`examples/example1.json.gz`]
44
44
  2. Navigate to https://ui.perfetto.dev/ and use the **Open trace file** option to load the file
45
45
 
46
- == Quick start using docker
47
-
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
- ----
65
-
66
46
  == Installation
67
47
 
68
48
  Install the gem and add to the application's `Gemfile` or `gems.rb` file by executing:
@@ -32,4 +32,10 @@ end
32
32
 
33
33
  require "mkmf"
34
34
 
35
+ have_func("gettid", "unistd.h")
36
+ have_header("pthread.h")
37
+ have_func("pthread_getname_np", "pthread.h")
38
+ have_func("pthread_threadid_np", "pthread.h")
39
+
40
+ create_header
35
41
  create_makefile "gvl_tracing_native_extension"
@@ -31,6 +31,16 @@
31
31
  #include <stdbool.h>
32
32
  #include <sys/types.h>
33
33
 
34
+ #include "extconf.h"
35
+
36
+ #ifdef HAVE_PTHREAD_H
37
+ #include <pthread.h>
38
+ #endif
39
+
40
+ #ifdef HAVE_GETTID
41
+ #include <unistd.h>
42
+ #endif
43
+
34
44
  static VALUE tracing_start(VALUE _self, VALUE output_path);
35
45
  static VALUE tracing_stop(VALUE _self);
36
46
  static double timestamp_microseconds(void);
@@ -59,12 +69,31 @@ void Init_gvl_tracing_native_extension(void) {
59
69
  rb_define_singleton_method(gvl_tracing_module, "stop", tracing_stop, 0);
60
70
  }
61
71
 
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;
72
+ static inline void initialize_thread_id(void) {
73
+ current_thread_serial_set = true;
74
+ current_thread_serial = RUBY_ATOMIC_FETCH_ADD(thread_serial, 1);
75
+ }
76
+
77
+ static inline void render_thread_metadata(void) {
78
+ uint64_t native_thread_id = 0;
79
+ #ifdef HAVE_GETTID
80
+ native_thread_id = gettid();
81
+ #elif HAVE_PTHREAD_THREADID_NP
82
+ pthread_threadid_np(pthread_self(), &native_thread_id);
83
+ #else
84
+ native_thread_id = current_thread_serial; // TODO: Better fallback for Windows?
85
+ #endif
86
+
87
+ char native_thread_name_buffer[64] = "(unnamed)";
88
+
89
+ #ifdef HAVE_PTHREAD_GETNAME_NP
90
+ pthread_getname_np(pthread_self(), native_thread_name_buffer, sizeof(native_thread_name_buffer));
91
+ #endif
92
+
93
+ fprintf(output_file,
94
+ " {\"ph\": \"M\", \"pid\": %u, \"tid\": %u, \"name\": \"thread_name\", \"args\": {\"name\": \"%lu %s\"}},\n",
95
+ process_id, current_thread_serial, native_thread_id, native_thread_name_buffer
96
+ );
68
97
  }
69
98
 
70
99
  static VALUE tracing_start(VALUE _self, VALUE output_path) {
@@ -134,7 +163,13 @@ static double timestamp_microseconds(void) {
134
163
  static void render_event(const char *event_name) {
135
164
  // Event data
136
165
  double now_microseconds = timestamp_microseconds() - started_tracing_at_microseconds;
137
- unsigned int thread_id = current_thread_id();
166
+
167
+ if (!current_thread_serial_set) {
168
+ initialize_thread_id();
169
+ render_thread_metadata();
170
+ }
171
+
172
+ unsigned int thread_id = current_thread_serial;
138
173
 
139
174
  // Each event is converted into two events in the output: one that signals the end of the previous event
140
175
  // (whatever it was), and one that signals the start of the actual event we're processing.
@@ -158,11 +193,11 @@ static void render_event(const char *event_name) {
158
193
  static void on_thread_event(rb_event_flag_t event_id, const rb_internal_thread_event_data_t *_unused1, void *_unused2) {
159
194
  const char* event_name = "bug_unknown_event";
160
195
  switch (event_id) {
161
- case RUBY_INTERNAL_THREAD_EVENT_READY: event_name = "ready"; break;
196
+ case RUBY_INTERNAL_THREAD_EVENT_READY: event_name = "wants_gvl"; break;
162
197
  case RUBY_INTERNAL_THREAD_EVENT_RESUMED: event_name = "running"; break;
163
198
  case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED: event_name = "waiting"; break;
164
199
  case RUBY_INTERNAL_THREAD_EVENT_STARTED: event_name = "started"; break;
165
- case RUBY_INTERNAL_THREAD_EVENT_EXITED: event_name = "exited"; break;
200
+ case RUBY_INTERNAL_THREAD_EVENT_EXITED: event_name = "died"; break;
166
201
  };
167
202
  render_event(event_name);
168
203
  }
data/gvl-tracing.gemspec CHANGED
@@ -36,13 +36,13 @@ Gem::Specification.new do |spec|
36
36
  spec.summary = "Get a timeline view of Global VM Lock usage in your Ruby app"
37
37
  spec.homepage = "https://github.com/ivoanjo/gvl-tracing"
38
38
  spec.license = "MIT"
39
- spec.required_ruby_version = ">= 3.2.0.dev"
39
+ spec.required_ruby_version = ">= 3.2.0"
40
40
 
41
41
  # Specify which files should be added to the gem when it is released.
42
42
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
43
43
  spec.files = Dir.chdir(__dir__) do
44
44
  `git ls-files -z`.split("\x0").reject do |f|
45
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
45
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features|examples)/|\.(?:git|travis|circleci)|appveyor)})
46
46
  end
47
47
  end
48
48
  spec.require_paths = ["lib", "ext"]
@@ -26,5 +26,5 @@
26
26
  # frozen_string_literal: true
27
27
 
28
28
  module GvlTracing
29
- VERSION = "0.2.0"
29
+ VERSION = "1.1.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: 0.2.0
4
+ version: 1.1.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-27 00:00:00.000000000 Z
11
+ date: 2023-02-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -19,19 +19,12 @@ extensions:
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - ".editorconfig"
22
+ - ".ruby-version"
22
23
  - ".standard.yml"
23
24
  - CODE_OF_CONDUCT.adoc
24
25
  - LICENSE
25
26
  - README.adoc
26
27
  - Rakefile
27
- - examples/example1.json.gz
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
35
28
  - ext/gvl_tracing_native_extension/extconf.rb
36
29
  - ext/gvl_tracing_native_extension/gvl_tracing.c
37
30
  - gems.rb
@@ -52,14 +45,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
52
45
  requirements:
53
46
  - - ">="
54
47
  - !ruby/object:Gem::Version
55
- version: 3.2.0.dev
48
+ version: 3.2.0
56
49
  required_rubygems_version: !ruby/object:Gem::Requirement
57
50
  requirements:
58
51
  - - ">="
59
52
  - !ruby/object:Gem::Version
60
53
  version: '0'
61
54
  requirements: []
62
- rubygems_version: 3.4.0.dev
55
+ rubygems_version: 3.4.1
63
56
  signing_key:
64
57
  specification_version: 4
65
58
  summary: Get a timeline view of Global VM Lock usage in your Ruby app
Binary file
data/examples/example1.rb DELETED
@@ -1,18 +0,0 @@
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("example1.json")
9
-
10
- Thread.new { sleep(0.05) while true }
11
-
12
- sleep(0.05)
13
-
14
- 3.times.map { Thread.new { fib(37) } }.map(&:join)
15
-
16
- sleep(0.05)
17
-
18
- GvlTracing.stop
Binary file
data/examples/example2.rb DELETED
@@ -1,14 +0,0 @@
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
data/examples/example3.rb DELETED
@@ -1,14 +0,0 @@
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 DELETED
Binary file
data/examples/gc.rb DELETED
@@ -1,13 +0,0 @@
1
- require "gvl-tracing"
2
-
3
- def alloc(n)
4
- n.times { Object.new }
5
- end
6
-
7
- GvlTracing.start("gc.json")
8
-
9
- 3.times.map { Thread.new { alloc(100_000) } }.map(&:join)
10
-
11
- sleep(0.05)
12
-
13
- GvlTracing.stop