heapinfo 0.0.5 → 0.1.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 +4 -4
- data/README.md +1 -0
- data/lib/heapinfo.rb +8 -8
- data/lib/heapinfo/arena.rb +47 -45
- data/lib/heapinfo/cache.rb +12 -13
- data/lib/heapinfo/chunk.rb +23 -23
- data/lib/heapinfo/chunks.rb +16 -5
- data/lib/heapinfo/dumper.rb +55 -44
- data/lib/heapinfo/ext/string.rb +2 -2
- data/lib/heapinfo/glibc/error.rb +2 -1
- data/lib/heapinfo/glibc/free.rb +29 -16
- data/lib/heapinfo/glibc/glibc.rb +5 -1
- data/lib/heapinfo/glibc/helper.rb +9 -6
- data/lib/heapinfo/helper.rb +22 -26
- data/lib/heapinfo/libc.rb +18 -17
- data/lib/heapinfo/nil.rb +14 -8
- data/lib/heapinfo/process.rb +34 -25
- data/lib/heapinfo/process_info.rb +15 -14
- data/lib/heapinfo/segment.rb +10 -9
- data/lib/heapinfo/tools/get_arena.c +0 -1
- data/lib/heapinfo/version.rb +1 -1
- metadata +75 -31
- data/spec/cache_spec.rb +0 -46
- data/spec/chunk_spec.rb +0 -40
- data/spec/chunks_spec.rb +0 -25
- data/spec/dumper_spec.rb +0 -105
- data/spec/files/32bit_maps +0 -23
- data/spec/files/64bit_maps +0 -29
- data/spec/files/victim.cpp +0 -33
- data/spec/helper_spec.rb +0 -73
- data/spec/libc_spec.rb +0 -75
- data/spec/nil_spec.rb +0 -15
- data/spec/process_spec.rb +0 -201
- data/spec/spec_helper.rb +0 -98
- data/spec/string_spec.rb +0 -18
data/lib/heapinfo/dumper.rb
CHANGED
@@ -4,18 +4,19 @@ module HeapInfo
|
|
4
4
|
# Default dump length
|
5
5
|
DUMP_BYTES = 8
|
6
6
|
|
7
|
-
# Instantiate a
|
7
|
+
# Instantiate a {HeapInfo::Dumper} object
|
8
8
|
#
|
9
9
|
# @param [HeapInfo::ProcessInfo] info process info object.
|
10
|
-
# @param [String] mem_filename The filename that can be access for dump. Should be
|
10
|
+
# @param [String] mem_filename The filename that can be access for dump. Should be +/proc/[pid]/mem+.
|
11
11
|
def initialize(info, mem_filename)
|
12
|
-
@info
|
12
|
+
@info = info
|
13
|
+
@filename = mem_filename
|
13
14
|
need_permission unless dumpable?
|
14
15
|
end
|
15
16
|
|
16
|
-
# A helper for
|
17
|
-
# @param [Mixed] args The use input commands, see examples of
|
18
|
-
# @return [String, NilClass] Dump results. If error happend,
|
17
|
+
# A helper for {HeapInfo::Process} to dump memory.
|
18
|
+
# @param [Mixed] args The use input commands, see examples of {HeapInfo::Process#dump}.
|
19
|
+
# @return [String, NilClass] Dump results. If error happend, +nil+ is returned.
|
19
20
|
# @example
|
20
21
|
# p dump(:elf, 4)
|
21
22
|
# # => "\x7fELF"
|
@@ -29,47 +30,51 @@ module HeapInfo
|
|
29
30
|
mem
|
30
31
|
rescue => e
|
31
32
|
raise e if e.is_a? ArgumentError
|
32
|
-
nil
|
33
|
+
nil
|
33
34
|
end
|
34
35
|
|
35
36
|
# Return the dump result as chunks.
|
36
|
-
# see
|
37
|
+
# see {HeapInfo::Chunks} and {HeapInfo::Chunk} for more information.
|
37
38
|
#
|
38
|
-
# Note: Same as
|
39
|
+
# Note: Same as {#dump}, need permission of attaching another process.
|
39
40
|
# @return [HeapInfo::Chunks] An array of chunk(s).
|
40
|
-
# @param [Mixed] args Same as arguments of
|
41
|
+
# @param [Mixed] args Same as arguments of {#dump}.
|
41
42
|
def dump_chunks(*args)
|
42
43
|
base = base_of(*args)
|
43
44
|
dump(*args).to_chunks(bits: @info.bits, base: base)
|
44
45
|
end
|
45
46
|
|
46
|
-
# Show dump results like in gdb's command
|
47
|
+
# Show dump results like in gdb's command +x+.
|
47
48
|
#
|
48
|
-
# Details are in
|
49
|
+
# Details are in {HeapInfo:Process#x}.
|
49
50
|
# @param [Integer] count The number of result need to dump.
|
50
|
-
# @param [Mixed] commands Same format as
|
51
|
-
# @param [IO] io
|
52
|
-
# @return [NilClass] The return value of
|
51
|
+
# @param [Mixed] commands Same format as +#dump(*args)+.
|
52
|
+
# @param [IO] io +IO+ that use for printing.
|
53
|
+
# @return [NilClass] The return value of +io.puts+.
|
53
54
|
# @example
|
54
55
|
# x 3, 0x400000
|
55
56
|
# # 0x400000: 0x00010102464c457f 0x0000000000000000
|
56
57
|
# # 0x400010: 0x00000001003e0002
|
57
58
|
def x(count, *commands, io: $stdout)
|
58
|
-
commands
|
59
|
+
commands += [count * size_t]
|
59
60
|
base = base_of(*commands)
|
60
|
-
res = dump(*commands).unpack(size_t == 4 ?
|
61
|
-
str = res.group_by.with_index{|_, i| i / (16 / size_t) }.map do |round, values|
|
62
|
-
"%#x:\t"
|
61
|
+
res = dump(*commands).unpack(size_t == 4 ? 'L*' : 'Q*')
|
62
|
+
str = res.group_by.with_index { |_, i| i / (16 / size_t) }.map do |round, values|
|
63
|
+
format("%#x:\t", (base + round * 16)) +
|
64
|
+
values.map { |v| Helper.color(format("0x%0#{size_t * 2}x", v)) }.join("\t")
|
63
65
|
end.join("\n")
|
64
66
|
io.puts str
|
65
67
|
end
|
66
68
|
|
67
69
|
# Search a specific value/string/regexp in memory.
|
68
|
-
#
|
69
|
-
# @param [Integer, String, Regexp] pattern
|
70
|
-
#
|
70
|
+
# +#find+ only return the first matched address.
|
71
|
+
# @param [Integer, String, Regexp] pattern
|
72
|
+
# The desired search pattern, can be value(+Integer+), string, or regular expression.
|
73
|
+
# @param [Integer, String, Symbol] from
|
74
|
+
# Start address for searching, can be segment(+Symbol+)
|
75
|
+
# or segments with offset. See examples for more information.
|
71
76
|
# @param [Integer] length The length limit for searching.
|
72
|
-
# @return [Integer, NilClass] The first matched address,
|
77
|
+
# @return [Integer, NilClass] The first matched address, +nil+ is returned when no such pattern found.
|
73
78
|
# @example
|
74
79
|
# find(/E.F/, :elf)
|
75
80
|
# # => 4194305
|
@@ -81,17 +86,17 @@ module HeapInfo
|
|
81
86
|
from = base_of(from)
|
82
87
|
length = 1 << 40 if length.is_a? Symbol
|
83
88
|
case pattern
|
84
|
-
when Integer
|
85
|
-
when String
|
86
|
-
when Regexp
|
87
|
-
else; nil
|
89
|
+
when Integer then find_integer(pattern, from, length)
|
90
|
+
when String then find_string(pattern, from, length)
|
91
|
+
when Regexp then find_regexp(pattern, from, length)
|
88
92
|
end
|
89
93
|
end
|
90
94
|
|
91
|
-
|
92
|
-
# Parse the dump command into <tt>[base, offset, length]</tt>
|
95
|
+
# Parse the dump command into +[base, offset, length]+
|
93
96
|
# @param [Array] args The command, see examples for more information
|
94
|
-
# @return [Array<Symbol, Integer>]
|
97
|
+
# @return [Array<Symbol, Integer>]
|
98
|
+
# +[base, offset, length]+, while +base+ can be a +Symbol+ or an +Integer+.
|
99
|
+
# +length+ has default value equals to +8+.
|
95
100
|
# @example
|
96
101
|
# HeapInfo::Dumper.parse_cmd([:heap, 32, 10])
|
97
102
|
# # [:heap, 32, 10]
|
@@ -111,12 +116,12 @@ module HeapInfo
|
|
111
116
|
[base, offset, len]
|
112
117
|
end
|
113
118
|
|
114
|
-
# Helper for
|
119
|
+
# Helper for +#parse_cmd+.
|
115
120
|
#
|
116
|
-
# Split commands to exactly three parts:
|
117
|
-
#
|
121
|
+
# Split commands to exactly three parts: +[base, offset, length]+.
|
122
|
+
# +length+ is +nil+ if not present.
|
118
123
|
# @param [Array] args
|
119
|
-
# @return [Array<String>]
|
124
|
+
# @return [Array<String>] +[base, offset, length]+ in string expression.
|
120
125
|
# @example
|
121
126
|
# HeapInfo::Dumper.split_cmd([:heap, 32, 10])
|
122
127
|
# # ['heap', '32', '10']
|
@@ -138,9 +143,14 @@ module HeapInfo
|
|
138
143
|
args[0, 3]
|
139
144
|
end
|
140
145
|
|
141
|
-
|
146
|
+
private
|
147
|
+
|
142
148
|
def need_permission
|
143
|
-
|
149
|
+
msg = 'Could not attach to process. ' \
|
150
|
+
'Check the setting of /proc/sys/kernel/yama/ptrace_scope, ' \
|
151
|
+
'or try again as the root user. ' \
|
152
|
+
'For more details, see /etc/sysctl.d/10-ptrace.conf'
|
153
|
+
puts Helper.color(msg, sev: :fatal)
|
144
154
|
end
|
145
155
|
|
146
156
|
# use /proc/[pid]/mem for memory dump, must sure have permission
|
@@ -161,10 +171,10 @@ module HeapInfo
|
|
161
171
|
|
162
172
|
def base_len_of(*args)
|
163
173
|
base, offset, len = Dumper.parse_cmd(args)
|
164
|
-
if HeapInfo::ProcessInfo::EXPORT.include?(base)
|
174
|
+
if HeapInfo::ProcessInfo::EXPORT.include?(base) && (segment = @info.send(base)).is_a?(Segment)
|
165
175
|
base = segment.base
|
166
|
-
elsif
|
167
|
-
raise ArgumentError
|
176
|
+
elsif !base.is_a?(Integer)
|
177
|
+
raise ArgumentError, "Invalid base: #{base}" # invalid usage
|
168
178
|
end
|
169
179
|
[base + offset, len]
|
170
180
|
end
|
@@ -174,15 +184,15 @@ module HeapInfo
|
|
174
184
|
end
|
175
185
|
|
176
186
|
def find_integer(value, from, length)
|
177
|
-
find_string([value].pack(size_t == 4 ?
|
187
|
+
find_string([value].pack(size_t == 4 ? 'L*' : 'Q*'), from, length)
|
178
188
|
end
|
179
189
|
|
180
|
-
def find_string(string, from
|
181
|
-
batch_dumper(from, length) {|str| str.index
|
190
|
+
def find_string(string, from, length)
|
191
|
+
batch_dumper(from, length) { |str| str.index(string) }
|
182
192
|
end
|
183
193
|
|
184
|
-
def find_regexp(pattern, from
|
185
|
-
batch_dumper(from, length) {|str| str =~ pattern}
|
194
|
+
def find_regexp(pattern, from, length)
|
195
|
+
batch_dumper(from, length) { |str| str =~ pattern }
|
186
196
|
end
|
187
197
|
|
188
198
|
def batch_dumper(from, remain_size)
|
@@ -199,6 +209,7 @@ module HeapInfo
|
|
199
209
|
return if idx.nil?
|
200
210
|
from + idx
|
201
211
|
end
|
212
|
+
|
202
213
|
def size_t
|
203
214
|
@info.bits / 8
|
204
215
|
end
|
data/lib/heapinfo/ext/string.rb
CHANGED
@@ -9,7 +9,7 @@ module HeapInfo
|
|
9
9
|
# @return [HeapInfo::Chunk]
|
10
10
|
def to_chunk(bits: 64, base: 0)
|
11
11
|
size_t = bits / 8
|
12
|
-
dumper = ->(addr, len) { self[addr-base, len] }
|
12
|
+
dumper = ->(addr, len) { self[addr - base, len] }
|
13
13
|
Chunk.new(size_t, base, dumper)
|
14
14
|
end
|
15
15
|
|
@@ -21,7 +21,7 @@ module HeapInfo
|
|
21
21
|
size_t = bits / 8
|
22
22
|
chunks = Chunks.new
|
23
23
|
cur = 0
|
24
|
-
while cur + size_t * 2 <=
|
24
|
+
while cur + size_t * 2 <= length
|
25
25
|
now_chunk = self[cur, size_t * 2].to_chunk
|
26
26
|
sz = now_chunk.size
|
27
27
|
chunks << self[cur, sz + 1].to_chunk(bits: bits, base: base + cur) # +1 for dump prev_inuse
|
data/lib/heapinfo/glibc/error.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module HeapInfo
|
2
|
+
# Define errors.
|
2
3
|
module Glibc
|
3
4
|
# @abstract Exceptions raised by HeapInfo inherit from Error
|
4
5
|
class Error < StandardError; end
|
5
6
|
# Exception raised in malloc.c(malloc, free, etc.) methods.
|
6
7
|
class MallocError < Error; end
|
7
8
|
def malloc_assert(condition)
|
8
|
-
raise MallocError
|
9
|
+
raise MallocError, yield unless condition
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
data/lib/heapinfo/glibc/free.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
# Implment glibc's free-related functions
|
2
2
|
# [Reference](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html)
|
3
3
|
module HeapInfo
|
4
|
+
# Implement free-related functions.
|
4
5
|
module Glibc
|
5
6
|
# Implmentation of <tt>void __libc_free(void *mem)</tt>.
|
6
|
-
# [glibc-2.23](https://github.com/david942j/heapinfo/blob/master/examples/libcdb/libc-2.23/malloc.c#L2934) or
|
7
|
+
# [glibc-2.23](https://github.com/david942j/heapinfo/blob/master/examples/libcdb/libc-2.23/malloc.c#L2934) or
|
8
|
+
# [Online Source](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_free)
|
7
9
|
# @param [Integer] mem Memory address to be free.
|
8
10
|
def libc_free(mem)
|
9
11
|
# TODO: free_hook
|
10
12
|
mem = ulong mem
|
11
|
-
return if mem
|
13
|
+
return if mem.zero? # free(0) has no effect
|
12
14
|
ptr = mem2chunk(mem)
|
13
15
|
return munmap_chunk(ptr) if chunk_is_mmapped(ptr)
|
14
16
|
av = arena_for_chunk(ptr)
|
15
17
|
int_free(av, ptr)
|
16
18
|
end
|
17
|
-
alias
|
19
|
+
alias free libc_free
|
20
|
+
|
21
|
+
private
|
18
22
|
|
19
|
-
private
|
20
23
|
# Implmentation of <tt>void _int_free (mstate av, mchunkptr p, [int have_lock])</tt>.
|
21
|
-
# [glibc-2.23](https://github.com/david942j/heapinfo/blob/master/examples/libcdb/libc-2.23/malloc.c#L2934) or
|
22
|
-
#
|
24
|
+
# [glibc-2.23](https://github.com/david942j/heapinfo/blob/master/examples/libcdb/libc-2.23/malloc.c#L2934) or
|
25
|
+
# [Online Source](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_free)
|
26
|
+
#
|
23
27
|
# The original method in C is too long, split to multiple methods to match ruby convention.
|
24
28
|
# @param [HeapInfo::Arena] av
|
25
29
|
# @param [Integer] ptr Use <tt>ptr</tt> instead of <tt>p</tt> to prevent confusing with ruby native method.
|
@@ -42,17 +46,19 @@ module HeapInfo
|
|
42
46
|
invalid_next_size(:fast, av, ptr, size)
|
43
47
|
idx = fastbin_index(size)
|
44
48
|
old = av.fastbin[idx].fd
|
45
|
-
malloc_assert(
|
49
|
+
malloc_assert(old != ptr) do
|
50
|
+
format("double free or corruption (fasttop)\ntop of fastbin[0x%x]: 0x%x=0x%x", size & -8, ptr, ptr)
|
51
|
+
end
|
46
52
|
true
|
47
53
|
end
|
48
54
|
|
49
|
-
def int_free_small(av, ptr, size)
|
55
|
+
def int_free_small(av, ptr, size) # rubocop:disable UnusedMethodArgument
|
50
56
|
# TODO: unfinished
|
51
57
|
true
|
52
58
|
end
|
53
59
|
|
54
|
-
def munmap_chunk(ptr)
|
55
|
-
# TODO: check page alignment and... page exists?
|
60
|
+
def munmap_chunk(ptr) # rubocop:disable UnusedMethodArgument
|
61
|
+
# TODO: check page alignment and... page exists?
|
56
62
|
true
|
57
63
|
end
|
58
64
|
|
@@ -61,21 +67,28 @@ module HeapInfo
|
|
61
67
|
def invalid_pointer(ptr, size)
|
62
68
|
errmsg = "free(): invalid pointer\n"
|
63
69
|
# unsigned compare
|
64
|
-
malloc_assert(ptr <= ulong(-size)) { errmsg +
|
65
|
-
malloc_assert(ptr % (size_t * 2)
|
70
|
+
malloc_assert(ptr <= ulong(-size)) { errmsg + format('ptr(0x%x) > -size(0x%x)', ptr, ulong(-size)) }
|
71
|
+
malloc_assert((ptr % (size_t * 2)).zero?) { errmsg + format('ptr(0x%x) %% %d != 0', ptr, size_t * 2) }
|
66
72
|
end
|
67
73
|
|
68
74
|
def invalid_size(size)
|
69
75
|
errmsg = "free(): invalid size\n"
|
70
|
-
malloc_assert(size >= min_chunk_size)
|
71
|
-
|
76
|
+
malloc_assert(size >= min_chunk_size) do
|
77
|
+
errmsg + format('size(0x%x) < min_chunk_size(0x%x)', size, min_chunk_size)
|
78
|
+
end
|
79
|
+
malloc_assert(aligned_ok(size)) { errmsg + format('alignment error: size(0x%x) %% 0x%x != 0', size, size_t * 2) }
|
72
80
|
end
|
73
81
|
|
74
82
|
def invalid_next_size(type, av, ptr, size)
|
75
83
|
errmsg = "free(): invalid next size (#{type})\n"
|
76
84
|
nxt_chk = dumper.call(ptr + size, size_t * 2).to_chunk(base: ptr + size)
|
77
|
-
malloc_assert(nxt_chk.size > 2 * size_t)
|
78
|
-
|
85
|
+
malloc_assert(nxt_chk.size > 2 * size_t) do
|
86
|
+
errmsg + format("next chunk(0x%x) has size(#{nxt_chk.size}) <= 2 * #{size_t}", nxt_chk.base)
|
87
|
+
end
|
88
|
+
malloc_assert(nxt_chk.size < av.system_mem) do
|
89
|
+
errmsg + format('next chunk(0x%x) has size(0x%x) >= av.system_mem(0x%x)',
|
90
|
+
nxt_chk.base, nxt_chk.size, av.system_mem)
|
91
|
+
end
|
79
92
|
end
|
80
93
|
end
|
81
94
|
end
|
data/lib/heapinfo/glibc/glibc.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module HeapInfo
|
2
|
+
# To define heap-related functions in glibc.
|
2
3
|
module Glibc
|
3
4
|
attr_accessor :size_t
|
4
|
-
|
5
|
+
|
6
|
+
private
|
7
|
+
|
5
8
|
attr_accessor :main_arena
|
6
9
|
attr_accessor :dumper
|
7
10
|
end
|
8
11
|
end
|
12
|
+
|
9
13
|
require 'heapinfo/glibc/error.rb'
|
10
14
|
require 'heapinfo/glibc/helper.rb'
|
11
15
|
require 'heapinfo/glibc/free.rb'
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module HeapInfo
|
2
|
+
# Define some useful functions here.
|
2
3
|
module Glibc
|
3
|
-
|
4
|
+
private
|
5
|
+
|
4
6
|
def mem2chunk(mem)
|
5
7
|
ulong(mem - 2 * size_t)
|
6
8
|
end
|
@@ -11,9 +13,10 @@ module HeapInfo
|
|
11
13
|
dumper.call(ptr, size_t * 2).to_chunk.mmapped?
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
16
|
+
def max_fast
|
15
17
|
size_t * 16
|
16
18
|
end
|
19
|
+
alias get_max_fast max_fast
|
17
20
|
|
18
21
|
# The minimal chunk size.
|
19
22
|
# Not the real implmentation, maybe wrong some day?
|
@@ -25,19 +28,19 @@ module HeapInfo
|
|
25
28
|
# Not the real implmentation, maybe wrong some day?
|
26
29
|
# @return [Boolean]
|
27
30
|
def aligned_ok(size)
|
28
|
-
size & (2 * size_t - 1)
|
31
|
+
(size & (2 * size_t - 1)).zero?
|
29
32
|
end
|
30
33
|
|
31
34
|
# @return [Integer]
|
32
35
|
def ulong(n)
|
33
|
-
n % 2
|
36
|
+
n % 2**(size_t * 8)
|
34
37
|
end
|
35
38
|
|
36
39
|
# @return [HeapInfo::Arena]
|
37
40
|
def arena_for_chunk(ptr)
|
38
41
|
# not support arena other than initial main_arena
|
39
|
-
return if dumper.call(ptr, size_t * 2).to_chunk.non_main_arena?
|
40
|
-
main_arena
|
42
|
+
return if dumper.call(ptr, size_t * 2).to_chunk.non_main_arena?
|
43
|
+
main_arena
|
41
44
|
end
|
42
45
|
|
43
46
|
# @return [Integer]
|
data/lib/heapinfo/helper.rb
CHANGED
@@ -7,15 +7,15 @@ module HeapInfo
|
|
7
7
|
def self.pidof(prog)
|
8
8
|
# plz, don't cmd injection your self :p
|
9
9
|
pid = `pidof #{prog}`.strip.to_i
|
10
|
-
return nil if pid
|
11
|
-
throw "pidof #{prog} fail" unless pid.between?(2,
|
10
|
+
return nil if pid.zero? # process not exists yet
|
11
|
+
throw "pidof #{prog} fail" unless pid.between?(2, 65_535)
|
12
12
|
pid
|
13
|
-
#TODO: handle when multi processes exists
|
13
|
+
# TODO: handle when multi processes exists
|
14
14
|
end
|
15
15
|
|
16
16
|
# Create read <tt>/proc/[pid]/*</tt> methods
|
17
17
|
%w(exe maps).each do |method|
|
18
|
-
|
18
|
+
define_singleton_method("#{method}_of".to_sym) do |pid|
|
19
19
|
begin
|
20
20
|
IO.binread("/proc/#{pid}/#{method}")
|
21
21
|
rescue
|
@@ -23,7 +23,7 @@ module HeapInfo
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# Parse the contents of <tt>/proc/[pid]/maps</tt>.
|
28
28
|
#
|
29
29
|
# @param [String] content The file content of <tt>/proc/[pid]/maps</tt>
|
@@ -35,15 +35,15 @@ module HeapInfo
|
|
35
35
|
# 7f2788315000-7f2788316000 r--p 00022000 ca:01 402319 /lib/x86_64-linux-gnu/ld-2.19.so
|
36
36
|
# EOS
|
37
37
|
# )
|
38
|
-
# # [[0x400000, 0x40b000, 'r-xp', '/bin/cat'],
|
39
|
-
# # [0xbc4000, 0xbe5000, 'rw-p', '[heap]'],
|
38
|
+
# # [[0x400000, 0x40b000, 'r-xp', '/bin/cat'],
|
39
|
+
# # [0xbc4000, 0xbe5000, 'rw-p', '[heap]'],
|
40
40
|
# # [0x7f2788315000, 0x7f2788316000, 'r--p', '/lib/x86_64-linux-gnu/ld-2.19.so']]
|
41
41
|
def self.parse_maps(content)
|
42
42
|
lines = content.split("\n")
|
43
43
|
lines.map do |line|
|
44
|
-
s = line.scan(
|
44
|
+
s = line.scan(%r{^([0-9a-f]+)-([0-9a-f]+)\s([rwxp-]{4})[^/|\[]*([/|\[].+)$})[0]
|
45
45
|
next nil if s.nil?
|
46
|
-
s[0],s[1] = s[0,2].map{|h|h.to_i(16)}
|
46
|
+
s[0], s[1] = s[0, 2].map { |h| h.to_i(16) }
|
47
47
|
s
|
48
48
|
end.compact
|
49
49
|
end
|
@@ -57,23 +57,19 @@ module HeapInfo
|
|
57
57
|
bin: "\e[38;5;120m", # light green
|
58
58
|
klass: "\e[38;5;155m", # pry like
|
59
59
|
sym: "\e[38;5;229m", # pry like
|
60
|
-
}
|
61
|
-
# Wrapper color codes for
|
60
|
+
}.freeze
|
61
|
+
# Wrapper color codes for pretty inspect.
|
62
62
|
# @param [String] s Contents for wrapper
|
63
|
-
# @param [Symbol?] sev Specific which kind of color want to use, valid symbols are defined in
|
64
|
-
# If this argument is not present, will detect according to the content of
|
65
|
-
# @return [String] wrapper with color codes.
|
63
|
+
# @param [Symbol?] sev Specific which kind of color want to use, valid symbols are defined in +#COLOR_CODE+.
|
64
|
+
# If this argument is not present, will detect according to the content of +s+.
|
65
|
+
# @return [String] wrapper with color codes.
|
66
66
|
def self.color(s, sev: nil)
|
67
67
|
s = s.to_s
|
68
|
-
color = ''
|
69
68
|
cc = COLOR_CODE
|
70
|
-
if cc.
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
else #normal string
|
75
|
-
color = cc[:normal_s]
|
76
|
-
end
|
69
|
+
color = if cc.key?(sev) then cc[sev]
|
70
|
+
elsif s =~ /^(0x)?[0-9a-f]+$/ then cc[:integer] # integers
|
71
|
+
else cc[:normal_s] # normal string
|
72
|
+
end
|
77
73
|
"#{color}#{s.sub(cc[:esc_m], color)}#{cc[:esc_m]}"
|
78
74
|
end
|
79
75
|
|
@@ -114,9 +110,9 @@ module HeapInfo
|
|
114
110
|
obj.class.name.split('::').last || obj.class.name
|
115
111
|
end
|
116
112
|
|
117
|
-
# For checking a string is actually an integer
|
118
|
-
# @param [String] str String to be checked
|
119
|
-
# @return [Boolean] If
|
113
|
+
# For checking a string is actually an integer.
|
114
|
+
# @param [String] str String to be checked.
|
115
|
+
# @return [Boolean] If +str+ can be converted into integer.
|
120
116
|
# @example
|
121
117
|
# Helper.integer? '1234'
|
122
118
|
# # => true
|
@@ -125,7 +121,7 @@ module HeapInfo
|
|
125
121
|
# Helper.integer? '0xheapoverflow'
|
126
122
|
# # => false
|
127
123
|
def self.integer?(str)
|
128
|
-
|
124
|
+
true if Integer(str)
|
129
125
|
rescue ArgumentError, TypeError
|
130
126
|
false
|
131
127
|
end
|