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