method_call_tracer 0.2.0 → 1.0.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
  SHA1:
3
- metadata.gz: 5ec122bd2856f55ddd0958f690f667cfac0dd6d0
4
- data.tar.gz: c517dc70ab2c883d8acc5e8b32e78a833c0565ba
3
+ metadata.gz: deeb870dcd4aa81de135eb555d0001c94be527f8
4
+ data.tar.gz: e8a9ac1fcf43516060cbef76292a3d894a4546b3
5
5
  SHA512:
6
- metadata.gz: f1f235d6fe985239be47614b1bcfd9a925c157152de974d7dabf071d0cf36a024dbc63f37db9b9bbfc71351faec6cfd0b93d5fef9874916199e456e1b26366a2
7
- data.tar.gz: 672b73d90ab60177d87b3f0a67ab828800154e077c20cdc51facc2178637ba3ff015731e7bf85a13c53d844827406218c61575799a114f335c7869272af34a01
6
+ metadata.gz: 11ca20032353767f513a4b17895ff8d5fd5533680092fe0b5feddb9296fda1846c3a277125c698810e5ea62d62f5c69c2f130f6b8e9243b0f44eda6b474ea74d
7
+ data.tar.gz: db328e9510208b7ca663bfbcc9d46eef8d946d1751f3fe7c3b8adafb4adc572573221b207a1273792771405c4ed4cda13bdb9f8164c8238e7027314a8ed99d5f
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ - Accept a second, optional parameter in the initializer so we can short-circuit matching methods based on the class name for performance. This is so useful that the initializer's two arguments are now both keywords, indicating equal importance.
6
+ - MethodTracer::Tracer no longer creates TracePoints upon initialization. Use the new `#enable` method to begin tracing.
7
+ - Change output format to be even more machine-readable
8
+
9
+ ## 0.2.0
10
+
11
+ - Rewrite to use Ruby's TracePoint instead of manually unbinding and rebinding methods. This has fewer side effects, but is much slower.
12
+
13
+ ## 0.1.0
14
+
15
+ - Initial release
data/README.md CHANGED
@@ -1,25 +1,34 @@
1
1
  # MethodTracer
2
2
 
3
- MethodTracer is a tool for detecting lines in an application that call certain methods, somewhat akin to the syscall monitoring functionality of strace(1). The methods to be traced are specified by file pattern, which makes it simple to trace an entire gem<sup>1</sup>. The most common use case is helping developers and testers focus their efforts when upgrading or changing gems in large applications.
3
+ MethodTracer is a tool for detecting lines in an application that call certain methods, somewhat akin to the syscall monitoring functionality of strace(1).
4
4
 
5
- <sup>1</sup> Designating traced methods by file lets you trace methods defined in the "namespace" of a different gem. For example, if a hypothetical gem `activerecord-extension` defines some methods on the class `ActiveRecord`, we still have the ability to trace only the methods from `activerecord-extension` without capturing the methods from `activerecord`.
5
+ The most common use case is helping developers and testers focus their efforts when upgrading or changing gems in large applications.
6
+
7
+ MethodTracer's output is intended to be easily machine-readable.
6
8
 
7
9
  **Note about gem name**: due to a naming collision on rubygems.org, this gem appears there as `method_call_tracer`.
8
10
 
9
11
  ## Usage
10
12
 
11
- To attach tracers to methods, instantiate `MethodTracer::Tracer` objects. For Rails applications, these can be placed in the provided initializer.
13
+ To attach tracers to methods, instantiate `MethodTracer::Tracer` objects and call `#enable`. For Rails applications, this can be done in the provided initializer.
14
+
15
+ The methods to be traced are specified by file pattern. You can optionally also specify a (partial) class name to speed up the matching process, which is recommended for performance. The comparison is a simple `String#include?`; no pattern matching is supported.
12
16
 
13
17
  ```ruby
14
- # Trace all methods defined by the system installation of gibbon:
15
- MethodTracer::Tracer.new('/var/lib/gems/2.3.0/gems/gibbon-2.2.4/')
18
+ # Trace all methods defined by the system installation of gibbon that also have "Gibbon" in the class name:
19
+ MethodTracer::Tracer.new(path: '/var/lib/gems/2.3.0/gems/gibbon-2.2.4/', name: 'Gibbon').enable
20
+
21
+ # Trace all methods defined by the system installation of gibbon, regardless of class name:
22
+ MethodTracer::Tracer.new(path: '/var/lib/gems/2.3.0/gems/gibbon-2.2.4/').enable
16
23
 
17
- # Trace all methods defined by rbenv's 2.3.3 installation of activerecord:
18
- MethodTracer::Tracer.new('/home/eddie/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/activerecord-5.0.7/')
24
+ # Trace all methods defined by rbenv's 2.3.3 installation of activerecord, regardless of class name:
25
+ MethodTracer::Tracer.new(path: '/home/eddie/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/activerecord-5.0.7/').enable
19
26
  ```
20
27
 
21
28
  With tracers attached, exercise as much of the application as possible. Perhaps run the comprehensive test suite that you of course have.
22
29
 
