mwrap 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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