mwrap 1.0.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.
@@ -0,0 +1,30 @@
1
+ git_manifest = `git ls-files 2>/dev/null`.split("\n")
2
+ manifest = File.exist?('MANIFEST') ?
3
+ File.readlines('MANIFEST').map!(&:chomp).delete_if(&:empty?) : git_manifest
4
+ if git_manifest[0] && manifest != git_manifest
5
+ tmp = "MANIFEST.#$$.tmp"
6
+ File.open(tmp, 'w') { |fp| fp.puts(git_manifest.join("\n")) }
7
+ File.rename(tmp, 'MANIFEST')
8
+ system('git add MANIFEST')
9
+ end
10
+
11
+ Gem::Specification.new do |s|
12
+ s.name = 'mwrap'
13
+ s.version = '1.0.0'
14
+ s.homepage = 'https://80x24.org/mwrap/'
15
+ s.authors = ["Ruby hackers"]
16
+ s.summary = 'LD_PRELOAD malloc wrapper for Ruby'
17
+ s.executables = %w(mwrap)
18
+ s.files = manifest
19
+ s.description = <<~EOF
20
+ mwrap wraps all malloc, calloc, and realloc calls to trace the Ruby
21
+ source location of such calls and bytes allocated at each callsite.
22
+ EOF
23
+ s.email = %q{e@80x24.org}
24
+ s.test_files = Dir['test/test_*.rb']
25
+ s.extensions = %w(ext/mwrap/extconf.rb)
26
+
27
+ s.add_development_dependency('test-unit', '~> 3.0')
28
+ s.add_development_dependency('rake-compiler', '~> 1.0')
29
+ s.licenses = %w(GPL-2.0+)
30
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2018 mwrap hackers <mwrap-public@80x24.org>
3
+ # License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
4
+ require 'test/unit'
5
+ require 'mwrap'
6
+ require 'rbconfig'
7
+ require 'tempfile'
8
+
9
+ class TestMwrap < Test::Unit::TestCase
10
+ RB = "#{RbConfig::CONFIG['bindir']}/#{RbConfig::CONFIG['RUBY_INSTALL_NAME']}"
11
+
12
+ mwrap_so = $".grep(%r{/mwrap\.so\z})[0]
13
+ env = ENV.to_hash
14
+ cur = env['LD_PRELOAD']
15
+ env['LD_PRELOAD'] = cur ? "#{mwrap_so}:#{cur}".freeze : mwrap_so
16
+ @@env = env.freeze
17
+ inc = File.dirname(mwrap_so)
18
+ @@cmd = %W(#{RB} -w --disable=gems -I#{inc} -rmwrap).freeze
19
+
20
+ def test_mwrap_preload
21
+ cmd = @@cmd + %w(
22
+ -e ("helloworld"*1000).clear
23
+ -e Mwrap.dump
24
+ )
25
+ Tempfile.create('junk') do |tmp|
26
+ tmp.sync = true
27
+ res = system(@@env, *cmd, err: tmp)
28
+ assert res, $?.inspect
29
+ tmp.rewind
30
+ lines = tmp.readlines
31
+ line_1 = lines.grep(/\s-e:1\b/)[0].strip
32
+ assert_equal '10001', line_1.split(/\s+/)[0]
33
+ end
34
+ end
35
+
36
+ def test_dump_via_destructor
37
+ env = @@env.dup
38
+ env['MWRAP'] = 'dump_fd:5'
39
+ cmd = @@cmd + %w(-e ("0"*10000).clear)
40
+ Tempfile.create('junk') do |tmp|
41
+ tmp.sync = true
42
+ res = system(env, *cmd, { 5 => tmp })
43
+ assert res, $?.inspect
44
+ tmp.rewind
45
+ assert_match(/\b10001\s+1\s+-e:1$/, tmp.read)
46
+
47
+ env['MWRAP'] = 'dump_fd:1,dump_min:10000'
48
+ tmp.rewind
49
+ tmp.truncate(0)
50
+ res = system(env, *cmd, { 1 => tmp })
51
+ assert res, $?.inspect
52
+ tmp.rewind
53
+ assert_match(/\b10001\s+1\s+-e:1$/, tmp.read)
54
+
55
+ tmp.rewind
56
+ tmp.truncate(0)
57
+ env['MWRAP'] = "dump_path:#{tmp.path},dump_min:10000"
58
+ res = system(env, *cmd)
59
+ assert res, $?.inspect
60
+ assert_match(/\b10001\s+1\s+-e:1$/, tmp.read)
61
+ end
62
+ end
63
+
64
+ def test_clear
65
+ cmd = @@cmd + %w(
66
+ -e ("0"*10000).clear
67
+ -e Mwrap.clear
68
+ -e ("0"*20000).clear
69
+ -e Mwrap.dump($stdout,9999)
70
+ )
71
+ Tempfile.create('junk') do |tmp|
72
+ tmp.sync = true
73
+ res = system(@@env, *cmd, { 1 => tmp })
74
+ assert res, $?.inspect
75
+ tmp.rewind
76
+ buf = tmp.read
77
+ assert_not_match(/\s+-e:1$/, buf)
78
+ assert_match(/\b20001\s+1\s+-e:3$/, buf)
79
+ end
80
+ end
81
+
82
+ # make sure we don't break commands spawned by an mwrap-ed Ruby process:
83
+ def test_non_ruby_exec
84
+ IO.pipe do |r, w|
85
+ th = Thread.new { r.read }
86
+ Tempfile.create('junk') do |tmp|
87
+ tmp.sync = true
88
+ env = @@env.merge('MWRAP' => "dump_path:#{tmp.path}")
89
+ cmd = %w(perl -e print("HELLO_WORLD"))
90
+ res = system(env, *cmd, out: w)
91
+ w.close
92
+ assert res, $?.inspect
93
+ assert_match(/0x[a-f0-9]+\b/, tmp.read)
94
+ end
95
+ assert_equal "HELLO_WORLD", th.value
96
+ end
97
+ end
98
+
99
+ # some URCU flavors use USR1, ensure the one we choose does not
100
+ def test_sigusr1_works
101
+ cmd = @@cmd + %w(
102
+ -e STDOUT.sync=true
103
+ -e trap(:USR1){p("HELLO_WORLD")}
104
+ -e END{Mwrap.dump}
105
+ -e puts -e STDIN.read)
106
+ IO.pipe do |r, w|
107
+ IO.pipe do |r2, w2|
108
+ pid = spawn(@@env, *cmd, in: r2, out: w, err: '/dev/null')
109
+ r2.close
110
+ w.close
111
+ assert_equal "\n", r.gets
112
+ buf = +''
113
+ 10.times { Process.kill(:USR1, pid) }
114
+ while IO.select([r], nil, nil, 0.1)
115
+ case tmp = r.read_nonblock(1000, exception: false)
116
+ when String
117
+ buf << tmp
118
+ end
119
+ end
120
+ w2.close
121
+ Process.wait(pid)
122
+ assert_predicate $?, :success?, $?.inspect
123
+ assert_equal(["\"HELLO_WORLD\"\n"], buf.split(/^/).uniq)
124
+ end
125
+ end
126
+ end
127
+
128
+ def test_reset
129
+ assert_nil Mwrap.reset
130
+ end
131
+
132
+ def test_each
133
+ cmd = @@cmd + %w(
134
+ -e ("0"*10000).clear
135
+ -e h={}
136
+ -e Mwrap.each(1000){|a,b,c|h[a]=[b,c]}
137
+ -e puts(Marshal.dump(h))
138
+ )
139
+ r = IO.popen(@@env, cmd, 'r')
140
+ h = Marshal.load(r.read)
141
+ assert_not_predicate h, :empty?
142
+ h.each_key { |k| assert_kind_of String, k }
143
+ h.each_value do |total,calls|
144
+ assert_operator total, :>, 0
145
+ assert_operator calls, :>, 0
146
+ assert_operator total, :>=, calls
147
+ end
148
+ end
149
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mwrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ruby hackers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: test-unit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake-compiler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: |
42
+ mwrap wraps all malloc, calloc, and realloc calls to trace the Ruby
43
+ source location of such calls and bytes allocated at each callsite.
44
+ email: e@80x24.org
45
+ executables:
46
+ - mwrap
47
+ extensions:
48
+ - ext/mwrap/extconf.rb
49
+ extra_rdoc_files: []
50
+ files:
51
+ - ".gitignore"
52
+ - COPYING
53
+ - MANIFEST
54
+ - README
55
+ - Rakefile
56
+ - bin/mwrap
57
+ - ext/mwrap/extconf.rb
58
+ - ext/mwrap/jhash.h
59
+ - ext/mwrap/mwrap.c
60
+ - mwrap.gemspec
61
+ - test/test_mwrap.rb
62
+ homepage: https://80x24.org/mwrap/
63
+ licenses:
64
+ - GPL-2.0+
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 3.0.0.beta1
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: LD_PRELOAD malloc wrapper for Ruby
86
+ test_files:
87
+ - test/test_mwrap.rb