gdb/memdump: add option to parse memdump log from device

igned-off-by: xuxingliang <xuxingliang@xiaomi.com>
This commit is contained in:
xuxingliang 2024-11-21 20:34:03 +08:00 committed by Xiang Xiao
parent 06a696076a
commit f849092ede

View file

@ -21,6 +21,7 @@
############################################################################
import argparse
import re
from collections import defaultdict
from typing import Dict, Generator, List, Protocol, Tuple
@ -75,7 +76,7 @@ def dump_nodes(
filters=None,
) -> Generator[MMNodeDump, None, None]:
no_heap = filters and filters.get("no_heap")
no_pool = filters and filters.get("no_heap")
no_pool = filters and filters.get("no_pool")
if not no_heap:
yield from (
@ -150,6 +151,64 @@ def get_heaps(args_heap=None) -> List[mm.MMHeap]:
return mm.get_heaps()
def parse_memdump_log(logfile, filters=None) -> Generator[MMNodeDump, None, None]:
nodes = []
class DumpNode(MMNodeDump):
def __init__(self, address, nodesize, seqno, pid, backtrace, is_free, overhead):
# C code dump the start address of the node, convert it the actual start address
self.address = address - overhead
self.nodesize = nodesize
self.seqno = seqno
self.pid = pid
self.backtrace = backtrace
self.overhead = overhead
self.is_free = False
self.from_pool = False
def __repr__(self) -> str:
return f"node@{self.address:#x}: size:{self.nodesize} seq:{self.seqno} pid:{self.pid} "
def contains(self, addr: int) -> bool:
return self.address <= addr < self.address + self.nodesize
@property
def prevnode(self):
return next(
(node for node in nodes if node.contains(self.address - 1)), None
)
@property
def nextnode(self):
address = self.address + self.nodesize # address of the next node
return next(
(node for node in nodes if node.address == address),
None,
)
with open(logfile) as f:
for line in f:
match = re.search(
r"\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+((?:\s+0x[0-9a-fA-F]+)+)", line
)
if not match:
continue
try:
pid = int(match.group(1))
size = int(match.group(2))
overhead = int(match.group(3))
seq = int(match.group(4))
addresses = match.group(5).split()
addr = int(addresses[0], base=16)
mem = tuple(int(addr, base=16) for addr in addresses[1:])
nodes.append(DumpNode(addr, size, seq, pid, mem, False, overhead))
except Exception as e:
print(f"Error parsing line: {line}, {e}")
return filter(filter_node(**filters), nodes) if filters else nodes
class MMDump(gdb.Command):
"""Dump memory manager heap"""
@ -168,6 +227,14 @@ class MMDump(gdb.Command):
help="Find the node that contains the address and exit",
)
parser.add_argument(
"-l",
"--log",
type=str,
default=None,
help="Use the memdump log file generated by memdump command on device instead of live dump",
)
parser.add_argument(
"--heap",
type=str,
@ -237,21 +304,30 @@ class MMDump(gdb.Command):
def find_address(self, addr, heap=None, log=None):
"""Find the node that contains the address from memdump log or live dump."""
addr = int(gdb.parse_and_eval(addr))
heaps = [mm.MMHeap(gdb.parse_and_eval(heap))] if heap else mm.get_heaps()
if log:
nodes = parse_memdump_log(log)
node = next((node for node in nodes if node.contains(addr)), None)
else:
heaps = [mm.MMHeap(gdb.parse_and_eval(heap))] if heap else mm.get_heaps()
# Find pool firstly
node = next(
(blk for pool in mm.get_pools(heaps) if (blk := pool.find(addr))), None
)
# Find pool firstly
node = next(
(blk for pool in mm.get_pools(heaps) if (blk := pool.find(addr))), None
)
# Try heap if not found in pool
node = node or next((node for heap in heaps if (node := heap.find(addr))), None)
# Try heap if not found in pool
node = node or next(
(node for heap in heaps if (node := heap.find(addr))), None
)
return addr, node
def collect_nodes(self, heap, log=None, filters=None):
heaps = [mm.MMHeap(gdb.parse_and_eval(heap))] if heap else mm.get_heaps()
nodes = dump_nodes(heaps, filters)
if log:
nodes = parse_memdump_log(log, filters=filters)
else:
heaps = [mm.MMHeap(gdb.parse_and_eval(heap))] if heap else mm.get_heaps()
nodes = dump_nodes(heaps, filters)
return nodes