#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

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):
    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):
    def __init__(self):
        super().__init__("prof", gdb.COMMAND_USER)

    def invoke(self, args, tty):
        try:
            StartPoint(args)
        except Exception as e:
            print(e)

Profile()




import pwd
import grp
import stat
import time

from datetime import datetime


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)
