appmap 0.34.5 → 0.37.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 +4 -4
 - data/.gitignore +2 -1
 - data/.rubocop.yml +3 -0
 - data/CHANGELOG.md +24 -0
 - data/README.md +11 -8
 - data/Rakefile +2 -1
 - data/appmap.gemspec +2 -1
 - data/appmap.yml +1 -7
 - data/lib/appmap.rb +2 -2
 - data/lib/appmap/class_map.rb +22 -9
 - data/lib/appmap/config.rb +55 -25
 - data/lib/appmap/event.rb +28 -10
 - data/lib/appmap/hook.rb +9 -8
 - data/lib/appmap/hook/method.rb +10 -6
 - data/lib/appmap/rails/request_handler.rb +88 -0
 - data/lib/appmap/rails/sql_handler.rb +10 -2
 - data/lib/appmap/railtie.rb +4 -6
 - data/lib/appmap/rspec.rb +11 -1
 - data/lib/appmap/trace.rb +9 -7
 - data/lib/appmap/version.rb +2 -2
 - data/spec/abstract_controller4_base_spec.rb +1 -1
 - data/spec/abstract_controller_base_spec.rb +6 -0
 - data/spec/class_map_spec.rb +36 -0
 - data/spec/fixtures/hook/exception_method.rb +44 -0
 - data/spec/hook_spec.rb +145 -2
 - data/test/cli_test.rb +0 -10
 - data/test/expectations/openssl_test_key_sign1.json +55 -0
 - data/test/expectations/openssl_test_key_sign2.json +58 -0
 - data/test/fixtures/gem_test/Gemfile +6 -0
 - data/test/fixtures/gem_test/appmap.yml +3 -0
 - data/test/fixtures/gem_test/test/to_param_test.rb +14 -0
 - data/test/gem_test.rb +34 -0
 - data/test/minitest_test.rb +2 -2
 - data/test/openssl_test.rb +10 -165
 - metadata +30 -10
 - data/.ruby-version +0 -1
 - data/lib/appmap/rails/action_handler.rb +0 -91
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: fba24ecdb42c951741292378419eef430a56cc16816df46a14b2ae3e318f8f94
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 67ff27467f72322c5a8514c90b0a6039e3fd1268dba103dea3b832eda95ac317
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 1823304c2733b46470481fd731a1f4650a80e769d96b47dbcdb0e5763e59930d6754068b4cf0729f9f748c55109575f3c8146b09030741fccfafd06cd45669b9
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: bbebe31041ed1720dc54b9522e4c6baac3b27d8d77be0622315657d0fa5b355c3e12474aa756811a6d747b563999c6e76206805c1392a015f92c6b863e8eeda0
         
     | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # v0.37.0
         
     | 
| 
      
 2 
     | 
    
         
            +
            * Capture method source and comment.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # v0.36.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            * *appmap.yml* package definition may specify `gem`.
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Skip loading the railtie if `APPMAP_INITIALIZE` environment variable
         
     | 
| 
      
 7 
     | 
    
         
            +
              is set to `false`.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # v0.35.2
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Make sure `MethodEvent#display_string` works when the value's `#to_s` and/or `#inspect`
         
     | 
| 
      
 11 
     | 
    
         
            +
              methods have problems.
         
     | 
| 
      
 12 
     | 
    
         
            +
              
         
     | 
| 
      
 13 
     | 
    
         
            +
            # v0.35.1
         
     | 
| 
      
 14 
     | 
    
         
            +
            * Take out hooking of `IO` and `Logger` methods.
         
     | 
| 
      
 15 
     | 
    
         
            +
            * Enable logging if either `APPMAP_DEBUG` or `DEBUG` is `true`.
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            # v0.35.0
         
     | 
| 
      
 18 
     | 
    
         
            +
            * Provide a custom display string for files and HTTP requests.
         
     | 
| 
      
 19 
     | 
    
         
            +
            * Report `mime_type` on HTTP response.
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            # v0.34.6
         
     | 
| 
      
 22 
     | 
    
         
            +
            * Only warn once about problems determining database version for an ActiveRecord
         
     | 
| 
      
 23 
     | 
    
         
            +
              connection.
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       1 
25 
     | 
    
         
             
            # v0.34.5
         
     | 
| 
       2 
26 
     | 
    
         
             
            * Ensure that hooking a method doesn't change its arity.
         
     | 
| 
       3 
27 
     | 
    
         | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,3 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
       1 
