ruby_memcheck 1.0.2 → 1.1.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/.github/workflows/test.yml +1 -1
 - data/.rubocop.yml +3 -0
 - data/README.md +20 -4
 - data/lib/ruby_memcheck/configuration.rb +15 -4
 - data/lib/ruby_memcheck/test_task_reporter.rb +2 -0
 - data/lib/ruby_memcheck/version.rb +1 -1
 - data/suppressions/ruby.supp +21 -0
 - metadata +4 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 4cd707ffa4ebdb25945d2d2654659a38c2272a0e73678d9c9f58e655e19b3cc6
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: df247c2a1e77374ad3c9521b1a3f2554c443fa80e33d50cd948727821a3267d5
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7b4c337a38ad04be648365f4de828187524f11f67699aaa187182d906e4dbf922d84767003e4bab79572c0b3c41fe908c2bb23b1029b1d49360a9c399df5010f
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3c73f128a88b0eabce497b0ebb464166ccc541dbf5ec0e03b1951d8e1569abd6123a48a454b6a2e9b808c95770ea922b6e027db8c2be2a458d5f0b53f3cb74bc
         
     | 
    
        data/.github/workflows/test.yml
    CHANGED
    
    | 
         @@ -6,9 +6,9 @@ jobs: 
     | 
|
| 
       6 
6 
     | 
    
         
             
                strategy:
         
     | 
| 
       7 
7 
     | 
    
         
             
                  matrix:
         
     | 
| 
       8 
8 
     | 
    
         
             
                    entry:
         
     | 
| 
       9 
     | 
    
         
            -
                      - { ruby: '2.6', allowed-failure: false }
         
     | 
| 
       10 
9 
     | 
    
         
             
                      - { ruby: '2.7', allowed-failure: false }
         
     | 
| 
       11 
10 
     | 
    
         
             
                      - { ruby: '3.0', allowed-failure: false }
         
     | 
| 
      
 11 
     | 
    
         
            +
                      - { ruby: '3.1', allowed-failure: false }
         
     | 
| 
       12 
12 
     | 
    
         
             
                      - { ruby: ruby-head, allowed-failure: false }
         
     | 
| 
       13 
13 
     | 
    
         
             
                name: ruby ${{ matrix.entry.ruby }}
         
     | 
| 
       14 
14 
     | 
    
         
             
                steps:
         
     | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -26,16 +26,19 @@ Only gems with native extensions can use this gem. If your gem is written in pla 
     | 
