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 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