Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add "heap find", "heap range" and "heap range run" commands #22

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ heap select - query used heap chunks
hexdump <addr> [-c] - print a hexdump, stating at the specific region of memory (expose hex characters with -c option)
heap arenas - print glibs arenas
heap arena <arena> - select glibc arena number
heap find -- Find stuff anywhere; supplies ranges to the find command
heap range -- Print all non-empty merged memory ranges sorted by start address
heap range run -- Run a command for each range from `heap range`
```

Useful resources
Expand Down
2 changes: 2 additions & 0 deletions heap/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2010 David Hugh Malcolm
# Copyright (C) 2015 Stefan Bühler
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
Expand Down
75 changes: 75 additions & 0 deletions heap/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,77 @@ def invoke(self, args, from_tty):
except ParserError as e:
print(e)

class HeapRange(gdb.Command):
'''Print all non-empty merged memory ranges sorted by start address

Each line contains a start and end address; the end address is the
first not valid address.
'''
def __init__(self):
gdb.Command.__init__ (self,
"heap range",
gdb.COMMAND_DATA,
prefix = True)

def invoke(self, args, from_tty):
import ranges
for r in ranges.merged_ranges():
print('\t%s' % r)

class HeapRangeRun(gdb.Command):
'''Run a command for each range from `heap range`

Use $range_start, $range_last (end - 1) and $range_end variables in the
command.
Example: heap range run find /g $range_start, $range_last, some_value
'''
def __init__(self):
gdb.Command.__init__ (self,
"heap range run",
gdb.COMMAND_DATA,
gdb.COMPLETE_COMMAND)

def invoke(self, args, from_tty):
import ranges
for r in ranges.merged_ranges():
gdb.execute("set $range_start=%s" % fmt_addr(r.start))
gdb.execute("set $range_last=%s" % fmt_addr(r.last))
gdb.execute("set $range_end=%s" % fmt_addr(r.end))
gdb.execute(args, from_tty = True)

class HeapFind(gdb.Command):
'''Find stuff anywhere; supplies ranges to the find command

Example: heap find /g some_callback_function
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this stuff need to be put in the help section?

'''
def __init__(self):
gdb.Command.__init__ (self,
"heap find",
gdb.COMMAND_DATA)

def invoke(self, args, from_tty):
import ranges
import re
from heap.compat import execute
FIND_SWITCHES = re.compile('^((?:\s*/\w+)*)(.*)$')
(switches, args) = FIND_SWITCHES.match(args).groups()
while len(args) > 0 and args[0][0] == '/':
switches.append(args.pop(0))
sumfound = 0
for r in ranges.merged_ranges():
result = execute('find%s %s, %s, %s' % (switches, fmt_addr(r.start), fmt_addr(r.last), args))
numfound = int(gdb.parse_and_eval('$numfound'))
sumfound += numfound
if numfound > 0:
result = result.splitlines()
result.pop() # no summary
for l in result:
print(l)
if sumfound > 0:
print ('%s patterns found' % sumfound)
else:
print ('Pattern not found')

class Hexdump(gdb.Command):
'Print a hexdump, starting at the specific region of memory'
def __init__(self):
Expand Down Expand Up @@ -354,6 +425,10 @@ def register_commands():
HeapSelect()
HeapArenas()
HeapArenaSelect()
HeapRange()
HeapRangeRun()
HeapFind()

Hexdump()

from heap.cpython import register_commands as register_cpython_commands
Expand Down
76 changes: 76 additions & 0 deletions heap/ranges.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2015 Stefan Bühler
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

from heap import caching_lookup_type, fmt_addr

class Range(object):
'''Memory range'''

def __init__(self, start, end):
if start > end:
raise ValueError("Invalid range")
self.start = start
self.end = end

def __str__(self):
return ('%s - %s') % (fmt_addr(self.start), fmt_addr(self.end))

@property
def last(self):
return self.end - 1

@property
def empty(self):
return self.start == self.end

def __cmp__(self, other):
return cmp((self.start, self.end), (other.start, other.end))

def ranges():
import re
PARSE_RANGE_LINE = re.compile('^\t(0x[0-9a-fA-F]+) - (0x[0-9a-fA-F]+)')

from heap.compat import execute

result = []
for line in execute('info file').splitlines():
match = PARSE_RANGE_LINE.match(line)
if not match: continue
(start, end) = match.groups()
result.append(Range(int(start, 16), int(end, 16)))
return result

def merged_ranges():
'''merge neighbours, drop empty ranges'''
cur_range = None
result = []
for r in sorted(ranges()):
if r.empty: continue
if cur_range:
if cur_range.end >= r.start:
# merge range
cur_range = Range(cur_range.start, max(cur_range.end, r.end))
else:
# no overlap, move forward
result.append(cur_range)
cur_range = r
else:
cur_range = r
if cur_range:
result.append(cur_range)

return result