|
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
            This gem runs Valgrind with the `--xml` option to generate an XML of all the errors. It will then parse the XML and use various heuristics based on the type of the error and the stack trace to filter out errors that are false positives.
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
            For more details, read [this blog post](https://blog.peterzhu.ca/ruby-memcheck/).
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       29 
31 
     | 
    
         
             
            ### Limitations
         
     | 
| 
       30 
32 
     | 
    
         | 
| 
       31 
33 
     | 
    
         
             
            Because of the aggressive heuristics used to filter out false positives, there are various limitations of what this gem can detect.
         
     | 
| 
       32 
34 
     | 
    
         | 
| 
       33 
35 
     | 
    
         
             
            1. This gem is only expected to work on Linux.
         
     | 
| 
      
 36 
     | 
    
         
            +
            1. This gem runs your gem's test suite to find errors and memory leaks. It will only be able to report errors on code paths that are covered by your tests. So make sure your test suite has good coverage!
         
     | 
| 
       34 
37 
     | 
    
         
             
            1. It will not find memory leaks in Ruby. It filters out everything in Ruby.
         
     | 
| 
       35 
38 
     | 
    
         
             
            1. It will not find memory leaks of allocations that occurred in Ruby (even if the memory leak is caused by your native extension).
         
     | 
| 
       36 
39 
     | 
    
         | 
| 
       37 
40 
     | 
    
         
             
                An example of this is if a string is allocated in Ruby, passed into your native extension, you change the pointer of the string without freeing the contents, so the contents of the string becomes leaked.
         
     | 
| 
       38 
     | 
    
         
            -
            1. To filter out false 
     | 
| 
      
 41 
     | 
    
         
            +
            1. To filter out false positives, it will only find definite leaks (i.e. memory regions with no pointers to it). It will not find possible leaks (i.e. memory regions with pointers to it).
         
     | 
| 
       39 
42 
     | 
    
         
             
            1. It will not find leaks that occur in the `Init` function of your native extension.
         
     | 
| 
       40 
43 
     | 
    
         
             
            1. It will not find uses of undefined values (e.g. conditional jumps depending on undefined values). This is just a technical limitation that has not been solved yet (contributions welcome!).
         
     | 
| 
       41 
44 
     | 
    
         | 
| 
         @@ -67,6 +70,7 @@ The easiest way to use this gem is to use it on your test suite (minitest or RSp 
     | 
|
| 
       67 
70 
     | 
    
         
             
                ```ruby
         
     | 
| 
       68 
71 
     | 
    
         
             
                RubyMemcheck.config(binary_name: "your_binary_name")
         
     | 
| 
       69 
72 
     | 
    
         
             
                ```
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
       70 
74 
     | 
    
         
             
            1. Setup the test task for your test framework.
         
     | 
| 
       71 
75 
     | 
    
         
             
                - **minitest**
         
     | 
| 
       72 
76 
     | 
    
         | 
| 
         @@ -105,18 +109,30 @@ The easiest way to use this gem is to use it on your test suite (minitest or RSp 
     | 
|
| 
       105 
109 
     | 
    
         
             
                  For example, if your Rakefile looked like this before:
         
     | 
| 
       106 
110 
     | 
    
         | 
| 
       107 
111 
     | 
    
         
             
                  ```ruby
         
     | 
| 
       108 
     | 
    
         
            -
                   
     | 
| 
      
 112 
     | 
    
         
            +
                  RSpec::Core::RakeTask.new(spec: :compile)
         
     | 
| 
       109 
113 
     | 
    
         
             
                  ```
         
     | 
| 
       110 
114 
     | 
    
         | 
| 
       111 
115 
     | 
    
         
             
                  You can change it to look like this:
         
     | 
| 
       112 
116 
     | 
    
         | 
| 
       113 
117 
     | 
    
         
             
                  ```ruby
         
     | 
| 
       114 
     | 
    
         
            -
                   
     | 
| 
      
 118 
     | 
    
         
            +
                  RSpec::Core::RakeTask.new(spec: :compile)
         
     | 
| 
       115 
119 
     | 
    
         
             
                  namespace :spec do
         
     | 
| 
       116 
120 
     | 
    
         
             
                    RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
         
     | 
| 
       117 
121 
     | 
    
         
             
                  end
         
     | 
| 
       118 
122 
     | 
    
         
             
                  ```
         
     | 
| 
       119 
123 
     | 
    
         | 
| 
      
 124 
     | 
    
         
            +
            1. Add the following line to your `test_helper.rb` or `spec_helper.rb`:
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                ```ruby
         
     | 
| 
      
 127 
     | 
    
         
            +
                at_exit { GC.start }
         
     | 
| 
      
 128 
     | 
    
         
            +
                ```
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                This will run a major garbage collection cycle before the Ruby process shuts down. This will ensure that any remaining Ruby objects are collected which will prevent Valgrind from reporting false positives.
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                It is safest to add the line above at the very first line of `test_helper.rb` or `spec_helper.rb`.
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                - **For minitest:** It is important that the line above is added BEFORE the `require "minitest/autorun"` line.
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
       120 
136 
     | 
    
         
             
            1. You're ready to run your test suite with Valgrind using `rake test:valgrind` or `rake spec:valgrind`! Note that this will take a while to run because Valgrind will make Ruby significantly slower.
         
     | 
| 
       121 
137 
     | 
    
         
             
            1. (Optional) If you find false positives in the output, you can create Valgrind suppression files. See the [`Suppression files`](#suppression-files) section for more details.
         
     | 
| 
       122 
138 
     | 
    
         | 
| 
         @@ -140,7 +156,7 @@ When you run `RubyMemcheck.config`, you are creating a default `RubyMemcheck::Co 
     | 
|
| 
       140 
156 
     | 
    
         | 
| 
       141 
157 
     | 
    
         
             
            If you find false positives in the output, you can create suppression files in a `suppressions` directory in the root directory of your gem. In this directory, you can create [Valgrind suppression files](https://wiki.wxwidgets.org/Valgrind_Suppression_File_Howto).
         
     | 
| 
       142 
158 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
            The most basic suppression file is ` 
     | 
| 
      
 159 
     | 
    
         
            +
            The most basic suppression file is `ruby.supp`. If you want some suppressions for only specific versions of Ruby, you can add the Ruby version to the filename. For example, `ruby-3.supp` will suppress for any Rubies with a major version of 3 (e.g. 3.0.0, 3.1.1, etc.), while suppression file `ruby-3.1.supp` will only be used for Ruby with a major and minor version of 3.1 (e.g. 3.1.0, 3.1.1, etc.).
         
     | 
| 
       144 
160 
     | 
    
         | 
| 
       145 
161 
     | 
    
         
             
            ## Success stories
         
     | 
| 
       146 
162 
     | 
    
         | 
| 
         @@ -31,7 +31,7 @@ module RubyMemcheck 
     | 
|
| 
       31 
31 
     | 
    
         
             
                  /\Arb_yield/,
         
     | 
| 
       32 
32 
     | 
    
         
             
                ].freeze
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
                attr_reader :binary_name, :ruby, :valgrind, :valgrind_options, : 
     | 
| 
      
 34 
     | 
    
         
            +
                attr_reader :binary_name, :ruby, :valgrind, :valgrind_options, :valgrind_suppression_files,
         
     | 
| 
       35 
35 
     | 
    
         
             
                  :valgrind_generate_suppressions, :skipped_ruby_functions, :valgrind_xml_dir, :output_io
         
     | 
| 
       36 
36 
     | 
    
         
             
                alias_method :valgrind_generate_suppressions?, :valgrind_generate_suppressions
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
         @@ -50,7 +50,9 @@ module RubyMemcheck 
     | 
|
| 
       50 
50 
     | 
    
         
             
                  @ruby = ruby
         
     | 
| 
       51 
51 
     | 
    
         
             
                  @valgrind = valgrind
         
     | 
| 
       52 
52 
     | 
    
         
             
                  @valgrind_options = valgrind_options
         
     | 
| 
       53 
     | 
    
         
            -
                  @ 
     | 
| 
      
 53 
     | 
    
         
            +
                  @valgrind_suppression_files =
         
     | 
| 
      
 54 
     | 
    
         
            +
                    get_valgrind_suppression_files(File.join(__dir__, "../../suppressions")) +
         
     | 
| 
      
 55 
     | 
    
         
            +
                    get_valgrind_suppression_files(valgrind_suppressions_dir)
         
     | 
| 
       54 
56 
     | 
    
         
             
                  @valgrind_generate_suppressions = valgrind_generate_suppressions
         
     | 
| 
       55 
57 
     | 
    
         
             
                  @skipped_ruby_functions = skipped_ruby_functions
         
     | 
| 
       56 
58 
     | 
    
         
             
                  @output_io = output_io
         
     | 
| 
         @@ -80,7 +82,7 @@ module RubyMemcheck 
     | 
|
| 
       80 
82 
     | 
    
         
             
                    "ulimit -s unlimited && ",
         
     | 
| 
       81 
83 
     | 
    
         
             
                    valgrind,
         
     | 
| 
       82 
84 
     | 
    
         
             
                    valgrind_options,
         
     | 
| 
       83 
     | 
    
         
            -
                     
     | 
| 
      
 85 
     | 
    
         
            +
                    valgrind_suppression_files.map { |f| "--suppressions=#{f}" },
         
     | 
| 
       84 
86 
     | 
    
         
             
                    valgrind_generate_suppressions ? "--gen-suppressions=all" : "",
         
     | 
| 
       85 
87 
     | 
    
         
             
                    ruby,
         
     | 
| 
       86 
88 
     | 
    
         
             
                    args,
         
     | 
| 
         @@ -90,13 +92,22 @@ module RubyMemcheck 
     | 
|
| 
       90 
92 
     | 
    
         
             
                private
         
     | 
| 
       91 
93 
     | 
    
         | 
| 
       92 
94 
     | 
    
         
             
                def get_valgrind_suppression_files(dir)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  dir = File.expand_path(dir)
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
       93 
97 
     | 
    
         
             
                  full_ruby_version = "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
         
     | 
| 
       94 
98 
     | 
    
         
             
                  versions = [full_ruby_version]
         
     | 
| 
       95 
99 
     | 
    
         
             
                  (0..3).reverse_each { |i| versions << full_ruby_version.split(".")[0, i].join(".") }
         
     | 
| 
       96 
100 
     | 
    
         
             
                  versions << RUBY_ENGINE
         
     | 
| 
       97 
101 
     | 
    
         | 
| 
       98 
102 
     | 
    
         
             
                  versions.map do |version|
         
     | 
| 
       99 
     | 
    
         
            -
                    Dir[File.join(dir, "#{binary_name}_#{version}.supp")]
         
     | 
| 
      
 103 
     | 
    
         
            +
                    old_format_files = Dir[File.join(dir, "#{binary_name}_#{version}.supp")]
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    unless old_format_files.empty?
         
     | 
| 
      
 106 
     | 
    
         
            +
                      warn("ruby_memcheck: please migrate your suppression filenames from " \
         
     | 
| 
      
 107 
     | 
    
         
            +
                        "`gem_name_ruby-3.1.0.supp` to `ruby-3.1.0.supp` (drop the gem name from the filename)")
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    old_format_files + Dir[File.join(dir, "#{version}.supp")]
         
     | 
| 
       100 
111 
     | 
    
         
             
                  end.flatten
         
     | 
| 
       101 
112 
     | 
    
         
             
                end
         
     | 
| 
       102 
113 
     | 
    
         
             
              end
         
     | 
| 
         @@ -33,9 +33,11 @@ module RubyMemcheck 
     | 
|
| 
       33 
33 
     | 
    
         
             
                  xml_files.each do |file|
         
     | 
| 
       34 
34 
     | 
    
         
             
                    Nokogiri::XML::Reader(File.open(file)).each do |node|
         
     | 
| 
       35 
35 
     | 
    
         
             
                      next unless node.name == "error" && node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       36 
37 
     | 
    
         
             
                      error_xml = Nokogiri::XML::Document.parse(node.outer_xml).root
         
     | 
| 
       37 
38 
     | 
    
         
             
                      error = ValgrindError.new(configuration, error_xml)
         
     | 
| 
       38 
39 
     | 
    
         
             
                      next if error.skip?
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       39 
41 
     | 
    
         
             
                      @errors << error
         
     | 
| 
       40 
42 
     | 
    
         
             
                    end
         
     | 
| 
       41 
43 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {
         
     | 
| 
      
 2 
     | 
    
         
            +
              On platforms where memcpy is safe for overlapped memory, the compiler will sometimes replace memmove with memcpy. Valgrind may report a false positive.
         
     | 
| 
      
 3 
     | 
    
         
            +
              Memcheck:Overlap
         
     | 
| 
      
 4 
     | 
    
         
            +
              fun:__memcpy_chk
         
     | 
| 
      
 5 
     | 
    
         
            +
              fun:memmove
         
     | 
| 
      
 6 
     | 
    
         
            +
              ...
         
     | 
| 
      
 7 
     | 
    
         
            +
            }
         
     | 
| 
      
 8 
     | 
    
         
            +
            {
         
     | 
| 
      
 9 
     | 
    
         
            +
              Requiring a file will add it to the loaded features, which may be reported as a leak.
         
     | 
| 
      
 10 
     | 
    
         
            +
              Memcheck:Leak
         
     | 
| 
      
 11 
     | 
    
         
            +
              ...
         
     | 
| 
      
 12 
     | 
    
         
            +
              fun:require_internal
         
     | 
| 
      
 13 
     | 
    
         
            +
              ...
         
     | 
| 
      
 14 
     | 
    
         
            +
            }
         
     | 
| 
      
 15 
     | 
    
         
            +
            {
         
     | 
| 
      
 16 
     | 
    
         
            +
              Remove this after Ruby 2.7.7, 3.0.5, 3.1.3 are relased. See: https://github.com/Shopify/ruby_memcheck/issues/6
         
     | 
| 
      
 17 
     | 
    
         
            +
              Memcheck:Leak
         
     | 
| 
      
 18 
     | 
    
         
            +
              ...
         
     | 
| 
      
 19 
     | 
    
         
            +
              fun:stack_chunk_alloc
         
     | 
| 
      
 20 
     | 
    
         
            +
              ...
         
     | 
| 
      
 21 
     | 
    
         
            +
            }
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: ruby_memcheck
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.0 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.1.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Peter Zhu
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-10-19 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: nokogiri
         
     | 
| 
         @@ -148,6 +148,7 @@ files: 
     | 
|
| 
       148 
148 
     | 
    
         
             
            - lib/ruby_memcheck/valgrind_error.rb
         
     | 
| 
       149 
149 
     | 
    
         
             
            - lib/ruby_memcheck/version.rb
         
     | 
| 
       150 
150 
     | 
    
         
             
            - ruby_memcheck.gemspec
         
     | 
| 
      
 151 
     | 
    
         
            +
            - suppressions/ruby.supp
         
     | 
| 
       151 
152 
     | 
    
         
             
            homepage: https://github.com/peterzhu2118/ruby_memcheck
         
     | 
| 
       152 
153 
     | 
    
         
             
            licenses:
         
     | 
| 
       153 
154 
     | 
    
         
             
            - MIT
         
     | 
| 
         @@ -168,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       168 
169 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       169 
170 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       170 
171 
     | 
    
         
             
            requirements: []
         
     | 
| 
       171 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 172 
     | 
    
         
            +
            rubygems_version: 3.3.7
         
     | 
| 
       172 
173 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       173 
174 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       174 
175 
     | 
    
         
             
            summary: Use Valgrind memcheck without going crazy
         
     |