rbtrace 0.4.13 → 0.5.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: 942f750987d2faf32e6d104d097bb8dfa68dda43644750259a1bc0d3a23a47d6
4
- data.tar.gz: 1fb44c1bbf4936871cef319331d27f9743debe12d28ca05235fc1750e70aa261
3
+ metadata.gz: dc6fc7320951955050f41babf4a1effbec1eaac42e9103a0c8b08c69cc2ad090
4
+ data.tar.gz: 105f410c0c78e6f23c3d6617165b7a1c3c9a72976735cfd185481c91acb3bb17
5
5
  SHA512:
6
- metadata.gz: df57be716aff5cc91cd2ce54e3bb55015b76a4390fe86b7f68b91a63794364387cd9edae630f6f83584c5288f8d2fcdd3a191c6c43e75a1cdabfb064ae5cd0be
7
- data.tar.gz: 60cf530a8271c124dd1cd8df54761812e951aa9a722df89da12c5be163a87838b7fcce03b12244699df732424a5eeaf26e6ac80270f4afaa79f756875654521a
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
@@ -13,7 +13,7 @@ require 'fileutils'
13
13
 
14
14
  libdir = File.basename RbConfig::CONFIG['libdir']
15
15
 
16
- unless File.exists?("#{CWD}/dst/#{libdir}/libmsgpackc.a")
16
+ unless File.exist?("#{CWD}/dst/#{libdir}/libmsgpackc.a")
17
17
  Logging.message "Building msgpack\n"
18
18
 
19
19
  msgpack = File.basename('msgpack-1.1.0.tar.gz')
@@ -28,7 +28,7 @@ unless File.exists?("#{CWD}/dst/#{libdir}/libmsgpackc.a")
28
28
  end
29
29
 
30
30
  Dir.chdir('src') do
31
- FileUtils.rm_rf(dir) if File.exists?(dir)
31
+ FileUtils.rm_rf(dir) if File.exist?(dir)
32
32
 
33
33
  sys("tar zxvfo #{msgpack}")
34
34
  Dir.chdir(dir) do
@@ -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
@@ -22,6 +22,7 @@
22
22
 
23
23
  #include <msgpack.h>
24
24
  #include <ruby.h>
25
+ #include <ruby/debug.h>
25
26
 
26
27
  #ifndef RUBY_VM
27
28
  #include <env.h>
@@ -51,6 +52,11 @@
51
52
  #endif
52
53
 
53
54
 
55
+ // The SUN_LEN macro is not available on Android
56
+ #ifndef SUN_LEN
57
+ #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
58
+ #endif
59
+
54
60
  static uint64_t
55
61
  ru_utime_usec()
