rbtrace 0.4.14 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +26 -0
- data/CHANGELOG +7 -0
- data/ext/extconf.rb +0 -5
- data/ext/rbtrace.c +12 -20
- data/lib/rbtrace/cli.rb +44 -3
- data/lib/rbtrace/core_ext.rb +0 -4
- data/lib/rbtrace/msgq.rb +2 -1
- data/lib/rbtrace/rbtracer.rb +3 -1
- data/lib/rbtrace/version.rb +1 -1
- data/lib/rbtrace.rb +22 -0
- data/rbtrace.gemspec +1 -1
- data/server.rb +1 -1
- data/test.sh +6 -4
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc6fc7320951955050f41babf4a1effbec1eaac42e9103a0c8b08c69cc2ad090
|
4
|
+
data.tar.gz: 105f410c0c78e6f23c3d6617165b7a1c3c9a72976735cfd185481c91acb3bb17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4e42ec27236d32067f52fa3c0ed1a1a3eb518f1f0d4de9c42dcbec787dc62346f39c790ae2b74fcca6835bc1413c3e44216398c8ca46d74d760f0d75a551873
|
7
|
+
data.tar.gz: 4602057b9797b58337d2308a195ee1d1e6e9247a7e08bebf40d153bd9c6f4fb1f4871851b514b0fef471cf87df002632b0c75ff6511dc926e58c86be453d8b67
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
name: Test
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
ruby:
|
7
|
+
name: Ruby ${{ matrix.ruby }}
|
8
|
+
timeout-minutes: 15
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
os: ["ubuntu-latest"]
|
13
|
+
ruby: ["3.2", "3.1", "3.0", "2.7", "2.6"]
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
steps:
|
16
|
+
- name: Check out code
|
17
|
+
uses: actions/checkout@v3
|
18
|
+
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby }}
|
23
|
+
bundler-cache: true
|
24
|
+
|
25
|
+
- name: Tests
|
26
|
+
run: ./test.sh
|
data/CHANGELOG
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
- 0.5.0 - 02-12-2023
|
2
|
+
|
3
|
+
- Improved --backtraces implementation
|
4
|
+
- Added --shapedump which is useful for lighter weight object shape dumps on Ruby 3.2 and up
|
5
|
+
- Move heap dumping to a fork for better performance
|
6
|
+
- Use TypedData API which has been supported for a very long time
|
7
|
+
- Update CI and fix some small errors
|
data/ext/extconf.rb
CHANGED
@@ -63,11 +63,6 @@ have_func('rb_during_gc', 'ruby.h')
|
|
63
63
|
have_func('rb_gc_add_event_hook', ['ruby.h', 'node.h'])
|
64
64
|
have_func('rb_postponed_job_register_one', 'ruby.h')
|
65
65
|
|
66
|
-
# increase message size on linux
|
67
|
-
if RUBY_PLATFORM =~ /linux/
|
68
|
-
$defs.push("-DBUF_SIZE=256")
|
69
|
-
end
|
70
|
-
|
71
66
|
# warnings save lives
|
72
67
|
$CFLAGS << " -Wall "
|
73
68
|
|
data/ext/rbtrace.c
CHANGED
@@ -79,7 +79,7 @@ timeofday_usec()
|
|
79
79
|
#define MAX_TRACERS 100 // max method tracers
|
80
80
|
#define MAX_EXPRS 10 // max expressions per tracer
|
81
81
|
#ifndef BUF_SIZE // msgq buffer size
|
82
|
-
#define BUF_SIZE
|
82
|
+
#define BUF_SIZE 1024
|
83
83
|
#endif
|
84
84
|
|
85
85
|
typedef struct {
|
@@ -876,9 +876,7 @@ eval_inspect(VALUE rb_code) {
|
|
876
876
|
}
|
877
877
|
|
878
878
|
static VALUE
|
879
|
-
rescue_inspect(VALUE arg) {
|
880
|
-
VALUE exception = rb_errinfo(); /* get last exception */
|
881
|
-
rb_set_errinfo(Qnil);
|
879
|
+
rescue_inspect(VALUE arg, VALUE exception) {
|
882
880
|
return rb_funcall(exception, rb_intern("inspect"), 0);
|
883
881
|
}
|
884
882
|
|
@@ -891,7 +889,6 @@ rbtrace__process_event(msgpack_object cmd)
|
|
891
889
|
static int last_tracer_id = -1; // hax
|
892
890
|
char query[BUF_SIZE];
|
893
891
|
|
894
|
-
char code[BUF_SIZE+150];
|
895
892
|
VALUE val = Qnil;
|
896
893
|
|
897
894
|
msgpack_object_array ary;
|
@@ -1135,31 +1132,26 @@ send_write(VALUE klass, VALUE val) {
|
|
1135
1132
|
return Qnil;
|
1136
1133
|
}
|
1137
1134
|
|
1135
|
+
static const rb_data_type_t rbtrace_type = {
|
1136
|
+
"RBTrace",
|
1137
|
+
{
|
1138
|
+
rbtrace_gc_mark,
|
1139
|
+
}
|
1140
|
+
};
|
1141
|
+
|
1138
1142
|
void
|
1139
1143
|
Init_rbtrace()
|
1140
1144
|
{
|
1141
1145
|
rbtrace_module = rb_define_module("RBTrace");
|
1142
1146
|
VALUE output = rb_define_module_under(rbtrace_module, "OUT");
|
1143
1147
|
|
1144
|
-
|
1148
|
+
rb_const_set(rbtrace_module, rb_intern("BUF_SIZE"), INT2NUM(BUF_SIZE));
|
1145
1149
|
|
1146
|
-
|
1147
|
-
"module RBTrace\n"
|
1148
|
-
" def self.eval_context\n"
|
1149
|
-
" @eval_context ||= binding\n"
|
1150
|
-
" end\n"
|
1151
|
-
|
1152
|
-
" def self.eval_and_inspect(code)\n"
|
1153
|
-
" t = Thread.new { Thread.current[:output] = eval_context.eval(code).inspect }\n"
|
1154
|
-
" t.join\n"
|
1155
|
-
" t[:output]\n"
|
1156
|
-
" end\n"
|
1157
|
-
"end\n"
|
1158
|
-
);
|
1150
|
+
rb_define_singleton_method(output, "write", send_write, 1);
|
1159
1151
|
|
1160
1152
|
// hook into the gc
|
1161
|
-
gc_hook = Data_Wrap_Struct(rb_cObject, rbtrace_gc_mark, NULL, NULL);
|
1162
1153
|
rb_global_variable(&gc_hook);
|
1154
|
+
gc_hook = TypedData_Wrap_Struct(rb_cObject, &rbtrace_type, NULL);
|
1163
1155
|
|
1164
1156
|
// catch signal telling us to read from the msgq
|
1165
1157
|
#if defined(HAVE_RB_POSTPONED_JOB_REGISTER_ONE)
|
data/lib/rbtrace/cli.rb
CHANGED
@@ -225,6 +225,9 @@ EOS
|
|
225
225
|
:default => "AUTO",
|
226
226
|
:short => "-h"
|
227
227
|
|
228
|
+
opt :shapesdump,
|
229
|
+
"generate a shapes dump for the process in FILENAME",
|
230
|
+
:default => "AUTO"
|
228
231
|
end
|
229
232
|
|
230
233
|
opts = Optimist.with_standard_exception_handling(parser) do
|
@@ -241,7 +244,7 @@ EOS
|
|
241
244
|
end
|
242
245
|
|
243
246
|
unless %w[ fork eval interactive backtrace backtraces slow slowcpu firehose methods config gc memory heapdump].find{ |n| opts[:"#{n}_given"] }
|
244
|
-
$stderr.puts "Error: --slow, --slowcpu, --gc, --firehose, --methods, --interactive, --backtraces, --backtrace, --memory, --heapdump or --config required."
|
247
|
+
$stderr.puts "Error: --slow, --slowcpu, --gc, --firehose, --methods, --interactive, --backtraces, --backtrace, --memory, --heapdump, --shapesdump or --config required."
|
245
248
|
$stderr.puts "Try --help for help."
|
246
249
|
exit(-1)
|
247
250
|
end
|
@@ -449,7 +452,7 @@ EOS
|
|
449
452
|
|
450
453
|
delim = "146621c9d681409aa"
|
451
454
|
|
452
|
-
code = "Thread.list.map{|t| t.backtrace[0...#{num}].join(\"#{delim}\")}.join(\"#{delim*2}\")"
|
455
|
+
code = "Thread.list.reject { |t| t.name == '__RBTrace__' }.map{ |t| t.backtrace[0...#{num}].join(\"#{delim}\")}.join(\"#{delim*2}\")"
|
453
456
|
|
454
457
|
if res = tracer.eval(code)
|
455
458
|
tracer.puts res.split(delim).join("\n")
|
@@ -499,9 +502,47 @@ EOS
|
|
499
502
|
temp.unlink
|
500
503
|
end
|
501
504
|
|
502
|
-
tracer.eval(
|
505
|
+
tracer.eval(<<-RUBY)
|
506
|
+
Thread.new do
|
507
|
+
Thread.current.name = '__RBTrace__'
|
508
|
+
pid = ::Process.fork do
|
509
|
+
file = File.open('#{filename}.tmp', 'w')
|
510
|
+
ObjectSpace.dump_all(output: file)
|
511
|
+
file.close
|
512
|
+
File.rename('#{filename}.tmp', '#{filename}')
|
513
|
+
exit!(0)
|
514
|
+
end
|
515
|
+
Process.waitpid(pid)
|
516
|
+
end
|
517
|
+
RUBY
|
503
518
|
puts "Heapdump being written to #{filename}"
|
504
519
|
|
520
|
+
elsif opts[:shapesdump_given]
|
521
|
+
filename = opts[:shapesdump]
|
522
|
+
|
523
|
+
if filename == "AUTO"
|
524
|
+
require 'tempfile'
|
525
|
+
temp = Tempfile.new("dump")
|
526
|
+
filename = temp.path
|
527
|
+
temp.close
|
528
|
+
temp.unlink
|
529
|
+
end
|
530
|
+
|
531
|
+
tracer.eval(<<-RUBY)
|
532
|
+
Thread.new do
|
533
|
+
Thread.current.name = '__RBTrace__'
|
534
|
+
pid = ::Process.fork do
|
535
|
+
file = File.open('#{filename}.tmp', 'w')
|
536
|
+
ObjectSpace.dump_shapes(output: file)
|
537
|
+
file.close
|
538
|
+
File.rename('#{filename}.tmp', '#{filename}')
|
539
|
+
exit!(0)
|
540
|
+
end
|
541
|
+
Process.waitpid(pid)
|
542
|
+
end
|
543
|
+
RUBY
|
544
|
+
puts "Shapes dump being written to #{filename}"
|
545
|
+
|
505
546
|
elsif opts[:eval_given]
|
506
547
|
if res = tracer.eval(code = opts[:eval])
|
507
548
|
tracer.puts ">> #{code}"
|
data/lib/rbtrace/core_ext.rb
CHANGED
data/lib/rbtrace/msgq.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'rbtrace'
|
1
2
|
require 'ffi'
|
2
3
|
|
3
4
|
module MsgQ
|
@@ -5,7 +6,7 @@ module MsgQ
|
|
5
6
|
ffi_lib FFI::CURRENT_PROCESS
|
6
7
|
|
7
8
|
class EventMsg < FFI::Struct
|
8
|
-
BUF_SIZE =
|
9
|
+
BUF_SIZE = RBTrace::BUF_SIZE
|
9
10
|
IPC_NOWAIT = 004000
|
10
11
|
|
11
12
|
layout :mtype, :long,
|
data/lib/rbtrace/rbtracer.rb
CHANGED
@@ -323,7 +323,9 @@ class RBTracer
|
|
323
323
|
msg = cmd.to_msgpack
|
324
324
|
# A message is null-terminated, but bytesize gives the unterminated
|
325
325
|
# length.
|
326
|
-
|
326
|
+
if msg.bytesize >= RbTrace::BUF_SIZE
|
327
|
+
raise ArgumentError, "command is too long (#{msg.bytesize}B >= #{MsgQ::EventMsg::BUF_SIZE}B)"
|
328
|
+
end
|
327
329
|
MsgQ::EventMsg.send_cmd(@qo, msg)
|
328
330
|
rescue Errno::EINTR
|
329
331
|
retry
|
data/lib/rbtrace/version.rb
CHANGED
data/lib/rbtrace.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RBTrace
|
4
|
+
class << self
|
5
|
+
def eval_and_inspect(code)
|
6
|
+
t = Thread.new do
|
7
|
+
Thread.current.name = '__RBTrace__'
|
8
|
+
Thread.current[:output] = eval_context.eval(code).inspect
|
9
|
+
end
|
10
|
+
t.join
|
11
|
+
t[:output]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def eval_context
|
17
|
+
@eval_context ||= binding
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rbtrace.so'
|
data/rbtrace.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency 'optimist', '>= 3.0.0'
|
24
24
|
s.add_dependency 'msgpack', '>= 0.4.3'
|
25
25
|
|
26
|
-
s.add_development_dependency "rake"
|
26
|
+
s.add_development_dependency "rake"
|
27
27
|
|
28
28
|
s.license = "MIT"
|
29
29
|
s.summary = 'rbtrace: like strace but for ruby code'
|
data/server.rb
CHANGED
data/test.sh
CHANGED
@@ -12,7 +12,7 @@ cd ..
|
|
12
12
|
bundle check
|
13
13
|
export RUBYOPT="-I.:lib"
|
14
14
|
|
15
|
-
ruby server.rb &
|
15
|
+
bundle exec ruby server.rb &
|
16
16
|
export PID=$!
|
17
17
|
|
18
18
|
trap cleanup INT TERM
|
@@ -23,11 +23,11 @@ cleanup() {
|
|
23
23
|
|
24
24
|
trace() {
|
25
25
|
echo ------------------------------------------
|
26
|
-
echo ./bin/rbtrace -p $PID
|
26
|
+
echo ./bin/rbtrace -p $PID "$@"
|
27
27
|
echo ------------------------------------------
|
28
|
-
./bin/rbtrace -p $PID
|
28
|
+
bundle exec ./bin/rbtrace -p $PID "$@" &
|
29
29
|
sleep 2
|
30
|
-
kill $!
|
30
|
+
kill $! || true
|
31
31
|
wait $! || true
|
32
32
|
echo
|
33
33
|
}
|
@@ -37,6 +37,8 @@ trace -m sleep
|
|
37
37
|
trace -m sleep Dir.chdir Dir.pwd Process.pid "String#gsub" "String#*"
|
38
38
|
trace -m "Kernel#"
|
39
39
|
trace -m "String#gsub(self,@test)" "String#*(self,__source__)" "String#multiply_vowels(self,self.length,num)"
|
40
|
+
trace -e 'p(1 + 1)'
|
41
|
+
trace -h
|
40
42
|
trace --gc --slow=200
|
41
43
|
trace --gc -m Dir.
|
42
44
|
trace --slow=250
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbtrace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Gupta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0'
|
69
69
|
description: rbtrace shows you method calls happening inside another ruby process
|
70
70
|
in real time.
|
71
71
|
email: aman@tmm1.net
|
@@ -75,7 +75,9 @@ extensions:
|
|
75
75
|
- ext/extconf.rb
|
76
76
|
extra_rdoc_files: []
|
77
77
|
files:
|
78
|
+
- ".github/workflows/ci.yml"
|
78
79
|
- ".gitignore"
|
80
|
+
- CHANGELOG
|
79
81
|
- Gemfile
|
80
82
|
- LICENSE
|
81
83
|
- README.md
|
@@ -85,6 +87,7 @@ files:
|
|
85
87
|
- ext/extconf.rb
|
86
88
|
- ext/rbtrace.c
|
87
89
|
- ext/src/msgpack-1.1.0.tar.gz
|
90
|
+
- lib/rbtrace.rb
|
88
91
|
- lib/rbtrace/cli.rb
|
89
92
|
- lib/rbtrace/core_ext.rb
|
90
93
|
- lib/rbtrace/interactive/irb.rb
|
@@ -123,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
126
|
- !ruby/object:Gem::Version
|
124
127
|
version: '0'
|
125
128
|
requirements: []
|
126
|
-
rubygems_version: 3.
|
129
|
+
rubygems_version: 3.4.19
|
127
130
|
signing_key:
|
128
131
|
specification_version: 4
|
129
132
|
summary: 'rbtrace: like strace but for ruby code'
|