30
+ Using file paths makes it simple to trace an entire gem because it includes methods defined in the "namespace" of a different gem. For example, if a hypothetical gem `activerecord-extension` defines some methods on the class `ActiveRecord`, we still have the ability to trace only the methods from `activerecord-extension` without capturing the methods from `activerecord`.
31
+
23
32
  ## Configuration
24
33
 
25
34
  MethodTracer supports the following configuration options:
@@ -28,10 +37,6 @@ MethodTracer supports the following configuration options:
28
37
 
29
38
  `MethodTracer::Config.output_file`: A filename or `IO` or `StringIO` object where the report output should be sent. Defaults to `$stdout`.
30
39
 
31
- ## Limitations
32
-
33
- MethodTracer theoretically does not interfere with the original behavior of methods being traced. However, certain gems are known to misbehave when the tracing logic is monkeypatched in, and MethodTracer will refuse to trace those gems. Furthermore, any methods defined after the `MethodTracer::Tracer` is instantiated will remain untraced.
34
-
35
40
  ## Installation
36
41
 
37
42
  Add this line to your application's Gemfile:
@@ -8,6 +8,6 @@ MethodTracer::Config.app_path = Rails.application.paths.path.to_s
8
8
  # like `$stdout`.
9
9
  # MethodTracer::Config.output_file = 'output_file_2.log'
10
10
 
11
- # Create a MethodTracer::Spy object for every gem to be spied on
12
- # MethodTracer::Tracer.new('/var/lib/gems/2.3.0/gems/gibbon-2.2.4/lib/gibbon/')
13
- # MethodTracer::Tracer.new('/var/lib/gems/2.3.0/gems/other_gem-1.0.0/lib/other_gem-1.0.0/')
11
+ # Create a MethodTracer::Tracer object for every gem to be spied on
12
+ # MethodTracer::Tracer.new(path: '/var/lib/gems/2.3.0/gems/gibbon-2.2.4/', name: 'Gibbon').enable
13
+ # MethodTracer::Tracer.new(path: '/var/lib/gems/2.3.0/gems/other_gem-1.0.0/').enable
@@ -2,24 +2,26 @@ require 'where_is'
2
2
 
3
3
  module MethodTracer
4
4
  class Tracer
5
- def initialize(target)
6
- @target_path = target
5
+ def initialize(path:, name: nil)
6
+ @target_path = path
7
+ @target_name = name
8
+ end
7
9
 
10
+ def enable
8
11
  @tracer = TracePoint.trace(:call) do |tp|
9
12
  record_call_if_interesting(tp)
10
13
  end
11
14
  end
12
15
 
13
16
  def record_call_if_interesting(tp)
14
- return unless class_is_interesting?(tp.defined_class)
17
+ return unless method_is_interesting?(tp.defined_class, tp.method_id)
15
18
 
16
19
  locations = caller_locations.select { |loc| loc.path.start_with?(Config.app_path) }
17
20
  return if locations.empty?
18
21
 
19
- outfile.write "#{@target_path} :#{tp.method_id}\n"
20
- locations.each do |loc|
21
- outfile.write "#{loc.path}:#{loc.lineno}\n"
22
- end
22
+ outfile.write "#{tp.defined_class} :#{tp.method_id} "
23
+ outfile.write locations.map { |loc| "#{loc.path}:#{loc.lineno}" }.join('; ')
24
+ outfile.write "\n"
23
25
  end
24
26
 
25
27
  def outfile
@@ -35,12 +37,28 @@ module MethodTracer
35
37
  end
36
38
  end
37
39
 
38
- def class_is_interesting?(candidate_class)
40
+ def method_is_interesting?(candidate_class, method_id)
41
+ candidate_name = if candidate_class.instance_of?(Class)
42
+ candidate_class.name
43
+ else
44
+ candidate_class.class.name
45
+ end
46
+
47
+ # short circuit if possible for speed
48
+ return false if !@target_name.nil? &&
49
+ !candidate_name.nil? &&
50
+ !candidate_name.include?(@target_name)
51
+
52
+ method_is_defined_in_target_path(candidate_class, method_id)
53
+ end
54
+
55
+ def method_is_defined_in_target_path(candidate_class, method_id)
39
56
  begin
40
- location = Where.is(candidate_class)
57
+ location = Where.is(candidate_class, method_id)
41
58
  rescue NameError
42
59
  return false
43
60
  end
61
+
44
62
  location[:file].start_with?(@target_path)
45
63
  end
46
64
  end
@@ -1,3 +1,3 @@
1
1
  module MethodTracer
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
data/run_tests.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  if [ "$#" -ge 1 ]; then
4
- name_arg=--name "$*"
4
+ name_arg="--name $*"
5
5
  else
6
6
  name_arg=""
7
7
  fi
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: method_call_tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eddie Lebow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-25 00:00:00.000000000 Z
11
+ date: 2018-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: where_is
@@ -50,6 +50,7 @@ extra_rdoc_files: []
50
50
  files:
51
51
  - ".gitignore"
52
52
  - ".rubocop.yml"
53
+ - CHANGELOG.md
53
54
  - Gemfile
54
55
  - README.md
55
56
  - Rakefile