2 
     | 
    
         
             
            - [About](#about)
         
     | 
| 
       2 
3 
     | 
    
         
             
            - [Installation](#installation)
         
     | 
| 
       3 
4 
     | 
    
         
             
            - [Configuration](#configuration)
         
     | 
| 
         @@ -13,7 +14,7 @@ 
     | 
|
| 
       13 
14 
     | 
    
         
             
              - [Using fixture apps](#using-fixture-apps)
         
     | 
| 
       14 
15 
     | 
    
         
             
                - [`test/fixtures`](#testfixtures)
         
     | 
| 
       15 
16 
     | 
    
         
             
                - [`spec/fixtures`](#specfixtures)
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       17 
18 
     | 
    
         | 
| 
       18 
19 
     | 
    
         
             
            # About
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
         @@ -27,8 +28,9 @@ granular than a full debug trace. It's designed to be optimal for understanding 
     | 
|
| 
       27 
28 
     | 
    
         
             
            There are several ways to record AppMaps of your Ruby program using the `appmap` gem:
         
     | 
| 
       28 
29 
     | 
    
         | 
| 
       29 
30 
     | 
    
         
             
            * Run your RSpec tests with the environment variable `APPMAP=true`. An AppMap will be generated for each spec.
         
     | 
| 
       30 
     | 
    
         
            -
            * Run your application server with AppMap remote recording enabled, and use the  
     | 
| 
       31 
     | 
    
         
            -
              browser extension to start, 
     | 
| 
      
 31 
     | 
    
         
            +
            * Run your application server with AppMap remote recording enabled, and use the [AppLand
         
     | 
| 
      
 32 
     | 
    
         
            +
              browser extension](https://github.com/applandinc/appland-browser-extension) to start,
         
     | 
| 
      
 33 
     | 
    
         
            +
              stop, and upload recordings.
         
     | 
| 
       32 
34 
     | 
    
         
             
            * Run the command `appmap record <program>` to record the entire execution of a program.
         
     | 
| 
       33 
35 
     | 
    
         | 
| 
       34 
36 
     | 
    
         
             
            Once you have recorded some AppMaps (for example, by running RSpec tests), you use the `appland upload` command
         
     | 
| 
         @@ -76,6 +78,7 @@ name: MyProject 
     | 
|
| 
       76 
78 
     | 
    
         
             
            packages:
         
     | 
| 
       77 
79 
     | 
    
         
             
            - path: app/controllers
         
     | 
| 
       78 
80 
     | 
    
         
             
            - path: app/models
         
     | 
| 
      
 81 
     | 
    
         
            +
            - gem: activerecord
         
     | 
| 
       79 
82 
     | 
    
         
             
            ```
         
     | 
| 
       80 
83 
     | 
    
         | 
| 
       81 
84 
     | 
    
         
             
            * **name** Provides the project name (required)
         
     | 
| 
         @@ -87,6 +90,7 @@ Each entry in the `packages` list is a YAML object which has the following keys: 
     | 
|
| 
       87 
90 
     | 
    
         | 
| 
       88 
91 
     | 
    
         
             
            * **path** The path to the source code directory. The path may be relative to the current working directory, or it may
         
     | 
| 
       89 
92 
     | 
    
         
             
              be an absolute path.
         
     | 
| 
      
 93 
     | 
    
         
            +
            * **gem** As an alternative to specifying the path, specify the name of a dependency gem. When using `gem`, don't specify `path`.
         
     | 
| 
       90 
94 
     | 
    
         
             
            * **exclude** A list of files and directories which will be ignored. By default, all modules, classes and public
         
     | 
| 
       91 
95 
     | 
    
         
             
              functions are inspected.
         
     | 
| 
       92 
96 
     | 
    
         | 
| 
         @@ -248,11 +252,11 @@ end 
     | 
|
| 
       248 
252 
     | 
    
         
             
            $ bundle exec rails server
         
     | 
| 
       249 
253 
     | 
    
         
             
            ```
         
     | 
| 
       250 
254 
     | 
    
         | 
| 
       251 
     | 
    
         
            -
            4. Open the  
     | 
| 
      
 255 
     | 
    
         
            +
            4. Open the AppLand browser extension and push `Start`.
         
     | 
| 
       252 
256 
     | 
    
         | 
| 
       253 
257 
     | 
    
         
             
            5. Use your app. For example, perform a login flow, or run through a manual UI test.
         
     | 
| 
       254 
258 
     | 
    
         | 
| 
       255 
     | 
    
         
            -
            6. Open the  
     | 
| 
      
 259 
     | 
    
         
            +
            6. Open the AppLand browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
         
     | 
| 
       256 
260 
     | 
    
         | 
| 
       257 
261 
     | 
    
         
             
            ## Ruby on Rails
         
     | 
| 
       258 
262 
     | 
    
         | 
| 
         @@ -265,6 +269,7 @@ Note that using this method is kind of a blunt instrument. Recording RSpecs and 
     | 
|
| 
       265 
269 
     | 
    
         
             
            For instructions on uploading, see the documentation of the [AppLand CLI](https://github.com/applandinc/appland-cli).
         
     | 
| 
       266 
270 
     | 
    
         | 
| 
       267 
271 
     | 
    
         
             
            # Development
         
     | 
| 
      
 272 
     | 
    
         
            +
            [](https://travis-ci.org/applandinc/appmap-ruby)
         
     | 
| 
       268 
273 
     | 
    
         | 
| 
       269 
274 
     | 
    
         
             
            ## Running tests
         
     | 
| 
       270 
275 
     | 
    
         | 
| 
         @@ -289,7 +294,7 @@ $ bundle exec rake compile 
     | 
|
| 
       289 
294 
     | 
    
         
             
            ### `test/fixtures`
         
     | 
| 
       290 
295 
     | 
    
         | 
| 
       291 
296 
     | 
    
         
             
            The fixture apps in `test/fixtures` are plain Ruby projects that exercise the basic functionality of the
         
     | 
| 
       292 
     | 
    
         
            -
            `appmap` gem. To develop in a fixture,  
     | 
| 
      
 297 
     | 
    
         
            +
            `appmap` gem. To develop in a fixture, simply enter the fixture directory and `bundle`.
         
     | 
| 
       293 
298 
     | 
    
         | 
| 
       294 
299 
     | 
    
         
             
            ### `spec/fixtures`
         
     | 
| 
       295 
300 
     | 
    
         | 
| 
         @@ -339,5 +344,3 @@ Finished in 0.07357 seconds (files took 2.1 seconds to load) 
     | 
|
| 
       339 
344 
     | 
    
         
             
            4 examples, 0 failures
         
     | 
| 
       340 
345 
     | 
    
         
             
            ```
         
     | 
| 
       341 
346 
     | 
    
         | 
| 
       342 
     | 
    
         
            -
            # Build status
         
     | 
| 
       343 
     | 
    
         
            -
            [](https://travis-ci.org/applandinc/appmap-ruby)
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -1,3 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $: << File.join(__dir__, 'lib')
         
     | 
| 
       1 
2 
     | 
    
         
             
            require 'appmap/version'
         
     | 
| 
       2 
3 
     | 
    
         
             
            GEM_VERSION = AppMap::VERSION
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
         @@ -113,7 +114,7 @@ namespace :spec do 
     | 
|
| 
       113 
114 
     | 
    
         
             
                desc ruby_version
         
     | 
| 
       114 
115 
     | 
    
         
             
                task ruby_version, [:specs] => ["compile", "build:fixtures:#{ruby_version}:all"] do |_, task_args|
         
     | 
| 
       115 
116 
     | 
    
         
             
                  run_specs(ruby_version, task_args)
         
     | 
| 
       116 
     | 
    
         
            -
                end.tap do|t|
         
     | 
| 
      
 117 
     | 
    
         
            +
                end.tap do |t|
         
     | 
| 
       117 
118 
     | 
    
         
             
                  desc "Run all specs"
         
     | 
| 
       118 
119 
     | 
    
         
             
                  task :all, [:specs] => t
         
     | 
| 
       119 
120 
     | 
    
         
             
                end
         
     | 
    
        data/appmap.gemspec
    CHANGED
    
    | 
         @@ -27,10 +27,11 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       27 
27 
     | 
    
         
             
              spec.add_dependency 'activesupport'
         
     | 
| 
       28 
28 
     | 
    
         
             
              spec.add_dependency 'faraday'
         
     | 
| 
       29 
29 
     | 
    
         
             
              spec.add_dependency 'gli'
         
     | 
| 
      
 30 
     | 
    
         
            +
              spec.add_dependency 'method_source'
         
     | 
| 
       30 
31 
     | 
    
         
             
              spec.add_dependency 'parser'
         
     | 
| 
       31 
32 
     | 
    
         
             
              spec.add_dependency 'rack'
         
     | 
| 
       32 
33 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
              spec.add_development_dependency 'bundler', ' 
     | 
| 
      
 34 
     | 
    
         
            +
              spec.add_development_dependency 'bundler', '>= 1.16'
         
     | 
| 
       34 
35 
     | 
    
         
             
              spec.add_development_dependency 'minitest', '~> 5.0'
         
     | 
| 
       35 
36 
     | 
    
         
             
              spec.add_development_dependency 'pry-byebug'
         
     | 
| 
       36 
37 
     | 
    
         
             
              spec.add_development_dependency 'rake', '>= 12.3.3'
         
     | 
    
        data/appmap.yml
    CHANGED
    
    
    
        data/lib/appmap.rb
    CHANGED
    
    | 
         @@ -83,8 +83,8 @@ module AppMap 
     | 
|
| 
       83 
83 
     | 
    
         
             
                end
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
85 
     | 
    
         
             
                # Builds a class map from a config and a list of Ruby methods.
         
     | 
| 
       86 
     | 
    
         
            -
                def class_map(methods)
         
     | 
| 
       87 
     | 
    
         
            -
                  ClassMap.build_from_methods( 
     | 
| 
      
 86 
     | 
    
         
            +
                def class_map(methods, options = {})
         
     | 
| 
      
 87 
     | 
    
         
            +
                  ClassMap.build_from_methods(methods, options)
         
     | 
| 
       88 
88 
     | 
    
         
             
                end
         
     | 
| 
       89 
89 
     | 
    
         | 
| 
       90 
90 
     | 
    
         
             
                # Returns default metadata detected from the Ruby system and from the
         
     | 
    
        data/lib/appmap/class_map.rb
    CHANGED
    
    | 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'method_source'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       3 
5 
     | 
    
         
             
            module AppMap
         
     | 
| 
       4 
6 
     | 
    
         
             
              class ClassMap
         
     | 
| 
       5 
7 
     | 
    
         
             
                module HasChildren
         
     | 
| 
         @@ -48,7 +50,7 @@ module AppMap 
     | 
|
| 
       48 
50 
     | 
    
         
             
                    end
         
     | 
| 
       49 
51 
     | 
    
         
             
                  end
         
     | 
| 
       50 
52 
     | 
    
         
             
                  Function = Struct.new(:name) do
         
     | 
| 
       51 
     | 
    
         
            -
                    attr_accessor :static, :location, :labels
         
     | 
| 
      
 53 
     | 
    
         
            +
                    attr_accessor :static, :location, :labels, :comment, :source
         
     | 
| 
       52 
54 
     | 
    
         | 
| 
       53 
55 
     | 
    
         
             
                    def type
         
     | 
| 
       54 
56 
     | 
    
         
             
                      'function'
         
     | 
| 
         @@ -60,31 +62,32 @@ module AppMap 
     | 
|
| 
       60 
62 
     | 
    
         
             
                        type: type,
         
     | 
| 
       61 
63 
     | 
    
         
             
                        location: location,
         
     | 
| 
       62 
64 
     | 
    
         
             
                        static: static,
         
     | 
| 
       63 
     | 
    
         
            -
                        labels: labels
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
      
 65 
     | 
    
         
            +
                        labels: labels,
         
     | 
| 
      
 66 
     | 
    
         
            +
                        comment: comment,
         
     | 
| 
      
 67 
     | 
    
         
            +
                        source: source
         
     | 
| 
      
 68 
     | 
    
         
            +
                      }.delete_if { |_, v| v.nil? || v == [] }
         
     | 
| 
       65 
69 
     | 
    
         
             
                    end
         
     | 
| 
       66 
70 
     | 
    
         
             
                  end
         
     | 
| 
       67 
71 
     | 
    
         
             
                end
         
     | 
| 
       68 
72 
     | 
    
         | 
| 
       69 
73 
     | 
    
         
             
                class << self
         
     | 
| 
       70 
     | 
    
         
            -
                  def build_from_methods( 
     | 
| 
      
 74 
     | 
    
         
            +
                  def build_from_methods(methods, options = {})
         
     | 
| 
       71 
75 
     | 
    
         
             
                    root = Types::Root.new
         
     | 
| 
       72 
76 
     | 
    
         
             
                    methods.each do |method|
         
     | 
| 
       73 
     | 
    
         
            -
                       
     | 
| 
       74 
     | 
    
         
            -
                        or raise "No package found for method #{method}"
         
     | 
| 
       75 
     | 
    
         
            -
                      add_function root, package, method
         
     | 
| 
      
 77 
     | 
    
         
            +
                      add_function root, method, options
         
     | 
| 
       76 
78 
     | 
    
         
             
                    end
         
     | 
| 
       77 
79 
     | 
    
         
             
                    root.children.map(&:to_h)
         
     | 
| 
       78 
80 
     | 
    
         
             
                  end
         
     | 
| 
       79 
81 
     | 
    
         | 
| 
       80 
82 
     | 
    
         
             
                  protected
         
     | 
| 
       81 
83 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
                  def add_function(root,  
     | 
| 
      
 84 
     | 
    
         
            +
                  def add_function(root, method, include_source: true)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    package = method.package
         
     | 
| 
       83 
86 
     | 
    
         
             
                    static = method.static
         
     | 
| 
       84 
87 
     | 
    
         | 
| 
       85 
88 
     | 
    
         
             
                    object_infos = [
         
     | 
| 
       86 
89 
     | 
    
         
             
                      {
         
     | 
| 
       87 
     | 
    
         
            -
                        name: package. 
     | 
| 
      
 90 
     | 
    
         
            +
                        name: package.name,
         
     | 
| 
       88 
91 
     | 
    
         
             
                        type: 'package'
         
     | 
| 
       89 
92 
     | 
    
         
             
                      }
         
     | 
| 
       90 
93 
     | 
    
         
             
                    ]
         
     | 
| 
         @@ -110,6 +113,16 @@ module AppMap 
     | 
|
| 
       110 
113 
     | 
    
         
             
                        [ method.defined_class, static ? '.' : '#', method.name ].join
         
     | 
| 
       111 
114 
     | 
    
         
             
                      end
         
     | 
| 
       112 
115 
     | 
    
         | 
| 
      
 116 
     | 
    
         
            +
                    if include_source
         
     | 
| 
      
 117 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 118 
     | 
    
         
            +
                        function_info[:source] = method.source
         
     | 
| 
      
 119 
     | 
    
         
            +
                        comment = method.comment || ''
         
     | 
| 
      
 120 
     | 
    
         
            +
                        function_info[:comment] = comment unless comment.empty?
         
     | 
| 
      
 121 
     | 
    
         
            +
                      rescue MethodSource::SourceNotFoundError
         
     | 
| 
      
 122 
     | 
    
         
            +
                        # pass
         
     | 
| 
      
 123 
     | 
    
         
            +
                      end
         
     | 
| 
      
 124 
     | 
    
         
            +
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
       113 
126 
     | 
    
         
             
                    function_info[:labels] = package.labels if package.labels
         
     | 
| 
       114 
127 
     | 
    
         
             
                    object_infos << function_info
         
     | 
| 
       115 
128 
     | 
    
         | 
    
        data/lib/appmap/config.rb
    CHANGED
    
    | 
         @@ -2,15 +2,39 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module AppMap
         
     | 
| 
       4 
4 
     | 
    
         
             
              class Config
         
     | 
| 
       5 
     | 
    
         
            -
                Package = Struct.new(:path, :package_name, :exclude, :labels) do
         
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
       7 
     | 
    
         
            -
                     
     | 
| 
      
 5 
     | 
    
         
            +
                Package = Struct.new(:path, :gem, :package_name, :exclude, :labels) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                    def build_from_path(path, package_name: nil, exclude: [], labels: [])
         
     | 
| 
      
 8 
     | 
    
         
            +
                      Package.new(path, nil, package_name, exclude, labels)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    def build_from_gem(gem, package_name: nil, exclude: [], labels: [])
         
     | 
| 
      
 12 
     | 
    
         
            +
                      gem_paths(gem).map do |gem_path|
         
     | 
| 
      
 13 
     | 
    
         
            +
                        Package.new(gem_path, gem, package_name, exclude, labels)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      end
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    private_class_method :new
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    protected
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    def gem_paths(gem)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      gemspec = Gem.loaded_specs[gem] or raise "Gem #{gem.inspect} not found"
         
     | 
| 
      
 23 
     | 
    
         
            +
                      gemspec.source_paths.map do |path|
         
     | 
| 
      
 24 
     | 
    
         
            +
                        File.join(gemspec.gem_dir, path)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def name
         
     | 
| 
      
 30 
     | 
    
         
            +
                    gem || path
         
     | 
| 
       8 
31 
     | 
    
         
             
                  end
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       10 
33 
     | 
    
         
             
                  def to_h
         
     | 
| 
       11 
34 
     | 
    
         
             
                    {
         
     | 
| 
       12 
35 
     | 
    
         
             
                      path: path,
         
     | 
| 
       13 
36 
     | 
    
         
             
                      package_name: package_name,
         
     | 
| 
      
 37 
     | 
    
         
            +
                      gem: gem,
         
     | 
| 
       14 
38 
     | 
    
         
             
                      exclude: exclude.blank? ? nil : exclude,
         
     | 
| 
       15 
39 
     | 
    
         
             
                      labels: labels.blank? ? nil : labels
         
     | 
| 
       16 
40 
     | 
    
         
             
                    }.compact
         
     | 
| 
         @@ -20,31 +44,29 @@ module AppMap 
     | 
|
| 
       20 
44 
     | 
    
         
             
                Hook = Struct.new(:method_names, :package) do
         
     | 
| 
       21 
45 
     | 
    
         
             
                end
         
     | 
| 
       22 
46 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
      
 47 
     | 
    
         
            +
                OPENSSL_PACKAGES = Package.build_from_path('openssl', package_name: 'openssl', labels: %w[security crypto])
         
     | 
| 
       24 
48 
     | 
    
         | 
| 
       25 
49 
     | 
    
         
             
                # Methods that should always be hooked, with their containing
         
     | 
| 
       26 
50 
     | 
    
         
             
                # package and labels that should be applied to them.
         
     | 
| 
       27 
51 
     | 
    
         
             
                HOOKED_METHODS = {
         
     | 
| 
       28 
     | 
    
         
            -
                  'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package. 
     | 
| 
      
 52 
     | 
    
         
            +
                  'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[security crypto]))
         
     | 
| 
       29 
53 
     | 
    
         
             
                }.freeze
         
     | 
| 
       30 
54 
     | 
    
         | 
| 
       31 
55 
     | 
    
         
             
                BUILTIN_METHODS = {
         
     | 
| 
       32 
     | 
    
         
            -
                  'OpenSSL::PKey::PKey' => Hook.new(:sign,  
     | 
| 
       33 
     | 
    
         
            -
                  'Digest::Instance' => Hook.new(:digest,  
     | 
| 
       34 
     | 
    
         
            -
                  'OpenSSL::X509::Request' => Hook.new(%i[sign verify],  
     | 
| 
       35 
     | 
    
         
            -
                  'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac],  
     | 
| 
       36 
     | 
    
         
            -
                  'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final],  
     | 
| 
       37 
     | 
    
         
            -
                  'OpenSSL::X509::Certificate' => Hook.new(:sign,  
     | 
| 
       38 
     | 
    
         
            -
                  ' 
     | 
| 
       39 
     | 
    
         
            -
                  'Net:: 
     | 
| 
       40 
     | 
    
         
            -
                  'Net:: 
     | 
| 
       41 
     | 
    
         
            -
                  'Net:: 
     | 
| 
       42 
     | 
    
         
            -
                  ' 
     | 
| 
       43 
     | 
    
         
            -
                  ' 
     | 
| 
       44 
     | 
    
         
            -
                  ' 
     | 
| 
       45 
     | 
    
         
            -
                  ' 
     | 
| 
       46 
     | 
    
         
            -
                  'JSON::Ext::Parser' => Hook.new(:parse, Package.new('json', package_name: 'json', labels: %w[serialization json])),
         
     | 
| 
       47 
     | 
    
         
            -
                  'JSON::Ext::Generator::State' => Hook.new(:generate, Package.new('json', package_name: 'json', labels: %w[serialization json]))
         
     | 
| 
      
 56 
     | 
    
         
            +
                  'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES),
         
     | 
| 
      
 57 
     | 
    
         
            +
                  'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGES),
         
     | 
| 
      
 58 
     | 
    
         
            +
                  'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES),
         
     | 
| 
      
 59 
     | 
    
         
            +
                  'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES),
         
     | 
| 
      
 60 
     | 
    
         
            +
                  'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGES),
         
     | 
| 
      
 61 
     | 
    
         
            +
                  'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES),
         
     | 
| 
      
 62 
     | 
    
         
            +
                  'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[http io])),
         
     | 
| 
      
 63 
     | 
    
         
            +
                  'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
         
     | 
| 
      
 64 
     | 
    
         
            +
                  'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[pop pop3 email io])),
         
     | 
| 
      
 65 
     | 
    
         
            +
                  'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
         
     | 
| 
      
 66 
     | 
    
         
            +
                  'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[serialization marshal])),
         
     | 
| 
      
 67 
     | 
    
         
            +
                  'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[serialization yaml])),
         
     | 
| 
      
 68 
     | 
    
         
            +
                  'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[serialization json])),
         
     | 
| 
      
 69 
     | 
    
         
            +
                  'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[serialization json]))
         
     | 
| 
       48 
70 
     | 
    
         
             
                }.freeze
         
     | 
| 
       49 
71 
     | 
    
         | 
| 
       50 
72 
     | 
    
         
             
                attr_reader :name, :packages
         
     | 
| 
         @@ -64,8 +86,16 @@ module AppMap 
     | 
|
| 
       64 
86 
     | 
    
         
             
                  # Loads configuration from a Hash.
         
     | 
| 
       65 
87 
     | 
    
         
             
                  def load(config_data)
         
     | 
| 
       66 
88 
     | 
    
         
             
                    packages = (config_data['packages'] || []).map do |package|
         
     | 
| 
       67 
     | 
    
         
            -
                       
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
      
 89 
     | 
    
         
            +
                      gem = package['gem']
         
     | 
| 
      
 90 
     | 
    
         
            +
                      path = package['path']
         
     | 
| 
      
 91 
     | 
    
         
            +
                      raise 'AppMap package configuration should specify gem or path, not both' if gem && path
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                      if gem
         
     | 
| 
      
 94 
     | 
    
         
            +
                        Package.build_from_gem(gem, exclude: package['exclude'] || [])
         
     | 
| 
      
 95 
     | 
    
         
            +
                      else
         
     | 
| 
      
 96 
     | 
    
         
            +
                        [ Package.build_from_path(path, exclude: package['exclude'] || []) ]
         
     | 
| 
      
 97 
     | 
    
         
            +
                      end
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end.flatten
         
     | 
| 
       69 
99 
     | 
    
         
             
                    Config.new config_data['name'], packages
         
     | 
| 
       70 
100 
     | 
    
         
             
                  end
         
     | 
| 
       71 
101 
     | 
    
         
             
                end
         
     | 
| 
         @@ -105,7 +135,7 @@ module AppMap 
     | 
|
| 
       105 
135 
     | 
    
         
             
                  hook = find_hook(defined_class)
         
     | 
| 
       106 
136 
     | 
    
         
             
                  return nil unless hook
         
     | 
| 
       107 
137 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                  Array(hook.method_names).include?(method_name) ? hook.package : nil 
     | 
| 
      
 138 
     | 
    
         
            +
                  Array(hook.method_names).include?(method_name) ? hook.package : nil
         
     | 
| 
       109 
139 
     | 
    
         
             
                end
         
     | 
| 
       110 
140 
     | 
    
         | 
| 
       111 
141 
     | 
    
         
             
                def find_hook(defined_class)
         
     | 
    
        data/lib/appmap/event.rb
    CHANGED
    
    | 
         @@ -31,25 +31,43 @@ module AppMap 
     | 
|
| 
       31 
31 
     | 
    
         
             
                    def display_string(value)
         
     | 
| 
       32 
32 
     | 
    
         
             
                      return nil unless value
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
                      value_string = custom_display_string(value) || default_display_string(value)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                      (value_string||'')[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    protected
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    def custom_display_string(value)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      case value
         
     | 
| 
      
 43 
     | 
    
         
            +
                      when File
         
     | 
| 
      
 44 
     | 
    
         
            +
                        "#{value.class}[path=#{value.path}]"
         
     | 
| 
      
 45 
     | 
    
         
            +
                      when Net::HTTP
         
     | 
| 
      
 46 
     | 
    
         
            +
                        "#{value.class}[#{value.address}:#{value.port}]"
         
     | 
| 
      
 47 
     | 
    
         
            +
                      when Net::HTTPGenericRequest
         
     | 
| 
      
 48 
     | 
    
         
            +
                        "#{value.class}[#{value.method} #{value.path}]"
         
     | 
| 
      
 49 
     | 
    
         
            +
                      end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    rescue StandardError
         
     | 
| 
      
 51 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    def default_display_string(value)
         
     | 
| 
       34 
55 
     | 
    
         
             
                      last_resort_string = lambda do
         
     | 
| 
       35 
56 
     | 
    
         
             
                        warn "AppMap encountered an error inspecting a #{value.class.name}: #{$!.message}"
         
     | 
| 
       36 
57 
     | 
    
         
             
                        '*Error inspecting variable*'
         
     | 
| 
       37 
58 
     | 
    
         
             
                      end
         
     | 
| 
       38 
59 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
                       
     | 
| 
      
 60 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 61 
     | 
    
         
            +
                        value.to_s
         
     | 
| 
      
 62 
     | 
    
         
            +
                      rescue NoMethodError
         
     | 
| 
       40 
63 
     | 
    
         
             
                        begin
         
     | 
| 
       41 
     | 
    
         
            -
                          value. 
     | 
| 
       42 
     | 
    
         
            -
                        rescue NoMethodError
         
     | 
| 
       43 
     | 
    
         
            -
                          begin
         
     | 
| 
       44 
     | 
    
         
            -
                            value.inspect
         
     | 
| 
       45 
     | 
    
         
            -
                          rescue StandardError
         
     | 
| 
       46 
     | 
    
         
            -
                            last_resort_string.call
         
     | 
| 
       47 
     | 
    
         
            -
                          end
         
     | 
| 
      
 64 
     | 
    
         
            +
                          value.inspect
         
     | 
| 
       48 
65 
     | 
    
         
             
                        rescue StandardError
         
     | 
| 
       49 
66 
     | 
    
         
             
                          last_resort_string.call
         
     | 
| 
       50 
67 
     | 
    
         
             
                        end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 68 
     | 
    
         
            +
                      rescue StandardError
         
     | 
| 
      
 69 
     | 
    
         
            +
                        last_resort_string.call
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
       53 
71 
     | 
    
         
             
                    end
         
     | 
| 
       54 
72 
     | 
    
         
             
                  end
         
     | 
| 
       55 
73 
     | 
    
         
             
                end
         
     | 
    
        data/lib/appmap/hook.rb
    CHANGED
    
    | 
         @@ -4,7 +4,7 @@ require 'English' 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module AppMap
         
     | 
| 
       6 
6 
     | 
    
         
             
              class Hook
         
     | 
| 
       7 
     | 
    
         
            -
                LOG = (ENV['DEBUG'] == 'true')
         
     | 
| 
      
 7 
     | 
    
         
            +
                LOG = (ENV['APPMAP_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                @unbound_method_arity = ::UnboundMethod.instance_method(:arity)
         
     | 
| 
       10 
10 
     | 
    
         
             
                @method_arity = ::Method.instance_method(:arity)
         
     | 
| 
         @@ -48,7 +48,6 @@ module AppMap 
     | 
|
| 
       48 
48 
     | 
    
         
             
                    hook = lambda do |hook_cls|
         
     | 
| 
       49 
49 
     | 
    
         
             
                      lambda do |method_id|
         
     | 
| 
       50 
50 
     | 
    
         
             
                        method = hook_cls.public_instance_method(method_id)
         
     | 
| 
       51 
     | 
    
         
            -
                        hook_method = Hook::Method.new(hook_cls, method)
         
     | 
| 
       52 
51 
     | 
    
         | 
| 
       53 
52 
     | 
    
         
             
                        warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
         
     | 
| 
       54 
53 
     | 
    
         | 
| 
         @@ -56,14 +55,16 @@ module AppMap 
     | 
|
| 
       56 
55 
     | 
    
         
             
                        # Skip methods that have no instruction sequence, as they are obviously trivial.
         
     | 
| 
       57 
56 
     | 
    
         
             
                        next unless disasm
         
     | 
| 
       58 
57 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                        # Don't try and trace the AppMap methods or there will be
         
     | 
| 
       60 
     | 
    
         
            -
                        # a stack overflow in the defined hook method.
         
     | 
| 
       61 
     | 
    
         
            -
                        next if /\AAppMap[:\.]/.match?(hook_method.method_display_name)
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
58 
     | 
    
         
             
                        next unless \
         
     | 
| 
       64 
59 
     | 
    
         
             
                          config.always_hook?(hook_cls, method.name) ||
         
     | 
| 
       65 
60 
     | 
    
         
             
                          config.included_by_location?(method)
         
     | 
| 
       66 
61 
     | 
    
         | 
| 
      
 62 
     | 
    
         
            +
                        hook_method = Hook::Method.new(config.package_for_method(method), hook_cls, method)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                        # Don't try and trace the AppMap methods or there will be
         
     | 
| 
      
 65 
     | 
    
         
            +
                        # a stack overflow in the defined hook method.
         
     | 
| 
      
 66 
     | 
    
         
            +
                        next if /\AAppMap[:\.]/.match?(hook_method.method_display_name)
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
       67 
68 
     | 
    
         
             
                        hook_method.activate
         
     | 
| 
       68 
69 
     | 
    
         
             
                      end
         
     | 
| 
       69 
70 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -97,9 +98,9 @@ module AppMap 
     | 
|
| 
       97 
98 
     | 
    
         
             
                        end
         
     | 
| 
       98 
99 
     | 
    
         | 
| 
       99 
100 
     | 
    
         
             
                      if method
         
     | 
| 
       100 
     | 
    
         
            -
                        Hook::Method.new(cls, method).activate
         
     | 
| 
      
 101 
     | 
    
         
            +
                        Hook::Method.new(hook.package, cls, method).activate
         
     | 
| 
       101 
102 
     | 
    
         
             
                      else
         
     | 
| 
       102 
     | 
    
         
            -
                        warn "Method #{method_name} not found on #{cls.name}" 
     | 
| 
      
 103 
     | 
    
         
            +
                        warn "Method #{method_name} not found on #{cls.name}"
         
     | 
| 
       103 
104 
     | 
    
         
             
                      end
         
     | 
| 
       104 
105 
     | 
    
         
             
                    end
         
     | 
| 
       105 
106 
     | 
    
         
             
                  end
         
     |