heapinfo 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/heapinfo.rb +1 -1
- data/lib/heapinfo/arena.rb +5 -5
- data/lib/heapinfo/cache.rb +1 -1
- data/lib/heapinfo/chunks.rb +2 -2
- data/lib/heapinfo/dumper.rb +18 -14
- data/lib/heapinfo/ext/string.rb +6 -4
- data/lib/heapinfo/helper.rb +27 -23
- data/lib/heapinfo/process.rb +8 -6
- data/lib/heapinfo/process_info.rb +5 -8
- data/lib/heapinfo/segment.rb +3 -3
- data/lib/heapinfo/version.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d934f5ad5614d320a103119b23a48312b5034598
|
4
|
+
data.tar.gz: 188ca4fe298b9859cbd546397d1176836a92e967
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3051a5f8568e6a57d48ea2604193710a635c1fbd0c880b7a3e971b3608869e4c3d34e34a4c69886f422c0fe96e941c6be2c8d1f8d7a113759d244a6c739d1266
|
7
|
+
data.tar.gz: 8a441e5ed3acebf552a047123d897fcaafd7b31117b26e07a5d2499d4e298da1baabd271517b84a065225e3fd47a1547da26da6796fb85763a1a2417adcf5f71
|
data/lib/heapinfo.rb
CHANGED
@@ -16,7 +16,7 @@ module HeapInfo
|
|
16
16
|
|
17
17
|
# Entry point for using {HeapInfo}.
|
18
18
|
# Show segments info of the process after loaded.
|
19
|
-
# @param [String,
|
19
|
+
# @param [String, Integer] prog
|
20
20
|
# The program name of victim. If a number is given, seem as pid (useful when multi-processes exist).
|
21
21
|
# @param [Hash] options Give library's file name.
|
22
22
|
# @option options [String, Regexp] :libc file name of glibc, default is +/bc[^a-z]*\.so/+.
|
data/lib/heapinfo/arena.rb
CHANGED
@@ -114,7 +114,7 @@ module HeapInfo
|
|
114
114
|
end.join
|
115
115
|
end
|
116
116
|
|
117
|
-
# @return [Array<Integer, Symbol,
|
117
|
+
# @return [Array<Integer, Symbol, nil>] single link list of +fd+ chain.
|
118
118
|
# Last element will be:
|
119
119
|
# - +:loop+ if loop detectded
|
120
120
|
# - +:invalid+ invalid address detected
|
@@ -168,8 +168,8 @@ module HeapInfo
|
|
168
168
|
end
|
169
169
|
|
170
170
|
# @param [Integer] size
|
171
|
-
# At most expand size. For +size = 2+, the expand list would be
|
172
|
-
# @return [String]
|
171
|
+
# At most expand size. For +size = 2+, the expand list would be <tt>bk, bk, bin, fd, fd</tt>.
|
172
|
+
# @return [String] Unsorted bin layouts wrapper with color codes.
|
173
173
|
def inspect(size: 2)
|
174
174
|
list = link_list(size)
|
175
175
|
return '' if list.size <= 1 && Helper.class_name(self) != 'UnsortedBin' # bad..
|
@@ -177,7 +177,7 @@ module HeapInfo
|
|
177
177
|
end
|
178
178
|
|
179
179
|
# Wrapper the double-linked list with color codes.
|
180
|
-
# @param [Array<Integer>] list The list from
|
180
|
+
# @param [Array<Integer>] list The list from {#link_list}.
|
181
181
|
# @return [String] Wrapper with color codes.
|
182
182
|
def pretty_list(list)
|
183
183
|
center = nil
|
@@ -199,7 +199,7 @@ module HeapInfo
|
|
199
199
|
#
|
200
200
|
# The list will like +[..., bk of bk, bk of bin, bin, fd of bin, fd of fd, ...]+.
|
201
201
|
# @param [Integer] expand_size
|
202
|
-
# At most expand size. For +size = 2+, the expand list would be
|
202
|
+
# At most expand size. For +size = 2+, the expand list would be <tt>bk, bk, bin, fd, fd</tt>.
|
203
203
|
# @return [Array<Integer>] The linked list.
|
204
204
|
def link_list(expand_size)
|
205
205
|
list = [@base]
|
data/lib/heapinfo/cache.rb
CHANGED
@@ -33,7 +33,7 @@ module HeapInfo
|
|
33
33
|
# Read cache from file.
|
34
34
|
#
|
35
35
|
# @param [String] key In file path format, only accept +[\w\/]+ to prevent horrible things.
|
36
|
-
# @return [Object,
|
36
|
+
# @return [Object, nil] Value that recorded, return +nil+ when cache miss.
|
37
37
|
def read(key)
|
38
38
|
filepath = realpath(key)
|
39
39
|
return unless File.file?(filepath)
|
data/lib/heapinfo/chunks.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module HeapInfo
|
2
|
-
# Self-defined array for collecting chunk(s)
|
2
|
+
# Self-defined array for collecting chunk(s).
|
3
3
|
class Chunks
|
4
4
|
include Enumerable
|
5
|
-
# Instantiate a
|
5
|
+
# Instantiate a {HeapInfo::Chunks} object.
|
6
6
|
def initialize
|
7
7
|
@chunks = []
|
8
8
|
end
|
data/lib/heapinfo/dumper.rb
CHANGED
@@ -6,17 +6,19 @@ module HeapInfo
|
|
6
6
|
|
7
7
|
# Instantiate a {HeapInfo::Dumper} object
|
8
8
|
#
|
9
|
-
# @param [HeapInfo::ProcessInfo] info process info object.
|
10
9
|
# @param [String] mem_filename The filename that can be access for dump. Should be +/proc/[pid]/mem+.
|
11
|
-
|
12
|
-
|
10
|
+
# @param [Proc] block
|
11
|
+
# Use for get segment info.
|
12
|
+
# See {Dumper#base_len_of} for more information.
|
13
|
+
def initialize(mem_filename, &block)
|
13
14
|
@filename = mem_filename
|
15
|
+
@info = block || ->(*) { HeapInfo::Nil.new }
|
14
16
|
need_permission unless dumpable?
|
15
17
|
end
|
16
18
|
|
17
19
|
# A helper for {HeapInfo::Process} to dump memory.
|
18
20
|
# @param [Mixed] args The use input commands, see examples of {HeapInfo::Process#dump}.
|
19
|
-
# @return [String,
|
21
|
+
# @return [String, nil] Dump results. If error happend, +nil+ is returned.
|
20
22
|
# @example
|
21
23
|
# p dump(:elf, 4)
|
22
24
|
# # => "\x7fELF"
|
@@ -41,12 +43,12 @@ module HeapInfo
|
|
41
43
|
# @param [Mixed] args Same as arguments of {#dump}.
|
42
44
|
def dump_chunks(*args)
|
43
45
|
base = base_of(*args)
|
44
|
-
dump(*args).to_chunks(bits: @info
|
46
|
+
dump(*args).to_chunks(bits: @info[:bits], base: base)
|
45
47
|
end
|
46
48
|
|
47
49
|
# Show dump results like in gdb's command +x+.
|
48
50
|
#
|
49
|
-
# Details are in {HeapInfo
|
51
|
+
# Details are in {HeapInfo::Process#x}.
|
50
52
|
# @param [Integer] count The number of result need to dump.
|
51
53
|
# @param [Symbol, String, Integer] address The base address to be dumped.
|
52
54
|
# @return [void]
|
@@ -73,7 +75,7 @@ module HeapInfo
|
|
73
75
|
# Start address for searching, can be segment(+Symbol+)
|
74
76
|
# or segments with offset. See examples for more information.
|
75
77
|
# @param [Integer] length The length limit for searching.
|
76
|
-
# @return [Integer,
|
78
|
+
# @return [Integer, nil] The first matched address, +nil+ is returned when no such pattern found.
|
77
79
|
# @example
|
78
80
|
# find(/E.F/, :elf)
|
79
81
|
# # => 4194305
|
@@ -119,6 +121,8 @@ module HeapInfo
|
|
119
121
|
|
120
122
|
# Get the base address and length.
|
121
123
|
#
|
124
|
+
# +@info+ will be used for getting the segment base,
|
125
|
+
# so we can support use symbol as base address.
|
122
126
|
# @param [Integer, Symbol, String] arg The base address, see examples.
|
123
127
|
# @param [Integer] len An integer.
|
124
128
|
# @example
|
@@ -128,14 +132,14 @@ module HeapInfo
|
|
128
132
|
# base_len_of('heap+0x30', 10) #=> [0x603030, 10]
|
129
133
|
# base_len_of('elf+0x3*2-1') #=> [0x400005, DUMP_BYTES]
|
130
134
|
def base_len_of(arg, len = DUMP_BYTES)
|
131
|
-
|
132
|
-
|
133
|
-
[
|
134
|
-
end
|
135
|
+
segments = @info.call(:segments) || {}
|
136
|
+
segments = segments.each_with_object({}) do |(k, seg), memo|
|
137
|
+
memo[k] = seg.base
|
138
|
+
end
|
135
139
|
base = case arg
|
136
140
|
when Integer then arg
|
137
|
-
when Symbol then
|
138
|
-
when String then Helper.evaluate(arg, store:
|
141
|
+
when Symbol then segments[arg]
|
142
|
+
when String then Helper.evaluate(arg, store: segments)
|
139
143
|
end
|
140
144
|
raise ArgumentError, "Invalid base: #{arg.inspect}" unless base.is_a?(Integer) # invalid usage
|
141
145
|
[base, len]
|
@@ -173,7 +177,7 @@ module HeapInfo
|
|
173
177
|
end
|
174
178
|
|
175
179
|
def size_t
|
176
|
-
@info
|
180
|
+
@info[:bits] / 8
|
177
181
|
end
|
178
182
|
end
|
179
183
|
end
|
data/lib/heapinfo/ext/string.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
module HeapInfo
|
2
|
+
# Define extensions of naive objects.
|
2
3
|
module Ext
|
4
|
+
# Extension of +String+ class.
|
3
5
|
module String
|
4
6
|
# Methods to be mixed into String
|
5
7
|
module InstanceMethods
|
6
|
-
# Convert string to a
|
8
|
+
# Convert string to a {HeapInfo::Chunk}.
|
7
9
|
# @option [Integer] bits 32 or 64 bit of this chunk.
|
8
|
-
# @option [Integer] base Base address will show when print the
|
10
|
+
# @option [Integer] base Base address will show when print the {Chunk} object.
|
9
11
|
# @return [HeapInfo::Chunk]
|
10
12
|
def to_chunk(bits: 64, base: 0)
|
11
13
|
size_t = bits / 8
|
@@ -13,9 +15,9 @@ module HeapInfo
|
|
13
15
|
Chunk.new(size_t, base, dumper)
|
14
16
|
end
|
15
17
|
|
16
|
-
# Convert string to array of
|
18
|
+
# Convert string to array of {HeapInfo::Chunk}.
|
17
19
|
# @option [Integer] bits 32 or 64 bit of this chunk.
|
18
|
-
# @option [Integer] base Base address will show when print the
|
20
|
+
# @option [Integer] base Base address will show when print the {Chunk} object.
|
19
21
|
# @return [HeapInfo::Chunks]
|
20
22
|
def to_chunks(bits: 64, base: 0)
|
21
23
|
size_t = bits / 8
|
data/lib/heapinfo/helper.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'dentaku'
|
2
|
+
require 'shellwords'
|
2
3
|
|
3
4
|
module HeapInfo
|
4
|
-
# Some helper functions
|
5
|
+
# Some helper functions.
|
5
6
|
module Helper
|
6
|
-
# Create read +/proc/[pid]/*+ methods
|
7
|
+
# Create read +/proc/[pid]/*+ methods.
|
7
8
|
%w(exe maps).each do |method|
|
8
9
|
define_singleton_method("#{method}_of".to_sym) do |pid|
|
9
10
|
begin
|
@@ -16,21 +17,23 @@ module HeapInfo
|
|
16
17
|
|
17
18
|
# Define class methods here.
|
18
19
|
module ClassMethods
|
19
|
-
# Get the process id
|
20
|
-
#
|
21
|
-
#
|
20
|
+
# Get the process id from program name.
|
21
|
+
#
|
22
|
+
# When multiple processes exist, the one with lastest start time would be returned.
|
23
|
+
# @param [String] prog The request process name.
|
24
|
+
# @return [Integer] Process id.
|
22
25
|
def pidof(prog)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
info = %x(ps -o pid=,lstart= --pid `pidof #{Shellwords.escape(prog)}` 2>/dev/null).lines.map do |l|
|
27
|
+
pid, time = l.split(' ', 2)
|
28
|
+
[Time.parse(time), pid.to_i]
|
29
|
+
end
|
30
|
+
return nil if info.empty? # process not exists yet
|
31
|
+
info.max_by(&:first).last
|
29
32
|
end
|
30
33
|
|
31
34
|
# Parse the contents of <tt>/proc/[pid]/maps</tt>.
|
32
35
|
#
|
33
|
-
# @param [String] content The file content of <tt>/proc/[pid]/maps</tt
|
36
|
+
# @param [String] content The file content of <tt>/proc/[pid]/maps</tt>.
|
34
37
|
# @return [Array] In form of <tt>[[start, end, permission, name], ...]</tt>. See examples.
|
35
38
|
# @example
|
36
39
|
# HeapInfo::Helper.parse_maps(<<EOS
|
@@ -59,20 +62,21 @@ module HeapInfo
|
|
59
62
|
@disable_color = !on
|
60
63
|
end
|
61
64
|
|
62
|
-
# Color codes for pretty print
|
65
|
+
# Color codes for pretty print.
|
63
66
|
COLOR_CODE = {
|
64
67
|
esc_m: "\e[0m",
|
65
|
-
normal_s: "\e[
|
66
|
-
integer: "\e[
|
68
|
+
normal_s: "\e[31m", # red
|
69
|
+
integer: "\e[1m\e[34m", # light blue
|
67
70
|
fatal: "\e[38;5;197m", # dark red
|
68
71
|
bin: "\e[38;5;120m", # light green
|
69
72
|
klass: "\e[38;5;155m", # pry like
|
70
|
-
sym: "\e[
|
73
|
+
sym: "\e[33m", # pry like
|
71
74
|
}.freeze
|
72
75
|
# Wrapper color codes for pretty inspect.
|
73
|
-
# @param [String] s Contents for wrapper
|
74
|
-
# @param [Symbol?] sev
|
75
|
-
#
|
76
|
+
# @param [String] s Contents for wrapper.
|
77
|
+
# @param [Symbol?] sev
|
78
|
+
# Specific which kind of color want to use, valid symbols are defined in +#COLOR_CODE+.
|
79
|
+
# If this argument is not present, will detect according to the content of +s+.
|
76
80
|
# @return [String] wrapper with color codes.
|
77
81
|
def color(s, sev: nil)
|
78
82
|
s = s.to_s
|
@@ -101,7 +105,7 @@ module HeapInfo
|
|
101
105
|
data.unpack(size_t == 4 ? 'L*' : 'Q*')[0]
|
102
106
|
end
|
103
107
|
|
104
|
-
# Convert number in hex format
|
108
|
+
# Convert number in hex format.
|
105
109
|
#
|
106
110
|
# @param [Integer] num Non-negative integer.
|
107
111
|
# @return [String] number in hex format.
|
@@ -112,9 +116,9 @@ module HeapInfo
|
|
112
116
|
format('-0x%x', -num)
|
113
117
|
end
|
114
118
|
|
115
|
-
# Retrieve pure class name(without module) of an object
|
116
|
-
# @param [Object] obj Any instance
|
117
|
-
# @return [String] Class name of
|
119
|
+
# Retrieve pure class name(without module) of an object.
|
120
|
+
# @param [Object] obj Any instance.
|
121
|
+
# @return [String] Class name of +obj+.
|
118
122
|
# @example
|
119
123
|
# # suppose obj is an instance of HeapInfo::Chunk
|
120
124
|
# Helper.class_name(obj)
|
data/lib/heapinfo/process.rb
CHANGED
@@ -8,12 +8,12 @@ module HeapInfo
|
|
8
8
|
libc: /bc[^a-z]*\.so/,
|
9
9
|
ld: %r{/ld-.+\.so}
|
10
10
|
}.freeze
|
11
|
-
# @return [
|
11
|
+
# @return [Integer, nil] return the pid of process, +nil+ if no such process found
|
12
12
|
attr_reader :pid
|
13
13
|
|
14
14
|
# Instantiate a {HeapInfo::Process} object.
|
15
|
-
# @param [String,
|
16
|
-
# @param [Hash{Symbol =>
|
15
|
+
# @param [String, Integer] prog Process name or pid, see {HeapInfo::heapinfo} for more information
|
16
|
+
# @param [Hash{Symbol => Regexp, String}] options libraries' filename, see {HeapInfo::heapinfo} for more information
|
17
17
|
def initialize(prog, options = {})
|
18
18
|
@prog = prog
|
19
19
|
@options = DEFAULT_LIB.merge options
|
@@ -126,7 +126,7 @@ module HeapInfo
|
|
126
126
|
# The dump results wrapper with color codes and nice typesetting will output to +stdout+.
|
127
127
|
# @param [Integer] count The number of result need to dump, see examples for more information.
|
128
128
|
# @param [String, Symbol, Integer] address The base address to be dumped.
|
129
|
-
# Same format as {#dump
|
129
|
+
# Same format as {#dump}, see {#dump} for more information.
|
130
130
|
# @return [void]
|
131
131
|
# @example
|
132
132
|
# h.x 8, :heap
|
@@ -154,7 +154,7 @@ module HeapInfo
|
|
154
154
|
# @param [Integer] length
|
155
155
|
# The search length limit, default is unlimited,
|
156
156
|
# which will search until pattern found or reach unreadable memory.
|
157
|
-
# @return [Integer,
|
157
|
+
# @return [Integer, nil] The first matched address, +nil+ is returned when no such pattern found.
|
158
158
|
# @example
|
159
159
|
# h.find(0xdeadbeef, 'heap+0x10', 0x1000)
|
160
160
|
# # => 6299664 # 0x602010
|
@@ -238,7 +238,9 @@ module HeapInfo
|
|
238
238
|
ProcessInfo::EXPORT.each do |m|
|
239
239
|
self.class.send(:define_method, m) { @info.send(m) }
|
240
240
|
end
|
241
|
-
@dumper = Dumper.new(
|
241
|
+
@dumper = Dumper.new(mem_filename) do |sym|
|
242
|
+
@info.send(sym) if @info.respond_to?(sym)
|
243
|
+
end
|
242
244
|
end
|
243
245
|
|
244
246
|
def mem_filename
|
@@ -26,9 +26,9 @@ module HeapInfo
|
|
26
26
|
def initialize(process)
|
27
27
|
@pid = process.pid
|
28
28
|
options = process.instance_variable_get(:@options)
|
29
|
-
maps
|
29
|
+
maps = load_maps
|
30
30
|
@bits = bits_of(Helper.exe_of(@pid))
|
31
|
-
@
|
31
|
+
@program = Segment.find(maps, File.readlink("/proc/#{@pid}/exe"))
|
32
32
|
@stack = Segment.find(maps, '[stack]')
|
33
33
|
# well.. stack is a strange case because it will grow in runtime..
|
34
34
|
# should i detect stack base growing..?
|
@@ -41,7 +41,7 @@ module HeapInfo
|
|
41
41
|
#
|
42
42
|
# @return [HeapInfo::Segment] The {Segment} of heap.
|
43
43
|
def heap # special handle because heap might not be initialized in the beginning
|
44
|
-
@heap ||= Segment.find(
|
44
|
+
@heap ||= Segment.find(load_maps, '[heap]')
|
45
45
|
end
|
46
46
|
|
47
47
|
# Return segemnts load currently.
|
@@ -55,11 +55,8 @@ module HeapInfo
|
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
# force reload maps
|
61
|
-
def maps!
|
62
|
-
@maps = Helper.parse_maps(Helper.maps_of(@pid))
|
58
|
+
def load_maps
|
59
|
+
Helper.parse_maps(Helper.maps_of(@pid))
|
63
60
|
end
|
64
61
|
|
65
62
|
def bits_of(elf)
|
data/lib/heapinfo/segment.rb
CHANGED
@@ -5,7 +5,7 @@ module HeapInfo
|
|
5
5
|
attr_reader :base
|
6
6
|
# Name of segment
|
7
7
|
attr_reader :name
|
8
|
-
# Instantiate a
|
8
|
+
# Instantiate a {HeapInfo::Segment} object
|
9
9
|
# @param [Integer] base Base address
|
10
10
|
# @param [String] name Name of segment
|
11
11
|
def initialize(base, name)
|
@@ -23,10 +23,10 @@ module HeapInfo
|
|
23
23
|
#
|
24
24
|
# Search the specific <tt>pattern</tt> in <tt>maps</tt> and return a {HeapInfo::Segment} object.
|
25
25
|
#
|
26
|
-
# @param [Array] maps <tt>maps</tt> is in
|
26
|
+
# @param [Array] maps <tt>maps</tt> is in form of the return value of {Helper::ClassMethods#parse_maps}.
|
27
27
|
# @param [Regexp, String] pattern
|
28
28
|
# The segment name want to match in maps. If +String+ is given, the pattern is matched as a substring.
|
29
|
-
# @return [HeapInfo::Segment,
|
29
|
+
# @return [HeapInfo::Segment, nil]
|
30
30
|
# The request {HeapInfo::Segment} object. If the pattern is not matched, <tt>nil</tt> will be returned.
|
31
31
|
def self.find(maps, pattern)
|
32
32
|
return Nil.new if pattern.nil?
|
data/lib/heapinfo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heapinfo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dentaku
|