1
0
mirror of https://github.com/DataDog/go-profiler-notes.git synced 2025-07-15 23:54:16 +02:00
Files
go-profiler-notes/delve/stackannotate.star
Felix Geisendörfer 48693a2c50 updates
2021-03-22 15:17:01 +01:00

144 lines
3.3 KiB
Plaintext

word_size = 8 # 64bit only for now
def get_reg(name):
for req in registers().Regs:
if req.Name == name:
return int(req.Value, 16)
def read_word(addr):
v = 0
m = examine_memory(addr, word_size)
s = 1
for b in m.Mem:
v += b * s
s = s * 256
return v
def hex(d, n = 0):
if d == 0:
return lpad("0", n, "0")
lookup = {10: "a", 11: "b", 12: "c", 13: "d", 14: "e", 15: "e", 16: "f"}
s = ""
while d > 0:
r = d % 16
d = d // 16
if r >= 10:
r = lookup[r]
else:
r = str(r)
s = r + s
return lpad(s, n, "0")
def lpad(s, n, c = " "):
while len(s) < n:
s = c + s
return s
def rpad(s, n, c = " "):
while len(s) < n:
s = s + c
return s
def ascii_table(rows, align_right = {}):
widths = []
for row in rows:
for i, col in enumerate(row):
if len(widths) < i+1:
widths.append(0)
widths[i] = max(widths[i], len(col))
s = ""
for row in rows:
for i, col in enumerate(row):
width = widths[i]
if align_right.get(i, False):
col = lpad(col, width)
else:
col = rpad(col, width)
s += col + " "
s += "\n"
return s
def getg():
# TODO(fg) there is probably a better way to implement this.
g = raw_command("goroutine").State.SelectedGoroutine
for gp in eval(None, "runtime.allgs").Variable.Value:
if gp.goid == g.ID:
return gp
def stack():
g = getg()
bp = get_reg("Rbp")
ip = get_reg("Rip")
sp = get_reg("Rsp")
regs = {"sp": sp, "bp": bp, "ip": ip}
addr_list = []
addr_dict = {}
offset = 0
while True:
addr = g.stack.hi+offset-word_size
addr_info = {
"addr": addr,
"val": read_word(addr),
"offset": offset,
"regs": [],
"note": [],
"func": None,
"arg": None,
"local": None,
"fp": False,
}
for (name, val) in regs.items():
if addr == val:
addr_info["regs"].append(name)
addr_list.append(addr_info)
addr_dict[addr] = addr_info
offset -= word_size
if addr <= sp:
break
for f in stacktrace(g.goid, 128, True).Locations:
fp_addr = g.stack.hi+f.FramePointerOffset
if fp_addr > 0 and len(addr_dict[fp_addr]["note"]) == 0:
addr_dict[fp_addr]["note"].append("frame pointer for "+f.Function.Name_)
pc_addr = fp_addr+word_size
pc = read_word(pc_addr)
ins = disassemble(None, pc, pc+1).Disassemble[0]
addr_dict[pc_addr]["note"].append("return addr to "+ins.Loc.Function.Name_)
for arg in f.Arguments:
addr_dict[arg.Addr]["note"].append("arg "+arg.Name+" "+arg.Type)
for local in f.Locals:
addr = local.Addr // 8 * 8
if addr_dict.get(addr):
addr_dict[addr]["note"].append("var "+local.Name+" "+local.Type)
return addr_list
# stackannotate (alias sa) will print an annotated stack dump.
def command_stackannotate():
rows = [["regs", "addr", "offset", "value", "explanation"]]
for addr_info in stack():
regs = ""
if len(addr_info["regs"]) > 0:
regs = ",".join(addr_info["regs"])+" -->"
note = "?"
if len(addr_info["note"]) > 0:
note = ", ".join(addr_info["note"])
rows.append([
regs,
hex(addr_info["addr"]),
lpad(str(addr_info["offset"]), 6),
lpad(hex(addr_info["val"]), word_size*2+2),
note,
])
print(ascii_table(rows))
def main():
dlv_command("config alias stackannotate sa")