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 +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
|