9eadf61e90
Signed-off-by: anjiahao <anjiahao@xiaomi.com>
311 lines
12 KiB
Python
311 lines
12 KiB
Python
############################################################################
|
|
# tools/gdb/memdump.py
|
|
#
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership. The
|
|
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance with the
|
|
# License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
############################################################################
|
|
|
|
import argparse
|
|
|
|
import gdb
|
|
from lists import sq_for_every, sq_queue
|
|
from utils import get_symbol_value
|
|
|
|
MM_ALLOC_BIT = 0x1
|
|
MM_PREVFREE_BIT = 0x2
|
|
MM_MASK_BIT = MM_ALLOC_BIT | MM_PREVFREE_BIT
|
|
MEMPOOL_MAGIC_ALLOC = 0x55555555
|
|
|
|
PID_MM_FREE = -4
|
|
PID_MM_ALLOC = -3
|
|
PID_MM_LEAK = -2
|
|
PID_MM_MEMPOOL = -1
|
|
|
|
|
|
def align_up(size, align) -> int:
|
|
"""Align the size to the specified alignment"""
|
|
return (size + (align - 1)) & ~(align - 1)
|
|
|
|
|
|
def mm_nodesize(size) -> int:
|
|
"""Return the real size of a memory node"""
|
|
return size & ~MM_MASK_BIT
|
|
|
|
|
|
def mm_foreach(heap):
|
|
"""Iterate over a heap, yielding each node"""
|
|
node = gdb.Value(heap["mm_heapstart"][0]).cast(
|
|
gdb.lookup_type("struct mm_allocnode_s").pointer()
|
|
)
|
|
while 1:
|
|
yield node
|
|
next = gdb.Value(node).cast(gdb.lookup_type("char").pointer())
|
|
next = gdb.Value(next + mm_nodesize(node["size"])).cast(
|
|
gdb.lookup_type("struct mm_allocnode_s").pointer()
|
|
)
|
|
if node >= heap["mm_heapend"].dereference() or next == node:
|
|
break
|
|
node = next
|
|
|
|
|
|
def mempool_multiple_foreach(mpool):
|
|
"""Iterate over all pools in a mempool, yielding each pool"""
|
|
i = 0
|
|
while i < mpool["npools"]:
|
|
pool = mpool["pools"] + i
|
|
yield pool
|
|
i += 1
|
|
|
|
|
|
def mempool_realblocksize(pool):
|
|
"""Return the real block size of a mempool"""
|
|
|
|
if get_symbol_value("CONFIG_MM_DFAULT_ALIGNMENT") == 0:
|
|
mempool_align = 2 * gdb.lookup_type("size_t").sizeof
|
|
else:
|
|
mempool_align = get_symbol_value("CONFIG_MM_DFAULT_ALIGNMENT")
|
|
|
|
if get_symbol_value("CONFIG_MM_BACKTRACE") >= 0:
|
|
return align_up(
|
|
pool["blocksize"] + gdb.lookup_type("struct mempool_backtrace_s").sizeof,
|
|
mempool_align,
|
|
)
|
|
else:
|
|
return pool["blocksize"]
|
|
|
|
|
|
def mempool_foreach(pool):
|
|
"""Iterate over all block in a mempool"""
|
|
|
|
blocksize = mempool_realblocksize(pool)
|
|
if pool["ibase"] != 0:
|
|
nblk = pool["interruptsize"] / blocksize
|
|
while nblk > 0:
|
|
bufaddr = gdb.Value(pool["ibase"] + nblk * blocksize + pool["blocksize"])
|
|
buf = bufaddr.cast(gdb.lookup_type("struct mempool_backtrace_s").pointer())
|
|
yield buf
|
|
nblk -= 1
|
|
|
|
entry = sq_queue.get_type().pointer()
|
|
for entry in sq_for_every(pool["equeue"], entry):
|
|
nblk = (pool["expandsize"] - gdb.lookup_type("sq_entry_t").sizeof) / blocksize
|
|
base = (
|
|
gdb.Value(entry).cast(gdb.lookup_type("char").pointer()) - nblk * blocksize
|
|
)
|
|
while nblk > 0:
|
|
bufaddr = gdb.Value(base + nblk * blocksize + pool["blocksize"])
|
|
buf = bufaddr.cast(gdb.lookup_type("struct mempool_backtrace_s").pointer())
|
|
yield buf
|
|
nblk -= 1
|
|
|
|
|
|
class Nxmemdump(gdb.Command):
|
|
"""Dump the heap and mempool memory"""
|
|
|
|
def __init__(self):
|
|
super(Nxmemdump, self).__init__("memdump", gdb.COMMAND_USER)
|
|
|
|
def mempool_dump(self, mpool, pid, seqmin, seqmax, address):
|
|
"""Dump the mempool memory"""
|
|
for pool in mempool_multiple_foreach(mpool):
|
|
if pid == PID_MM_FREE:
|
|
entry = sq_queue.get_type().pointer()
|
|
|
|
for entry in sq_for_every(pool["queue"], entry):
|
|
gdb.write("%12u%#*x\n" % (pool["blocksize"], self.align, entry))
|
|
self.aordblks += 1
|
|
self.uordblks += pool["blocksize"]
|
|
|
|
for entry in sq_for_every(pool["iqueue"], entry):
|
|
gdb.write("%12u%#*x\n" % (pool["blocksize"], self.align, entry))
|
|
self.aordblks += 1
|
|
self.uordblks += pool["blocksize"]
|
|
else:
|
|
for buf in mempool_foreach(pool):
|
|
if (
|
|
(pid == buf["pid"] or pid == PID_MM_ALLOC)
|
|
and (buf["seqno"] >= seqmin and buf["seqno"] < seqmax)
|
|
and buf["magic"] == MEMPOOL_MAGIC_ALLOC
|
|
):
|
|
charnode = gdb.Value(buf).cast(
|
|
gdb.lookup_type("char").pointer()
|
|
)
|
|
gdb.write(
|
|
"%6d%12u%12u%#*x"
|
|
% (
|
|
buf["pid"],
|
|
mm_nodesize(pool["blocksize"]),
|
|
buf["seqno"],
|
|
self.align,
|
|
(int)(charnode - pool["blocksize"]),
|
|
)
|
|
)
|
|
if buf.type.has_key("backtrace"):
|
|
max = buf["backtrace"].type.range()[1]
|
|
for x in range(0, max):
|
|
gdb.write(" ")
|
|
gdb.write(
|
|
buf["backtrace"][x].format_string(
|
|
raw=False, symbols=True, address=False
|
|
)
|
|
)
|
|
|
|
if address and (
|
|
address < int(charnode)
|
|
and address >= (int)(charnode - pool["blocksize"])
|
|
):
|
|
gdb.write(
|
|
"\nThe address 0x%x found belongs to"
|
|
"the mempool node with base address 0x%x\n"
|
|
% (address, charnode)
|
|
)
|
|
return True
|
|
|
|
gdb.write("\n")
|
|
self.aordblks += 1
|
|
self.uordblks += pool["blocksize"]
|
|
return False
|
|
|
|
def memdump(self, pid, seqmin, seqmax, address):
|
|
"""Dump the heap memory"""
|
|
if pid >= PID_MM_ALLOC:
|
|
gdb.write("Dump all used memory node info:\n")
|
|
gdb.write(
|
|
"%6s%12s%12s%*s %s\n"
|
|
% ("PID", "Size", "Sequence", self.align, "Address", "Callstack")
|
|
)
|
|
else:
|
|
gdb.write("Dump all free memory node info:\n")
|
|
gdb.write("%12s%*s\n" % ("Size", self.align, "Address"))
|
|
|
|
heap = gdb.parse_and_eval("g_mmheap")
|
|
if heap.type.has_key("mm_mpool"):
|
|
if self.mempool_dump(heap["mm_mpool"], pid, seqmin, seqmax, address):
|
|
return
|
|
|
|
for node in mm_foreach(heap):
|
|
if node["size"] & MM_ALLOC_BIT != 0:
|
|
if (
|
|
pid == node["pid"]
|
|
or (pid == PID_MM_ALLOC and node["pid"] != PID_MM_MEMPOOL)
|
|
) and (node["seqno"] >= seqmin and node["seqno"] < seqmax):
|
|
charnode = gdb.Value(node).cast(gdb.lookup_type("char").pointer())
|
|
gdb.write(
|
|
"%6d%12u%12u%#*x"
|
|
% (
|
|
node["pid"],
|
|
mm_nodesize(node["size"]),
|
|
node["seqno"],
|
|
self.align,
|
|
(int)(
|
|
charnode
|
|
+ gdb.lookup_type("struct mm_allocnode_s").sizeof
|
|
),
|
|
)
|
|
)
|
|
|
|
if node.type.has_key("backtrace"):
|
|
max = node["backtrace"].type.range()[1]
|
|
for x in range(0, max):
|
|
gdb.write(" ")
|
|
gdb.write(
|
|
node["backtrace"][x].format_string(
|
|
raw=False, symbols=True, address=False
|
|
)
|
|
)
|
|
|
|
gdb.write("\n")
|
|
|
|
if address and (
|
|
address < int(charnode + node["size"])
|
|
and address
|
|
>= (int)(
|
|
charnode + gdb.lookup_type("struct mm_allocnode_s").sizeof
|
|
)
|
|
):
|
|
gdb.write(
|
|
"\nThe address 0x%x found belongs to"
|
|
"the memory node with base address 0x%x\n"
|
|
% (address, charnode)
|
|
)
|
|
return
|
|
|
|
self.aordblks += 1
|
|
self.uordblks += mm_nodesize(node["size"])
|
|
else:
|
|
if pid == PID_MM_FREE:
|
|
charnode = gdb.Value(node).cast(gdb.lookup_type("char").pointer())
|
|
gdb.write(
|
|
"%12u%#*x\n"
|
|
% (
|
|
mm_nodesize(node["size"]),
|
|
self.align,
|
|
(int)(
|
|
charnode
|
|
+ gdb.lookup_type("struct mm_allocnode_s").sizeof
|
|
),
|
|
)
|
|
)
|
|
self.aordblks += 1
|
|
self.uordblks += mm_nodesize(node["size"])
|
|
|
|
gdb.write("%12s%12s\n" % ("Total Blks", "Total Size"))
|
|
gdb.write("%12d%12d\n" % (self.aordblks, self.uordblks))
|
|
|
|
def complete(self, text, word):
|
|
return gdb.COMPLETE_SYMBOL
|
|
|
|
def parse_arguments(self, argv):
|
|
parser = argparse.ArgumentParser(description="memdump command")
|
|
parser.add_argument("-p", "--pid", type=str, help="Thread PID")
|
|
parser.add_argument("-a", "--addr", type=str, help="Query memory address")
|
|
parser.add_argument("-i", "--min", type=str, help="Minimum value")
|
|
parser.add_argument("-x", "--max", type=str, help="Maximum value")
|
|
parser.add_argument("--used", action="store_true", help="Used flag")
|
|
parser.add_argument("--free", action="store_true", help="Free flag")
|
|
args = parser.parse_args(args=(None if len(argv) == 1 else argv))
|
|
return {
|
|
"pid": int(args.pid, 0) if args.pid else None,
|
|
"seqmin": int(args.min, 0) if args.min else 0,
|
|
"seqmax": int(args.max, 0) if args.max else 0xFFFFFFFF,
|
|
"used": args.used,
|
|
"free": args.free,
|
|
"addr": int(args.addr, 0) if args.addr else None,
|
|
}
|
|
|
|
def invoke(self, args, from_tty):
|
|
if gdb.lookup_type("size_t").sizeof == 4:
|
|
self.align = 11
|
|
else:
|
|
self.align = 19
|
|
|
|
arg = self.parse_arguments(args.split(" "))
|
|
|
|
pid = PID_MM_ALLOC
|
|
if arg["used"]:
|
|
pid = PID_MM_ALLOC
|
|
elif arg["free"]:
|
|
pid = PID_MM_LEAK
|
|
elif arg["pid"]:
|
|
pid = arg["pid"]
|
|
|
|
self.aordblks = 0
|
|
self.uordblks = 0
|
|
self.memdump(pid, arg["seqmin"], arg["seqmax"], arg["addr"])
|
|
|
|
|
|
Nxmemdump()
|