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.
@@ -4,18 +4,19 @@ module HeapInfo
4
4
  # Default dump length
5
5
  DUMP_BYTES = 8
6
6
 
7
- # Instantiate a <tt>HeapInfo::Dumper</tt> object
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 <tt>/proc/[pid]/mem</tt>.
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, @filename = info, mem_filename
12
+ @info = info
13
+ @filename = mem_filename
13
14
  need_permission unless dumpable?
14
15
  end
15
16
 
16
- # A helper for <tt>HeapInfo::Process</tt> to dump memory.
17
- # @param [Mixed] args The use input commands, see examples of <tt>HeapInfo::Process#dump</tt>.
18
- # @return [String, NilClass] Dump results. If error happend, <tt>nil</tt> is returned.
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 <tt>HeapInfo::Chunks</tt> and <tt>HeapInfo::Chunk</tt> for more information.
37
+ # see {HeapInfo::Chunks} and {HeapInfo::Chunk} for more information.
37
38
  #
38
- # Note: Same as <tt>dump</tt>, need permission of attaching another process.
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 <tt>#dump</tt>
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 <tt>x</tt>.
47
+ # Show dump results like in gdb's command +x+.
47
48
  #
48
- # Details are in <tt>HeapInfo:Process#x</tt>
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 <tt>#dump(*args)</tt>.
51
- # @param [IO] io <tt>IO</tt> that use for printing.
52
- # @return [NilClass] The return value of <tt>io.puts</tt>.
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 = commands + [count * size_t]
59
+ commands += [count * size_t]
59
60
  base = base_of(*commands)
60
- res = dump(*commands).unpack(size_t == 4 ? "L*" : "Q*")
61
- str = res.group_by.with_index{|_, i| i / (16 / size_t) }.map do |round, values|
62
- "%#x:\t" % (base + round * 16) + values.map{|v| Helper.color "0x%0#{size_t * 2}x" % v}.join("\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
- # <tt>#find</tt> only return the first matched address.
69
- # @param [Integer, String, Regexp] pattern The desired search pattern, can be value(<tt>Integer</tt>), string, or regular expression.
70
- # @param [Integer, String, Symbol] from Start address for searching, can be segment(<tt>Symbol</tt>) or segments with offset. See examples for more information.
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, <tt>nil</tt> is returned when no such pattern found.
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; find_integer(pattern, from, length)
85
- when String; find_string(pattern, from, length)
86
- when Regexp; find_regexp(pattern, from, length)
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>] <tt>[base, offset, length]</tt>, while <tt>base</tt> can be a [Symbol] or an [Integer]. <tt>length</tt> has default value equal to <tt>8</tt>.
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 <tt>#parse_cmd</tt>.
119
+ # Helper for +#parse_cmd+.
115
120
  #
116
- # Split commands to exactly three parts: <tt>[base, offset, length]</tt>
117
- # <tt>length</tt> is <tt>nil</tt> if not present.
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>] <tt>[base, offset, length]</tt> in string expression.
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
- private
146
+ private
147
+
142
148
  def need_permission
143
- puts Helper.color(%q(Could not attach to process. Check the setting of /proc/sys/kernel/yama/ptrace_scope, or try again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf), sev: :fatal)
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) and (segment = @info.send(base)).is_a? Segment
174
+ if HeapInfo::ProcessInfo::EXPORT.include?(base) && (segment = @info.send(base)).is_a?(Segment)
165
175
  base = segment.base
166
- elsif not base.is_a? Integer
167
- raise ArgumentError.new("Invalid base: #{base}") # invalid usage
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 ? "L*" : "Q*"), from, length)
187
+ find_string([value].pack(size_t == 4 ? 'L*' : 'Q*'), from, length)
178
188
  end
179
189
 
180
- def find_string(string, from ,length)
181
- batch_dumper(from, length) {|str| str.index string}
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 ,length)
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
@@ -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 <= self.length
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
@@ -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.new(yield) unless condition
9
+ raise MallocError, yield unless condition
9
10
  end
10
11
  end
11
12
  end
@@ -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 [Online Source](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_free)
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 == 0 # free(0) has no effect
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 :free :libc_free
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 [Online Source](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_free)
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( old != ptr ) { "double free or corruption (fasttop)\ntop of fastbin[0x%x]: 0x%x=0x%x" % [size & -8, ptr, ptr] }
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 + "ptr(0x%x) > -size(0x%x)" % [ptr, ulong(-size)] }
65
- malloc_assert(ptr % (size_t * 2) == 0) { errmsg + "ptr(0x%x) %% %d != 0" % [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) { errmsg + "size(0x%x) < min_chunk_size(0x%x)" % [size, min_chunk_size] }
71
- malloc_assert(aligned_ok size) { errmsg + "alignment error: size(0x%x) %% 0x%x != 0" % [size, size_t * 2] }
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) { errmsg + "next chunk(0x%x) has size(#{nxt_chk.size}) <= 2 * #{size_t}" % nxt_chk.base }
78
- malloc_assert(nxt_chk.size < av.system_mem) { errmsg + "next chunk(0x%x) has size(0x%x) >= av.system_mem(0x%x)" % [nxt_chk.base, nxt_chk.size, av.system_mem] }
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
@@ -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
- private
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
- private
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 get_max_fast
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) == 0
31
+ (size & (2 * size_t - 1)).zero?
29
32
  end
30
33
 
31
34
  # @return [Integer]
32
35
  def ulong(n)
33
- n % 2 ** (size_t * 8)
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]
@@ -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 == 0 # process not exists yet
11
- throw "pidof #{prog} fail" unless pid.between?(2, 65535)
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
- self.define_singleton_method("#{method}_of".to_sym) do |pid|
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(/^([0-9a-f]+)-([0-9a-f]+)\s([rwxp-]{4})[^\/|\[]*([\/|\[].+)$/)[0]
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 for pretty inspect
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 <tt>#COLOR_CODE</tt>.
64
- # If this argument is not present, will detect according to the content of <tt>s</tt>
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.keys.include?(sev)
71
- color = cc[sev]
72
- elsif s =~ /^(0x)?[0-9a-f]+$/ # integers
73
- color = cc[:integer]
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 <tt>str</tt> can be converted into integer
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
- !!Integer(str)
124
+ true if Integer(str)
129
125
  rescue ArgumentError, TypeError
130
126
  false
131
127
  end