283 lines
7.3 KiB
Python
283 lines
7.3 KiB
Python
#from https://www.pythonsheets.com/appendix/python-gdb.html
|
|
# try help user-defined from gdb for desc
|
|
|
|
import gdb
|
|
import time
|
|
import re
|
|
import json
|
|
import pwd
|
|
import grp
|
|
import stat
|
|
import time
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
class DumpMemory(gdb.Command):
|
|
"""Dump memory info into a file."""
|
|
|
|
def __init__(self):
|
|
super().__init__("dm", gdb.COMMAND_USER)
|
|
|
|
def get_addr(self, p, tty):
|
|
"""Get memory addresses."""
|
|
cmd = "info proc mappings"
|
|
out = gdb.execute(cmd, tty, True)
|
|
addrs = []
|
|
for l in out.split("\n"):
|
|
if re.match(f".*{p}*", l):
|
|
s, e, *_ = l.split()
|
|
addrs.append((s, e))
|
|
return addrs
|
|
|
|
def dump(self, addrs):
|
|
"""Dump memory result."""
|
|
if not addrs:
|
|
return
|
|
|
|
for s, e in addrs:
|
|
f = int(time.time() * 1000)
|
|
gdb.execute(f"dump memory {f}.bin {s} {e}")
|
|
|
|
def invoke(self, args, tty):
|
|
try:
|
|
# cat /proc/self/maps
|
|
addrs = self.get_addr(args, tty)
|
|
# dump memory
|
|
self.dump(addrs)
|
|
except Exception as e:
|
|
print("Usage: dm [pattern]\n\twhith pattern : heap, stack or any value in /proc/self/maps")
|
|
|
|
|
|
DumpMemory()
|
|
|
|
|
|
class DumpJson(gdb.Command):
|
|
"""Dump std::string as a styled JSON."""
|
|
|
|
def __init__(self):
|
|
super().__init__("dj", gdb.COMMAND_USER)
|
|
|
|
def get_json(self, args):
|
|
"""Parse std::string to JSON string."""
|
|
ret = gdb.parse_and_eval(args)
|
|
typ = str(ret.type)
|
|
if re.match("^std::.*::string", typ):
|
|
return json.loads(str(ret))
|
|
return None
|
|
|
|
def invoke(self, args, tty):
|
|
try:
|
|
# string to json string
|
|
s = self.get_json(args)
|
|
# json string to object
|
|
o = json.loads(s)
|
|
print(json.dumps(o, indent=2))
|
|
except Exception as e:
|
|
print(f"Parse json error! {args}")
|
|
|
|
|
|
DumpJson()
|
|
|
|
tp = {}
|
|
|
|
|
|
class Tracepoint(gdb.Breakpoint):
|
|
|
|
def __init__(self, *args):
|
|
super().__init__(*args)
|
|
self.silent = True
|
|
self.count = 0
|
|
|
|
def stop(self):
|
|
self.count += 1
|
|
frame = gdb.newest_frame()
|
|
block = frame.block()
|
|
sym_and_line = frame.find_sal()
|
|
framename = frame.name()
|
|
filename = sym_and_line.symtab.filename
|
|
line = sym_and_line.line
|
|
# show tracepoint info
|
|
print(f"{framename} @ {filename}:{line}")
|
|
# show args and vars
|
|
for s in block:
|
|
if not s.is_argument and not s.is_variable:
|
|
continue
|
|
typ = s.type
|
|
val = s.value(frame)
|
|
size = typ.sizeof
|
|
name = s.name
|
|
print(f"\t{name}({typ}: {val}) [{size}]")
|
|
# do not stop at tracepoint
|
|
return False
|
|
|
|
|
|
class SetTracepoint(gdb.Command):
|
|
""" Everytime the function in parameter get called, a message is printed with the argument used """
|
|
|
|
def __init__(self):
|
|
super().__init__("tp", gdb.COMMAND_USER)
|
|
|
|
def invoke(self, args, tty):
|
|
try:
|
|
global tp
|
|
tp[args] = Tracepoint(args)
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
|
|
def finish(event):
|
|
for t, p in tp.items():
|
|
c = p.count
|
|
print(f"Tracepoint '{t}' Count: {c}")
|
|
|
|
|
|
gdb.events.exited.connect(finish)
|
|
SetTracepoint()
|
|
|
|
|
|
class EndPoint(gdb.FinishBreakpoint):
|
|
def __init__(self, breakpoint, *a, **kw):
|
|
super().__init__(*a, **kw)
|
|
self.silent = True
|
|
self.breakpoint = breakpoint
|
|
|
|
def stop(self):
|
|
# normal finish
|
|
end = time.time()
|
|
start, out = self.breakpoint.stack.pop()
|
|
diff = end - start
|
|
print(out.strip())
|
|
print(f"\tCost: {diff}")
|
|
return False
|
|
|
|
|
|
class StartPoint(gdb.Breakpoint):
|
|
def __init__(self, *a, **kw):
|
|
super().__init__(*a, **kw)
|
|
self.silent = True
|
|
self.stack = []
|
|
|
|
def stop(self):
|
|
start = time.time()
|
|
# start, end, diff
|
|
frame = gdb.newest_frame()
|
|
sym_and_line = frame.find_sal()
|
|
func = frame.function().name
|
|
filename = sym_and_line.symtab.filename
|
|
line = sym_and_line.line
|
|
block = frame.block()
|
|
|
|
args = []
|
|
for s in block:
|
|
if not s.is_argument:
|
|
continue
|
|
name = s.name
|
|
typ = s.type
|
|
val = s.value(frame)
|
|
args.append(f"{name}: {val} [{typ}]")
|
|
|
|
# format
|
|
out = ""
|
|
out += f"{func} @ {filename}:{line}\n"
|
|
for a in args:
|
|
out += f"\t{a}\n"
|
|
|
|
# append current status to a breakpoint stack
|
|
self.stack.append((start, out))
|
|
EndPoint(self, internal=True)
|
|
return False
|
|
|
|
|
|
class Profile(gdb.Command):
|
|
""" Profile execution time of a function """
|
|
|
|
def __init__(self):
|
|
super().__init__("prof", gdb.COMMAND_USER)
|
|
|
|
def invoke(self, args, tty):
|
|
try:
|
|
StartPoint(args)
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
|
|
Profile()
|
|
|
|
|
|
class StatPrint:
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def get_filetype(self, st_mode):
|
|
if stat.S_ISDIR(st_mode):
|
|
return "directory"
|
|
if stat.S_ISCHR(st_mode):
|
|
return "character device"
|
|
if stat.S_ISBLK(st_mode):
|
|
return "block device"
|
|
if stat.S_ISREG:
|
|
return "regular file"
|
|
if stat.S_ISFIFO(st_mode):
|
|
return "FIFO"
|
|
if stat.S_ISLNK(st_mode):
|
|
return "symbolic link"
|
|
if stat.S_ISSOCK(st_mode):
|
|
return "socket"
|
|
return "unknown"
|
|
|
|
def get_access(self, st_mode):
|
|
out = "-"
|
|
info = ("r", "w", "x")
|
|
perm = [
|
|
(stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR),
|
|
(stat.S_IRGRP, stat.S_IRWXG, stat.S_IXGRP),
|
|
(stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH),
|
|
]
|
|
for pm in perm:
|
|
for c, p in zip(pm, info):
|
|
out += p if st_mode & c else "-"
|
|
return out
|
|
|
|
def get_time(self, st_time):
|
|
tv_sec = int(st_time["tv_sec"])
|
|
return datetime.fromtimestamp(tv_sec).isoformat()
|
|
|
|
def to_string(self):
|
|
st = self.val
|
|
st_ino = int(st["st_ino"])
|
|
st_mode = int(st["st_mode"])
|
|
st_uid = int(st["st_uid"])
|
|
st_gid = int(st["st_gid"])
|
|
st_size = int(st["st_size"])
|
|
st_blksize = int(st["st_blksize"])
|
|
st_blocks = int(st["st_blocks"])
|
|
st_atim = st["st_atim"]
|
|
st_mtim = st["st_mtim"]
|
|
st_ctim = st["st_ctim"]
|
|
|
|
out = "{\n"
|
|
out += f"Size: {st_size}\n"
|
|
out += f"Blocks: {st_blocks}\n"
|
|
out += f"IO Block: {st_blksize}\n"
|
|
out += f"Inode: {st_ino}\n"
|
|
out += f"Access: {self.get_access(st_mode)}\n"
|
|
out += f"File Type: {self.get_filetype(st_mode)}\n"
|
|
out += f"Uid: ({st_uid}/{pwd.getpwuid(st_uid).pw_name})\n"
|
|
out += f"Gid: ({st_gid}/{grp.getgrgid(st_gid).gr_name})\n"
|
|
out += f"Access: {self.get_time(st_atim)}\n"
|
|
out += f"Modify: {self.get_time(st_mtim)}\n"
|
|
out += f"Change: {self.get_time(st_ctim)}\n"
|
|
out += "}"
|
|
return out
|
|
|
|
|
|
# Could be disabled at runtime with:
|
|
# disable pretty-print global sp
|
|
# then check "info pretty-print"
|
|
p = gdb.printing.RegexpCollectionPrettyPrinter("sp")
|
|
p.add_printer("stat", "^stat$", StatPrint)
|
|
|
|
o = gdb.current_objfile()
|
|
gdb.printing.register_pretty_printer(o, p)
|