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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22b8757e3a48390494b019943a52754a154051c3
4
- data.tar.gz: 4b4af3bf4d67e0ca35b9f5f8feb537499565f56e
3
+ metadata.gz: d934f5ad5614d320a103119b23a48312b5034598
4
+ data.tar.gz: 188ca4fe298b9859cbd546397d1176836a92e967
5
5
  SHA512:
6
- metadata.gz: 3872db0f32aaa2fbbedcca004163cb0b5181495cc81605a6587ef5c6dfcebf60f43209146aeda541c716cbdb6bca2bd2770927643cf3bfe50ade2cf7b709a282
7
- data.tar.gz: 82a57d0254a650ba531b5f00db676acc3ef4635cd0dbcdc34877f5a09ea8b369667377d96456e466e0a20ad2a6fb3dcb3737cb05966c95cd2483315bb24b7a8c
6
+ metadata.gz: 3051a5f8568e6a57d48ea2604193710a635c1fbd0c880b7a3e971b3608869e4c3d34e34a4c69886f422c0fe96e941c6be2c8d1f8d7a113759d244a6c739d1266
7
+ data.tar.gz: 8a441e5ed3acebf552a047123d897fcaafd7b31117b26e07a5d2499d4e298da1baabd271517b84a065225e3fd47a1547da26da6796fb85763a1a2417adcf5f71
@@ -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, Fixnum] prog
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/+.
@@ -114,7 +114,7 @@ module HeapInfo
114
114
  end.join
115
115
  end
116
116
 
117
- # @return [Array<Integer, Symbol, NilClass>] single link list of +fd+ chain.
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 +bk, bk, bin, fd, fd+.
172
- # @return [String] unsorted bin layouts wrapper with color codes.
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 +#link_list+.
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 +bk, bk, bin, fd, fd+.
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]
@@ -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, NilClass] Value that recorded, return +nil+ when cache miss.
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)
@@ -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 <tt>HeapInfo::Chunks</tt> object.
5
+ # Instantiate a {HeapInfo::Chunks} object.
6
6
  def initialize
7
7
  @chunks = []
8
8
  end
@@ -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
- def initialize(info, mem_filename)
12
- @info = info
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, NilClass] Dump results. If error happend, +nil+ is returned.
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.bits, base: base)
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:Process#x}.
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, NilClass] The first matched address, +nil+ is returned when no such pattern found.
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
- values = HeapInfo::ProcessInfo::EXPORT.map do |seg|
132
- segment = @info.respond_to?(seg) && @info.send(seg)
133
- [seg, segment.base] if segment.is_a?(Segment)
134
- end.compact.to_h
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 values[arg]
138
- when String then Helper.evaluate(arg, store: values)
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.bits / 8
180
+ @info[:bits] / 8
177
181
  end
178
182
  end
179
183
  end
@@ -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 <tt>HeapInfo::Chunk</tt>.
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 <tt>Chunk</tt> object.
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 <tt>HeapInfo::Chunk</tt>.
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 <tt>Chunk</tt> object.
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
@@ -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 of a process
20
- # @param [String] prog The request process name
21
- # @return [Fixnum] process id
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
- # plz, don't cmd injection your self :p
24
- pid = `pidof #{prog}`.strip.to_i
25
- return nil if pid.zero? # process not exists yet
26
- throw "pidof #{prog} fail" unless pid.between?(2, 65_535)
27
- pid
28
- # TODO: handle when multi processes exists
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[38;5;1m", # red
66
- integer: "\e[38;5;12m", # light blue
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[38;5;229m", # pry like
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 Specific which kind of color want to use, valid symbols are defined in +#COLOR_CODE+.
75
- # If this argument is not present, will detect according to the content of +s+.
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 <tt>obj</tt>
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)
@@ -8,12 +8,12 @@ module HeapInfo
8
8
  libc: /bc[^a-z]*\.so/,
9
9
  ld: %r{/ld-.+\.so}
10
10
  }.freeze
11
- # @return [Fixnum, NilClass] return the pid of process, +nil+ if no such process found
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, Fixnum] 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
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(*args)}, see {#dump} for more information.
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, NilClass] The first matched address, +nil+ is returned when no such pattern found.
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(@info, mem_filename)
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
- @elf = @program = Segment.find(maps, File.readlink("/proc/#{@pid}/exe"))
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(maps!, '[heap]')
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
- attr_reader :maps
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)
@@ -5,7 +5,7 @@ module HeapInfo
5
5
  attr_reader :base
6
6
  # Name of segment
7
7
  attr_reader :name
8
- # Instantiate a <tt>HeapInfo::Segment</tt> object
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 the form of the return value of <tt>HeapInfo::Helper.parse_maps</tt>.
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, NilClass]
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?
@@ -1,3 +1,4 @@
1
1
  module HeapInfo
2
- VERSION = '1.0.0'.freeze
2
+ # Current gem version.
3
+ VERSION = '1.0.1'.freeze
3
4
  end
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.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-06 00:00:00.000000000 Z
11
+ date: 2017-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dentaku