gvl-tracing 0.2.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/README.adoc +1 -21
- data/ext/gvl_tracing_native_extension/extconf.rb +6 -0
- data/ext/gvl_tracing_native_extension/gvl_tracing.c +44 -9
- data/gvl-tracing.gemspec +2 -2
- data/lib/gvl_tracing/version.rb +1 -1
- metadata +5 -12
- data/examples/example1.json.gz +0 -0
- data/examples/example1.rb +0 -18
- data/examples/example2.json.gz +0 -0
- data/examples/example2.rb +0 -14
- data/examples/example3.json.gz +0 -0
- data/examples/example3.rb +0 -14
- data/examples/gc.json.gz +0 -0
- data/examples/gc.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1eedec6bc1503bdc8fabde77ac69167de69a9fe1cc8c211720ce7fa34d5fd66a
|
4
|
+
data.tar.gz: 6e504dfe9f06f97b3247e00cf5cbc0fa17ffec39136474dfc5e9ff9914a10351
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
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 = "
|
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 = "
|
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
|
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"]
|
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:
|
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:
|
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
|
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.
|
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
|
data/examples/example1.json.gz
DELETED
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
|
data/examples/example2.json.gz
DELETED
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
|
data/examples/example3.json.gz
DELETED
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
|