56
62
  {
@@ -73,7 +79,7 @@ timeofday_usec()
73
79
  #define MAX_TRACERS 100 // max method tracers
74
80
  #define MAX_EXPRS 10 // max expressions per tracer
75
81
  #ifndef BUF_SIZE // msgq buffer size
76
- #define BUF_SIZE 120
82
+ #define BUF_SIZE 1024
77
83
  #endif
78
84
 
79
85
  typedef struct {
@@ -688,7 +694,7 @@ rbtracer_add(char *query, bool is_slow)
688
694
  *idx = NULL,
689
695
  *method = NULL;
690
696
 
691
- if (NULL != (idx = rindex(query, '.'))) {
697
+ if (NULL != (idx = strrchr(query, '.'))) {
692
698
  klass_begin = 0;
693
699
  klass_end = idx - query;
694
700
  is_singleton = true;
@@ -700,7 +706,7 @@ rbtracer_add(char *query, bool is_slow)
700
706
 
701
707
  method = idx+1;
702
708
 
703
- } else if (NULL != (idx = rindex(query, '#'))) {
709
+ } else if (NULL != (idx = strrchr(query, '#'))) {
704
710
  klass_begin = 0;
705
711
  klass_end = idx - query;
706
712
  is_singleton = false;
@@ -870,9 +876,7 @@ eval_inspect(VALUE rb_code) {
870
876
  }
871
877
 
872
878
  static VALUE
873
- rescue_inspect(VALUE arg) {
874
- VALUE exception = rb_errinfo(); /* get last exception */
875
- rb_set_errinfo(Qnil);
879
+ rescue_inspect(VALUE arg, VALUE exception) {
876
880
  return rb_funcall(exception, rb_intern("inspect"), 0);
877
881
  }
878
882
 
@@ -885,7 +889,6 @@ rbtrace__process_event(msgpack_object cmd)
885
889
  static int last_tracer_id = -1; // hax
886
890
  char query[BUF_SIZE];
887
891
 
888
- char code[BUF_SIZE+150];
889
892
  VALUE val = Qnil;
890
893
 
891
894
  msgpack_object_array ary;
@@ -1129,31 +1132,26 @@ send_write(VALUE klass, VALUE val) {
1129
1132
  return Qnil;
1130
1133
  }
1131
1134
 
1135
+ static const rb_data_type_t rbtrace_type = {
1136
+ "RBTrace",
1137
+ {
1138
+ rbtrace_gc_mark,
1139
+ }
1140
+ };
1141
+
1132
1142
  void
1133
1143
  Init_rbtrace()
1134
1144
  {
1135
1145
  rbtrace_module = rb_define_module("RBTrace");
1136
1146
  VALUE output = rb_define_module_under(rbtrace_module, "OUT");
1137
1147
 
1138
- rb_define_singleton_method(output, "write", send_write, 1);
1148
+ rb_const_set(rbtrace_module, rb_intern("BUF_SIZE"), INT2NUM(BUF_SIZE));
1139
1149
 
1140
- rb_eval_string(
1141
- "module RBTrace\n"
1142
- " def self.eval_context\n"
1143
- " @eval_context ||= binding\n"
1144
- " end\n"
1145
-
1146
- " def self.eval_and_inspect(code)\n"
1147
- " t = Thread.new { Thread.current[:output] = eval_context.eval(code).inspect }\n"
1148
- " t.join\n"
1149
- " t[:output]\n"
1150
- " end\n"
1151
- "end\n"
1152
- );
1150
+ rb_define_singleton_method(output, "write", send_write, 1);
1153
1151
 
1154
1152
  // hook into the gc
1155
- gc_hook = Data_Wrap_Struct(rb_cObject, rbtrace_gc_mark, NULL, NULL);
1156
1153
  rb_global_variable(&gc_hook);
1154
+ gc_hook = TypedData_Wrap_Struct(rb_cObject, &rbtrace_type, NULL);
1157
1155
 
1158
1156
  // catch signal telling us to read from the msgq
1159
1157
  #if defined(HAVE_RB_POSTPONED_JOB_REGISTER_ONE)
data/lib/rbtrace/cli.rb CHANGED
@@ -12,7 +12,7 @@ class RBTraceCLI
12
12
  #
13
13
  # Returns nothing.
14
14
  def self.check_msgmnb
15
- if File.exists?(msgmnb = "/proc/sys/kernel/msgmnb")
15
+ if File.exist?(msgmnb = "/proc/sys/kernel/msgmnb")
16
16
  curr = File.read(msgmnb).to_i
17
17
  max = 1024*1024
18
18
  cmd = "sysctl kernel.msgmnb=#{max}"
@@ -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
@@ -273,7 +276,7 @@ EOS
273
276
  file = [
274
277
  config,
275
278
  File.expand_path("../../../tracers/#{config}.tracer", __FILE__)
276
- ].find{ |f| File.exists?(f) }
279
+ ].find{ |f| File.exist?(f) }
277
280
 
278
281
  unless file
279
282
  parser.die :config, '(file does not exist)'
@@ -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("file = File.open('#{filename}', 'w'); ObjectSpace.dump_all(output: file); file.close")
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}"
@@ -1,7 +1,3 @@
1
- class String
2
- alias :bytesize :size
3
- end unless ''.respond_to?(:bytesize)
4
-
5
1
  module FFI::LastError
6
2
  Errnos = Errno::constants.map(&Errno.method(:const_get)).inject({}) do |hash, c|
7
3
  hash[ c.const_get(:Errno) ] = c
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 = RUBY_PLATFORM =~ /linux/ ? 256 : 120
9
+ BUF_SIZE = RBTrace::BUF_SIZE
9
10
  IPC_NOWAIT = 004000
10
11
 
11
12
  layout :mtype, :long,
@@ -96,7 +96,7 @@ class RBTracer
96
96
  end
97
97
 
98
98
  def clean_socket_path
99
- FileUtils.rm(socket_path) if File.exists?(socket_path)
99
+ FileUtils.rm(socket_path) if File.exist?(socket_path)
100
100
  end
101
101
 
102
102
  # Watch for method calls slower than a threshold.
@@ -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
- raise ArgumentError, 'command is too long' if msg.bytesize >= MsgQ::EventMsg::BUF_SIZE
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RBTracer
4
- VERSION = '0.4.13'
4
+ VERSION = "0.5.0"
5
5
  end
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", "~> 10.0"
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
@@ -1,4 +1,5 @@
1
- require 'ext/rbtrace'
1
+ require 'rbtrace'
2
+ require 'tmpdir'
2
3
 
3
4
  class String
4
5
  def multiply_vowels(num)
@@ -26,19 +27,21 @@ end
26
27
 
27
28
  while true
28
29
  proc {
29
- Dir.chdir("/tmp") do
30
- Dir.pwd
31
- Process.pid
32
- 'hello'.multiply_vowels(3){ :ohai }
33
- sleep rand*0.5
30
+ Dir.mktmpdir do |tmp|
31
+ Dir.chdir(tmp) do
32
+ Dir.pwd
33
+ Process.pid
34
+ 'hello'.multiply_vowels(3){ :ohai }
35
+ sleep rand*0.5
34
36
 
35
- ENV['blah']
36
- GC.start
37
+ ENV['blah']
38
+ GC.start
37
39
 
38
- reload_test.call
39
- Test.run
40
+ reload_test.call
41
+ Test.run
40
42
 
41
- #fib(1024*100)
43
+ #fib(1024*100)
44
+ end
42
45
  end
43
46
  }.call
44
47
  end
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.13
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-19 00:00:00.000000000 Z
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: '10.0'
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: '10.0'
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
@@ -107,7 +110,7 @@ homepage: http://github.com/tmm1/rbtrace
107
110
  licenses:
108
111
  - MIT
109
112
  metadata: {}
110
- post_install_message:
113
+ post_install_message:
111
114
  rdoc_options: []
112
115
  require_paths:
113
116
  - lib
@@ -123,8 +126,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
126
  - !ruby/object:Gem::Version
124
127
  version: '0'
125
128
  requirements: []
126
- rubygems_version: 3.0.3
127
- signing_key:
129
+ rubygems_version: 3.4.19
130
+ signing_key:
128
131
  specification_version: 4
129
132
  summary: 'rbtrace: like strace but for ruby code'
130
133
  test_files: []