heapinfo 1.0.0 → 1.0.1
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/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
|