rbtrace 0.4.13 → 0.5.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: 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: []