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