From 7abed09e8afa1b42bf357dee8f175ab7e7f4cec9 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Thu, 21 Oct 2021 17:00:41 -0400 Subject: [PATCH] feat: support --gen-suppressions When valgrind is run with `--gen-suppressions`, it will write the error's suppression to XML as something like: insert_a_suppression_name_here Memcheck:Leak match-leak-kinds: definite malloc objspace_xmalloc0 ruby_xmalloc0 ruby_xmalloc_body ruby_xmalloc If this data is present, then ValgrindError will stream it in the right format for copy-pasting into a suppressions file. See https://wiki.wxwidgets.org/Valgrind_Suppression_File_Howto for more. --- lib/ruby_memcheck.rb | 1 + lib/ruby_memcheck/suppression.rb | 25 +++++++++ lib/ruby_memcheck/valgrind_error.rb | 4 +- .../ruby_memcheck_suppression_test.rb | 53 +++++++++++++++++++ test/ruby_memcheck/ruby_memcheck_test.rb | 22 ++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 lib/ruby_memcheck/suppression.rb create mode 100644 test/ruby_memcheck/ruby_memcheck_suppression_test.rb diff --git a/lib/ruby_memcheck.rb b/lib/ruby_memcheck.rb index a180efb..4edadfd 100644 --- a/lib/ruby_memcheck.rb +++ b/lib/ruby_memcheck.rb @@ -8,6 +8,7 @@ require "ruby_memcheck/stack" require "ruby_memcheck/test_task" require "ruby_memcheck/valgrind_error" +require "ruby_memcheck/suppression" require "ruby_memcheck/version" module RubyMemcheck diff --git a/lib/ruby_memcheck/suppression.rb b/lib/ruby_memcheck/suppression.rb new file mode 100644 index 0000000..42a2760 --- /dev/null +++ b/lib/ruby_memcheck/suppression.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module RubyMemcheck + class Suppression + attr_reader :root + + def initialize(configuration, suppression_node) + @root = suppression_node + end + + def to_s + return "" if root.nil? + + str = StringIO.new + str << "{\n" + str << " #{root.at_xpath("sname").content}\n" + str << " #{root.at_xpath("skind").content}\n" + root.xpath("./sframe/fun | ./sframe/obj").each do |frame| + str << " #{frame.name}:#{frame.content}\n" + end + str << "}\n" + str.string + end + end +end diff --git a/lib/ruby_memcheck/valgrind_error.rb b/lib/ruby_memcheck/valgrind_error.rb index 198b103..a1dc275 100644 --- a/lib/ruby_memcheck/valgrind_error.rb +++ b/lib/ruby_memcheck/valgrind_error.rb @@ -2,7 +2,7 @@ module RubyMemcheck class ValgrindError - attr_reader :kind, :msg, :stack + attr_reader :kind, :msg, :stack, :suppression def initialize(configuration, error) @kind = error.at_xpath("kind").content @@ -14,6 +14,7 @@ def initialize(configuration, error) end @stack = Stack.new(configuration, error.at_xpath("stack")) @configuration = configuration + @suppression = Suppression.new(configuration, error.at_xpath("suppression")) end def skip? @@ -34,6 +35,7 @@ def to_s " #{frame}\n" end end + str << suppression.to_s str.string end diff --git a/test/ruby_memcheck/ruby_memcheck_suppression_test.rb b/test/ruby_memcheck/ruby_memcheck_suppression_test.rb new file mode 100644 index 0000000..f8bb9b0 --- /dev/null +++ b/test/ruby_memcheck/ruby_memcheck_suppression_test.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require "test_helper" +require "nokogiri" + +module RubyMemcheck + class RubyMemcheckSuppressionTest < Minitest::Test + def setup + @configuration = Configuration.new( + binary_name: "ruby_memcheck_c_test", + output_io: @output_io, + ) + end + + def test_given_nil + assert_equal("", RubyMemcheck::Suppression.new(@configuration, nil).to_s) + end + + def test_given_a_suppression_node + suppression = ::Nokogiri::XML(<<~EOF).at_xpath("//suppression") + + + insert_a_suppression_name_here + Memcheck:Leak + match-leak-kinds: definite + malloc + objspace_xmalloc0 + ruby_xmalloc0 + /usr/lib/libX11.so.6.3.0 + ruby_xmalloc_body + ruby_xmalloc + + + EOF + expected = <<~EOF + { + insert_a_suppression_name_here + Memcheck:Leak + fun:malloc + fun:objspace_xmalloc0 + fun:ruby_xmalloc0 + obj:/usr/lib/libX11.so.6.3.0 + fun:ruby_xmalloc_body + fun:ruby_xmalloc + } + EOF + assert_equal( + expected, + RubyMemcheck::Suppression.new(@configuration, suppression).to_s, + ) + end + end +end diff --git a/test/ruby_memcheck/ruby_memcheck_test.rb b/test/ruby_memcheck/ruby_memcheck_test.rb index eabe1f3..aa7ab2a 100644 --- a/test/ruby_memcheck/ruby_memcheck_test.rb +++ b/test/ruby_memcheck/ruby_memcheck_test.rb @@ -97,6 +97,28 @@ def test_suppressions assert_empty(@output_io.string) end + def test_generation_of_suppressions + valgrind_options = RubyMemcheck::Configuration::DEFAULT_VALGRIND_OPTIONS.dup + valgrind_options << "--gen-suppressions=all" + build_configuration(valgrind_options: valgrind_options) + + assert_raises(RubyMemcheck::TestTask::VALGRIND_REPORT_MSG) do + run_with_memcheck(<<~RUBY) + RubyMemcheck::CTest.new.memory_leak + RUBY + end + + assert_equal(1, @test_task.errors.length) + + output = @output_io.string + refute_empty(output) + assert_match(/^100 bytes in 1 blocks are definitely lost in loss record/, output) + assert_match(/^ \*memory_leak \(ruby_memcheck_c_test\.c:\d+\)$/, output) + assert_match(/^ insert_a_suppression_name_here/, output) + assert_match(/^ Memcheck:Leak/, output) + assert_match(/^ fun:allocate_memory_leak/, output) + end + def test_reports_multiple_errors assert_raises(RubyMemcheck::TestTask::VALGRIND_REPORT_MSG) do run_with_memcheck(<<~RUBY)