helpers.common.memory: add print_annotated_memory() helper

This is similar to print_annotated_stack() except that it works on an
arbitrary memory range. It's useful for trying to find some context in
mystery memory.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2024-04-02 17:24:31 -07:00
parent 1232a404c6
commit c7717280ad
3 changed files with 84 additions and 3 deletions

View File

@ -9,15 +9,19 @@ The ``drgn.helpers.common.memory`` module provides helpers for working with memo
"""
import operator
import typing
from typing import Optional
import drgn
from drgn import IntegerLike, Program, SymbolKind
from drgn import IntegerLike, PlatformFlags, Program, SymbolKind
from drgn.helpers.common.format import escape_ascii_string
from drgn.helpers.common.prog import takes_program_or_default
from drgn.helpers.linux.slab import slab_object_info
__all__ = ("identify_address",)
__all__ = (
"identify_address",
"print_annotated_memory",
)
_SYMBOL_KIND_STR = {
@ -84,3 +88,63 @@ def identify_address(prog: Program, addr: IntegerLike) -> Optional[str]:
symbol_kind = _SYMBOL_KIND_STR.get(symbol.kind, "symbol")
return f"{symbol_kind}: {symbol.name}+{offset}"
@takes_program_or_default
def print_annotated_memory(
prog: Program, address: IntegerLike, size: IntegerLike, physical: bool = False
) -> None:
"""
Print the contents of a range of memory, annotating values that can be
identified.
Currently, this will identify any addresses in the memory range with
:func:`~drgn.helpers.common.memory.identify_address()`.
See :func:`~drgn.helpers.common.stack.print_annotated_stack()` for a
similar function that annotates stack traces.
>>> print_annotated_memory(0xffffffff963eb200, 56)
ADDRESS VALUE
ffffffff963eb200: 00000000000000b8
ffffffff963eb208: 000000000000a828
ffffffff963eb210: 0000000000000000
ffffffff963eb218: ffff8881042948e0 [slab object: mnt_cache+0x20]
ffffffff963eb220: ffff88810074a540 [slab object: dentry+0x0]
ffffffff963eb228: ffff8881042948e0 [slab object: mnt_cache+0x20]
ffffffff963eb230: ffff88810074a540 [slab object: dentry+0x0]
:param address: Starting address.
:param size: Number of bytes to read.
:param physical: Whether *address* is a physical memory address. If
``False``, then it is a virtual memory address.
"""
address = operator.index(address)
mem = prog.read(address, size, physical)
# The platform must be known if we were able to read memory.
assert prog.platform is not None
byteorder: 'typing.Literal["little", "big"]'
if prog.platform.flags & PlatformFlags.IS_LITTLE_ENDIAN:
byteorder = "little"
else:
byteorder = "big"
if prog.platform.flags & PlatformFlags.IS_64_BIT:
word_size = 8
line_format = "{:016x}: {:016x}{}"
print("ADDRESS VALUE")
else:
word_size = 4
line_format = "{:08x}: {:08x}{}"
print("ADDRESS VALUE")
for offset in range(0, len(mem), word_size):
value = int.from_bytes(mem[offset : offset + word_size], byteorder)
identified = identify_address(prog, value)
if identified is None:
identified = ""
else:
identified = f" [{identified}]"
print(line_format.format(address + offset, value, identified))

View File

@ -23,6 +23,9 @@ def print_annotated_stack(trace: StackTrace) -> None:
Currently, this will identify any addresses on the stack with
:func:`~drgn.helpers.common.memory.identify_address()`.
See :func:`~drgn.helpers.common.memory.print_annotated_memory()` for a
similar function that annotates arbitrary memory ranges.
>>> print_annotated_stack(stack_trace(1))
STACK POINTER VALUE
[stack frame #0 at 0xffffffff8dc93c41 (__schedule+0x429/0x488) in context_switch at ./kernel/sched/core.c:5209:2 (inlined)]

View File

@ -4,7 +4,8 @@
from contextlib import redirect_stdout
import io
from drgn.helpers.common.memory import identify_address
from drgn import sizeof
from drgn.helpers.common.memory import identify_address, print_annotated_memory
from drgn.helpers.common.stack import print_annotated_stack
from drgn.helpers.linux.mm import pfn_to_virt
from tests.linux_kernel import (
@ -63,6 +64,19 @@ class TestIdentifyAddress(LinuxKernelTestCase):
self.assertIsNone(identify_address(self.prog, self.prog["drgn_test_va"]))
class TestPrintAnnotatedMemory(LinuxKernelTestCase):
@skip_unless_have_test_kmod
def test_print_annotated_memory(self):
f = io.StringIO()
with redirect_stdout(f):
print_annotated_memory(
self.prog,
self.prog["drgn_test_small_slab_objects"].address_,
sizeof(self.prog["drgn_test_small_slab_objects"]),
)
self.assertIn("slab object: drgn_test_small+0x0", f.getvalue())
class TestPrintAnnotatedStack(LinuxKernelTestCase):
@skip_unless_have_stack_tracing
@skip_unless_have_test_kmod