rbtrace 0.3.5 → 0.3.6

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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbtrace (0.3.5)
4
+ rbtrace (0.3.6)
5
5
  ffi (>= 1.0.5)
6
6
  msgpack (>= 0.4.3)
7
7
  trollop (>= 1.16.2)
data/README.md CHANGED
@@ -15,7 +15,7 @@ in production.
15
15
 
16
16
  ## tracer types
17
17
 
18
- rbtrace has several different tracing modes:
18
+ rbtrace has several different tracing modes.
19
19
 
20
20
  ### firehose: show everything
21
21
 
@@ -33,6 +33,12 @@ rbtrace has several different tracing modes:
33
33
 
34
34
  % rbtrace -p <PID> --gc
35
35
 
36
+ ### notes
37
+
38
+ `--firehose` is not reliable on osx.
39
+
40
+ `--slow`, `--gc` and `--methods` can be combined.
41
+
36
42
  ## predefined tracers
37
43
 
38
44
  rbtrace also includes a set of [predefined tracers](https://github.com/tmm1/rbtrace/tree/master/tracers)
@@ -186,12 +192,6 @@ for popular ruby libraries and functions.
186
192
 
187
193
  ## todo
188
194
 
189
- * write some real tests (that assert things)
190
- * more expressive selectors (.*Controller#)
191
- * include date in show time (-t)
192
- * add --eval
193
- * add --forkgdb
194
- * add --strace mode/expression
195
195
  * add triggers to start tracing slow methods only inside another method
196
196
  * add watch expressions to fire tracers only when an expression is true
197
197
  * add special expressions for method args (_arg0_, _arguments_)
data/bin/rbtrace CHANGED
@@ -142,7 +142,7 @@ class RBTracer
142
142
  raise ArgumentError, 'could not signal process, are you running as root?'
143
143
  end
144
144
 
145
- Process.kill 'URG', @pid
145
+ signal
146
146
  sleep 0.25 # wait for process to create msgqs
147
147
 
148
148
  @qi = MsgQ.msgget( pid, 0666)
@@ -169,6 +169,7 @@ class RBTracer
169
169
 
170
170
  @out = STDOUT
171
171
  @prefix = ' '
172
+ @printed_newline = true
172
173
 
173
174
  @show_time = false
174
175
  @show_duration = true
@@ -201,6 +202,35 @@ class RBTracer
201
202
  send_cmd(:devmode)
202
203
  end
203
204
 
205
+ # Fork the process and return the copy's pid.
206
+ #
207
+ # Returns a Fixnum pid.
208
+ def fork
209
+ send_cmd(:fork)
210
+ if wait_for(30, 'for fork'){ !!@forked_pid }
211
+ @forked_pid
212
+ else
213
+ STDERR.puts '*** timed out waiting for fork'
214
+ end
215
+ end
216
+
217
+ # Evaluate some ruby code.
218
+ #
219
+ # Returns the String result.
220
+ def eval(code)
221
+ if (err = valid_syntax?(code)) != true
222
+ raise ArgumentError, "#{err.class} for expression #{code.inspect}"
223
+ end
224
+
225
+ send_cmd(:eval, code)
226
+
227
+ if wait_for(10, 'for eval response'){ !!@eval_result }
228
+ @eval_result
229
+ else
230
+ STDERR.puts '*** timed out waiting for eval response'
231
+ end
232
+ end
233
+
204
234
  # Turn on GC tracing.
205
235
  #
206
236
  # Returns nothing.
@@ -257,17 +287,17 @@ class RBTracer
257
287
  rescue Errno::ESRCH
258
288
  end
259
289
 
260
- puts
290
+ newline
261
291
 
262
292
  if wait_for{ @attached == false }
263
- puts
293
+ newline
264
294
  STDERR.puts "*** detached from process #{pid}"
265
295
  else
266
- puts
296
+ newline
267
297
  STDERR.puts "*** could not detach cleanly from process #{pid}"
268
298
  end
269
299
  rescue Errno::EINVAL, Errno::EIDRM
270
- puts
300
+ newline
271
301
  STDERR.puts "*** process #{pid} is gone"
272
302
  STDERR.puts "*** #{$!.inspect}"
273
303
  STDERR.puts $!.backtrace.join("\n ")
@@ -302,15 +332,24 @@ class RBTracer
302
332
  end
303
333
  end
304
334
 
335
+ def puts(arg=nil)
336
+ @printed_newline = true
337
+ arg ? @out.puts(arg) : @out.puts
338
+ end
339
+
305
340
  private
306
341
 
342
+ def signal
343
+ Process.kill 'URG', @pid
344
+ end
345
+
307
346
  # Process incoming events until either a timeout or a condition becomes true.
308
347
  #
309
348
  # time - The Fixnum timeout in seconds.
310
349
  # block - The Block that is checked every 50ms until it returns true.
311
350
  #
312
351
  # Returns true when the condition was met, or false on a timeout.
313
- def wait_for(time=5)
352
+ def wait_for(time=5, reason='to detach cleanly')
314
353
  wait = 0.05 # polling interval
315
354
 
316
355
  (time/wait).to_i.times do
@@ -321,7 +360,7 @@ class RBTracer
321
360
 
322
361
  return true if yield
323
362
  rescue Interrupt
324
- STDERR.puts "*** waiting to detach cleanly (#{time.to_i}s left)"
363
+ STDERR.puts "*** waiting #{reason} (#{time.to_i}s left)"
325
364
  retry
326
365
  end
327
366
  end
@@ -337,7 +376,7 @@ class RBTracer
337
376
  rescue Errno::EINTR
338
377
  retry
339
378
  end
340
- Process.kill 'URG', @pid
379
+ signal
341
380
  recv_lines
342
381
  end
343
382
 
@@ -351,18 +390,20 @@ class RBTracer
351
390
 
352
391
  def valid_syntax?(code)
353
392
  begin
354
- eval("#{code}\nBEGIN {return true}", nil, 'rbtrace_expression', 0)
393
+ Kernel.eval("#{code}\nBEGIN {return true}", nil, 'rbtrace_expression', 0)
355
394
  rescue Exception => e
356
395
  e
357
396
  end
358
397
  end
359
398
 
360
- def print(*args)
361
- @out.print(*args)
399
+ def print(arg)
400
+ @printed_newline = false
401
+ @out.print(arg)
362
402
  end
363
403
 
364
- def puts(*args)
365
- @out.puts(*args)
404
+ def newline
405
+ puts unless @printed_newline
406
+ @printed_newline = true
366
407
  end
367
408
 
368
409
  def parse_cmd(line)
@@ -379,6 +420,11 @@ class RBTracer
379
420
  event = cmd.shift
380
421
 
381
422
  case event
423
+ when 'during_gc'
424
+ sleep 0.01
425
+ signal
426
+ return
427
+
382
428
  when 'attached'
383
429
  tracer_pid, = *cmd
384
430
  if tracer_pid != Process.pid
@@ -406,6 +452,14 @@ class RBTracer
406
452
  end
407
453
 
408
454
  case event
455
+ when 'forked'
456
+ pid, = *cmd
457
+ @forked_pid = pid
458
+
459
+ when 'evaled'
460
+ res, = *cmd
461
+ @eval_result = res
462
+
409
463
  when 'mid'
410
464
  mid, name = *cmd
411
465
  @methods[mid] = name
@@ -562,7 +616,7 @@ class RBTracer
562
616
  parser = Trollop::Parser.new do
563
617
  version <<-EOS
564
618
  rbtrace: like strace, but for ruby code
565
- version 0.3.5
619
+ version 0.3.6
566
620
  (c) 2011 Aman Gupta (tmm1)
567
621
  http://github.com/tmm1/rbtrace
568
622
  EOS
@@ -665,6 +719,14 @@ EOS
665
719
 
666
720
  opt :devmode,
667
721
  "assume the ruby process is reloading classes and methods"
722
+
723
+ opt :fork,
724
+ "fork a copy of the process for debugging (so you can attach gdb.rb)"
725
+
726
+ opt :eval,
727
+ "evaluate a ruby expression in the process",
728
+ :type => String,
729
+ :short => '-e'
668
730
  end
669
731
 
670
732
  opts = Trollop.with_standard_exception_handling(parser) do
@@ -672,12 +734,16 @@ EOS
672
734
  parser.parse(ARGV)
673
735
  end
674
736
 
675
- unless %w[ slow firehose methods config gc ].find{ |n| opts[:"#{n}_given"] }
737
+ unless %w[ fork eval slow firehose methods config gc ].find{ |n| opts[:"#{n}_given"] }
676
738
  $stderr.puts "Error: --slow, --gc, --firehose, --methods or --config required."
677
739
  $stderr.puts "Try --help for help."
678
740
  exit(-1)
679
741
  end
680
742
 
743
+ if opts[:fork_given] and opts[:pid].size != 1
744
+ parser.die :fork, '(can only be invoked with one pid)'
745
+ end
746
+
681
747
  methods = []
682
748
 
683
749
  if opts[:methods_given]
@@ -764,37 +830,35 @@ EOS
764
830
  parser.die :pid, "(#{e.message})"
765
831
  end
766
832
 
767
- if opts[:devmode_given]
768
- tracer.devmode
769
- end
770
-
771
- if opts[:gc_given]
772
- tracer.gc
773
- end
774
-
775
- if opts[:firehose_given]
776
- tracer.firehose
777
- else
778
- if methods.any?
779
- tracer.add(methods)
780
- end
833
+ if opts[:fork_given]
834
+ pid = tracer.fork
835
+ STDERR.puts "*** forked off a busy looping copy at #{pid} (make sure to kill -9 it when you're done)"
781
836
 
782
- if opts[:slow_given]
783
- tracer.watch(opts[:slow])
837
+ elsif opts[:eval_given]
838
+ if res = tracer.eval(code = opts[:eval])
839
+ tracer.puts ">> #{code}"
840
+ tracer.puts "=> #{res}"
784
841
  end
785
- end
786
842
 
787
- if output
788
- tracer.out = output
789
- end
843
+ else
844
+ tracer.out = output if output
845
+ tracer.prefix = ' ' * opts[:prefix]
846
+ tracer.show_time = opts[:start_time]
847
+ tracer.show_duration = !opts[:no_duration]
790
848
 
791
- tracer.prefix = ' ' * opts[:prefix]
792
- tracer.show_duration = !opts[:no_duration]
793
- tracer.show_time = opts[:start_time]
849
+ tracer.devmode if opts[:devmode_given]
850
+ tracer.gc if opts[:gc_given]
794
851
 
795
- begin
796
- tracer.recv_loop
797
- rescue Interrupt, SignalException
852
+ if opts[:firehose_given]
853
+ tracer.firehose
854
+ else
855
+ tracer.add(methods) if methods.any?
856
+ tracer.watch(opts[:slow]) if opts[:slow_given]
857
+ end
858
+ begin
859
+ tracer.recv_loop
860
+ rescue Interrupt, SignalException
861
+ end
798
862
  end
799
863
  ensure
800
864
  if tracer
data/ext/extconf.rb CHANGED
@@ -35,6 +35,8 @@ unless have_library('msgpackc_ext') and have_header('msgpack.h')
35
35
  raise 'msgpack build failed'
36
36
  end
37
37
 
38
+ have_func('rb_during_gc', 'ruby.h')
39
+
38
40
  # increase message size on linux
39
41
  if RUBY_PLATFORM =~ /linux/
40
42
  $defs.push("-DBUF_SIZE=256")
data/ext/rbtrace.c CHANGED
@@ -21,7 +21,6 @@
21
21
 
22
22
  #ifndef RUBY_VM
23
23
  #include <env.h>
24
- #include <intern.h>
25
24
  #include <node.h>
26
25
  #include <st.h>
27
26
  #define rb_sourcefile() (ruby_current_node ? ruby_current_node->nd_file : 0)
@@ -264,8 +263,10 @@ rbtrace__send_names(ID mid, VALUE klass)
264
263
  if (!rbtracer.klass_tbl)
265
264
  rbtracer.klass_tbl = st_init_numtable();
266
265
 
267
- if (!st_is_member(rbtracer.klass_tbl, klass)) {
268
- st_insert(rbtracer.klass_tbl, (st_data_t)klass, (st_data_t)1);
266
+ if (rbtracer.devmode || !st_is_member(rbtracer.klass_tbl, klass)) {
267
+ if (!rbtracer.devmode)
268
+ st_insert(rbtracer.klass_tbl, (st_data_t)klass, (st_data_t)1);
269
+
269
270
  rbtrace__send_event(2,
270
271
  "klass",
271
272
  'l', klass,
@@ -715,26 +716,17 @@ rbtracer_watch(uint32_t threshold)
715
716
  }
716
717
  }
717
718
 
718
- static void
719
- rbtracer_unwatch()
720
- {
721
- if (rbtracer.slow) {
722
- event_hook_remove();
723
-
724
- rbtracer.firehose = false;
725
- rbtracer.slow = false;
726
- }
727
- }
728
-
729
719
  static void
730
720
  msgq_teardown()
731
721
  {
732
- if (rbtracer.mqo_id != -1) {
722
+ pid_t pid = getpid();
723
+
724
+ if (rbtracer.mqo_id != -1 && rbtracer.mqo_key == (key_t)pid) {
733
725
  msgctl(rbtracer.mqo_id, IPC_RMID, NULL);
734
726
  rbtracer.mqo_id = -1;
735
727
  rbtracer.mqo_key = 0;
736
728
  }
737
- if (rbtracer.mqi_id != -1) {
729
+ if (rbtracer.mqi_id != -1 && rbtracer.mqi_key == (key_t)-pid) {
738
730
  msgctl(rbtracer.mqi_id, IPC_RMID, NULL);
739
731
  rbtracer.mqi_id = -1;
740
732
  rbtracer.mqi_key = 0;
@@ -774,117 +766,188 @@ msgq_setup()
774
766
  }
775
767
 
776
768
  static void
777
- sigurg(int signal)
769
+ rbtrace__process_event(msgpack_object cmd)
778
770
  {
771
+ if (cmd.type != MSGPACK_OBJECT_ARRAY)
772
+ return;
773
+
779
774
  static int last_tracer_id = -1; // hax
780
- msgq_setup();
781
- if (rbtracer.mqi_id == -1) return;
775
+ char query[BUF_SIZE];
782
776
 
783
- event_msg_t msg;
784
- int n = 0;
777
+ char code[BUF_SIZE+150];
778
+ VALUE val = Qnil;
785
779
 
786
- while (true) {
787
- int ret = -1;
780
+ msgpack_object_array ary;
781
+ msgpack_object_raw str;
788
782
 
789
- for (n=0; n<10 && ret==-1; n++)
790
- ret = msgrcv(rbtracer.mqi_id, &msg, sizeof(msg)-sizeof(long), 0, IPC_NOWAIT);
783
+ /* fprintf(stderr, "GOT: ");*/
784
+ /* msgpack_object_print(stderr, cmd);*/
785
+ /* fprintf(stderr, "\n");*/
791
786
 
792
- if (ret == -1) {
793
- break;
794
- } else {
795
- char query[sizeof(msg.buf)];
787
+ ary = cmd.via.array;
788
+
789
+ if (ary.size < 1 ||
790
+ ary.ptr[0].type != MSGPACK_OBJECT_RAW)
791
+ return;
796
792
 
797
- msgpack_object cmd;
798
- msgpack_object_array ary;
799
- msgpack_object_raw str;
793
+ str = ary.ptr[0].via.raw;
800
794
 
801
- msgpack_unpacked unpacked;
802
- msgpack_unpacked_init(&unpacked);
795
+ if (0 == strncmp("attach", str.ptr, str.size)) {
796
+ if (ary.size != 2 ||
797
+ ary.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER)
798
+ return;
803
799
 
804
- bool success = msgpack_unpack_next(&unpacked, msg.buf, sizeof(msg.buf), NULL);
805
- cmd = unpacked.data;
800
+ pid_t pid = (pid_t) ary.ptr[1].via.u64;
801
+
802
+ if (pid && rbtracer.attached_pid == 0)
803
+ rbtracer.attached_pid = pid;
806
804
 
807
- if (!success || cmd.type != MSGPACK_OBJECT_ARRAY)
808
- continue;
805
+ rbtrace__send_event(1,
806
+ "attached",
807
+ 'u', (uint32_t) rbtracer.attached_pid
808
+ );
809
+
810
+ } else if (0 == strncmp("detach", str.ptr, str.size)) {
811
+ if (rbtracer.attached_pid) {
812
+ rbtrace__send_event(1,
813
+ "detached",
814
+ 'u', (uint32_t) rbtracer.attached_pid
815
+ );
816
+ }
809
817
 
810
- /* fprintf(stderr, "GOT: ");*/
811
- /* msgpack_object_print(stderr, cmd);*/
812
- /* fprintf(stderr, "\n");*/
818
+ rbtracer_detach();
813
819
 
814
- ary = cmd.via.array;
820
+ } else if (0 == strncmp("watch", str.ptr, str.size)) {
821
+ if (ary.size != 2 ||
822
+ ary.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER)
823
+ return;
815
824
 
816
- if (ary.size < 1 ||
817
- ary.ptr[0].type != MSGPACK_OBJECT_RAW)
818
- continue;
825
+ unsigned int msec = ary.ptr[1].via.u64;
826
+ rbtracer_watch(msec);
819
827
 
820
- str = ary.ptr[0].via.raw;
828
+ } else if (0 == strncmp("firehose", str.ptr, str.size)) {
829
+ rbtracer.firehose = true;
830
+ event_hook_install();
821
831
 
822
- if (0 == strncmp("attach", str.ptr, str.size)) {
823
- if (ary.size != 2 ||
824
- ary.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER)
825
- continue;
832
+ } else if (0 == strncmp("add", str.ptr, str.size)) {
833
+ if (ary.size != 2 ||
834
+ ary.ptr[1].type != MSGPACK_OBJECT_RAW)
835
+ return;
826
836
 
827
- pid_t pid = (pid_t) ary.ptr[1].via.u64;
837
+ str = ary.ptr[1].via.raw;
828
838
 
829
- if (pid && rbtracer.attached_pid == 0)
830
- rbtracer.attached_pid = pid;
839
+ strncpy(query, str.ptr, str.size);
840
+ query[str.size] = 0;
841
+ last_tracer_id = rbtracer_add(query);
831
842
 
832
- rbtrace__send_event(1,
833
- "attached",
834
- 'u', (uint32_t) rbtracer.attached_pid
835
- );
843
+ } else if (0 == strncmp("addexpr", str.ptr, str.size)) {
844
+ if (ary.size != 2 ||
845
+ ary.ptr[1].type != MSGPACK_OBJECT_RAW)
846
+ return;
836
847
 
837
- } else if (0 == strncmp("detach", str.ptr, str.size)) {
838
- if (rbtracer.attached_pid) {
839
- rbtrace__send_event(1,
840
- "detached",
841
- 'u', (uint32_t) rbtracer.attached_pid
848
+ str = ary.ptr[1].via.raw;
849
+
850
+ strncpy(query, str.ptr, str.size);
851
+ query[str.size] = 0;
852
+ rbtracer_add_expr(last_tracer_id, query);
853
+
854
+ } else if (0 == strncmp("gc", str.ptr, str.size)) {
855
+ rbtracer.gc = true;
856
+
857
+ } else if (0 == strncmp("devmode", str.ptr, str.size)) {
858
+ rbtracer.devmode = true;
859
+
860
+ } else if (0 == strncmp("fork", str.ptr, str.size)) {
861
+ pid_t outer = fork();
862
+
863
+ if (outer == 0) {
864
+ rb_eval_string_protect("$0 = \"[DEBUG] #{Process.ppid}\"", 0);
865
+ setpgrp();
866
+ pid_t inner = fork();
867
+
868
+ if (inner == 0) {
869
+ // a ruby process will never have more than 20k
870
+ // open file descriptors, right?
871
+ int fd;
872
+ for (fd=3; fd<20000; fd++)
873
+ close(fd);
874
+
875
+ // busy loop
876
+ while (1) sleep(1);
877
+
878
+ // don't return to ruby
879
+ _exit(0);
880
+ }
881
+
882
+ rbtrace__send_event(1,
883
+ "forked",
884
+ inner == -1 ? 'b' : 'u',
885
+ inner == -1 ? false : (uint32_t) inner
842
886
  );
843
- }
844
887
 
845
- rbtracer_detach();
888
+ // kill off outer fork
889
+ _exit(0);
890
+ }
846
891
 
847
- } else if (0 == strncmp("watch", str.ptr, str.size)) {
848
- if (ary.size != 2 ||
849
- ary.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER)
850
- continue;
892
+ if (outer != -1) {
893
+ waitpid(outer, NULL, 0);
894
+ }
851
895
 
852
- unsigned int msec = ary.ptr[1].via.u64;
853
- rbtracer_watch(msec);
896
+ } else if (0 == strncmp("eval", str.ptr, str.size)) {
897
+ if (ary.size != 2 ||
898
+ ary.ptr[1].type != MSGPACK_OBJECT_RAW)
899
+ return;
854
900
 
855
- } else if (0 == strncmp("firehose", str.ptr, str.size)) {
856
- rbtracer.firehose = true;
857
- event_hook_install();
901
+ str = ary.ptr[1].via.raw;
858
902
 
859
- } else if (0 == strncmp("add", str.ptr, str.size)) {
860
- if (ary.size != 2 ||
861
- ary.ptr[1].type != MSGPACK_OBJECT_RAW)
862
- continue;
903
+ strncpy(query, str.ptr, str.size);
904
+ query[str.size] = 0;
863
905
 
864
- str = ary.ptr[1].via.raw;
906
+ snprintf(code, BUF_SIZE+150, "(begin; %s; rescue Exception => e; e; end).inspect", query);
907
+ val = rb_eval_string_protect(code, 0);
865
908
 
866
- strncpy(query, str.ptr, str.size);
867
- query[str.size] = 0;
868
- last_tracer_id = rbtracer_add(query);
909
+ if (TYPE(val) == T_STRING) {
910
+ rbtrace__send_event(1,
911
+ "evaled",
912
+ 's', RSTRING_PTR(val)
913
+ );
914
+ }
869
915
 
870
- } else if (0 == strncmp("addexpr", str.ptr, str.size)) {
871
- if (ary.size != 2 ||
872
- ary.ptr[1].type != MSGPACK_OBJECT_RAW)
873
- continue;
916
+ }
917
+ }
874
918
 
875
- str = ary.ptr[1].via.raw;
919
+ static void
920
+ sigurg(int signal)
921
+ {
922
+ msgq_setup();
923
+ if (rbtracer.mqi_id == -1) return;
876
924
 
877
- strncpy(query, str.ptr, str.size);
878
- query[str.size] = 0;
879
- rbtracer_add_expr(last_tracer_id, query);
925
+ #ifdef HAVE_RB_DURING_GC
926
+ if (rb_during_gc()) {
927
+ rbtrace__send_event(0, "during_gc");
928
+ return;
929
+ }
930
+ #endif
880
931
 
881
- } else if (0 == strncmp("gc", str.ptr, str.size)) {
882
- rbtracer.gc = true;
932
+ event_msg_t msg;
933
+ int n = 0;
883
934
 
884
- } else if (0 == strncmp("devmode", str.ptr, str.size)) {
885
- rbtracer.devmode = true;
935
+ while (true) {
936
+ int ret = -1;
886
937
 
887
- }
938
+ for (n=0; n<10 && ret==-1; n++)
939
+ ret = msgrcv(rbtracer.mqi_id, &msg, sizeof(msg)-sizeof(long), 0, IPC_NOWAIT);
940
+
941
+ if (ret == -1) {
942
+ break;
943
+ } else {
944
+ msgpack_unpacked unpacked;
945
+ msgpack_unpacked_init(&unpacked);
946
+
947
+ bool success = msgpack_unpack_next(&unpacked, msg.buf, sizeof(msg.buf), NULL);
948
+ if (!success) continue;
949
+
950
+ rbtrace__process_event(unpacked.data);
888
951
  }
889
952
  }
890
953
  }
data/rbtrace.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'rbtrace'
3
- s.version = '0.3.5'
3
+ s.version = '0.3.6'
4
4
  s.homepage = 'http://github.com/tmm1/rbtrace'
5
5
 
6
6
  s.authors = 'Aman Gupta'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbtrace
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 5
10
- version: 0.3.5
9
+ - 6
10
+ version: 0.3.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Aman Gupta
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-21 00:00:00 -08:00
18
+ date: 2011-02-22 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency