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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/COPYING +339 -0
- data/MANIFEST +12 -0
- data/README +86 -0
- data/Rakefile +16 -0
- data/bin/mwrap +29 -0
- data/ext/mwrap/extconf.rb +13 -0
- data/ext/mwrap/jhash.h +256 -0
- data/ext/mwrap/mwrap.c +598 -0
- data/mwrap.gemspec +30 -0
- data/test/test_mwrap.rb +149 -0
- metadata +87 -0
data/mwrap.gemspec
ADDED
@@ -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
|
data/test/test_mwrap.rb
ADDED
@@ -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
|