# 0.0.22 (Released January 5th, 2023)

These are some of the highlights of drgn 0.0.22. See the GitHub release for the full release notes, including more improvements and bug fixes.

## Listing Stack Frame Locals

`drgn.StackFrame.locals()` returns the names of all arguments and local variables in the scope of a stack frame. This allows you to get a quick idea of what’s going on in a function without needing to read the source code right away.

Let’s use the `__schedule` stack frame from the following trace as an example:

    >>> trace = prog.stack_trace(1)
    >>> trace
    #0  context_switch (./kernel/sched/core.c:5209:2)
    #1  __schedule (./kernel/sched/core.c:6521:8)
    #2  schedule (./kernel/sched/core.c:6597:3)
    #3  do_wait (./kernel/exit.c:1562:4)
    #4  kernel_wait4 (./kernel/exit.c:1706:8)
    #5  __do_sys_wait4 (./kernel/exit.c:1734:13)
    #6  do_syscall_x64 (./arch/x86/entry/common.c:50:14)
    #7  do_syscall_64 (./arch/x86/entry/common.c:80:7)
    #8  entry_SYSCALL_64+0x9b/0x197 (./arch/x86/entry/entry_64.S:120)
    #9  0x7f6a34a00057
    >>> trace[1].locals()
    ['sched_mode', 'prev', 'next', 'switch_count', 'prev_state', 'rf', 'rq', 'cpu']
    >>> for name in trace[1].locals():
    ...     print(name, trace[1][name].format_(dereference=False))
    ...
    sched_mode (unsigned int)0
    prev (struct task_struct *)0xffffa3b601178000
    next (struct task_struct *)0xffffa3b6026db800
    switch_count (unsigned long *)0xffffa3b601178528
    prev_state (unsigned long)<absent>
    rf (struct rq_flags){
            .flags = (unsigned long)1,
            .cookie = (struct pin_cookie){},
            .clock_update_flags = (unsigned int)4,
    }
    rq (struct rq *)0xffffa3b67fda9640
    cpu (int)<absent>

Compare this to the kernel source code. Note that some of the variables have been optimized out by the compiler.

This feature was contributed by Stephen Brennan.

## Merged Slab Caches

The Linux kernel slab allocator merges “similar” slab caches as an optimization, which often causes confusion. `slab_cache_is_merged()` (added back in 0.0.20) returns whether or not a slab cache has been merged, but not what it was merged with. In this release, Stephen Brennan added `get_slab_cache_aliases()`, which provides a mapping from a slab cache name to the name of the cache it was merged into:

    >>> get_slab_cache_aliases(prog)
    {'io_kiocb': 'maple_node', 'ip_dst_cache': 'uid_cache', 'aio_kiocb': 'uid_cache', 'ip_fib_alias': 'Acpi-Parse', 'pid_namespace': 'pid', 'iommu_iova': 'vmap_area', 'fasync_cache': 'ftrace_event_field', 'dnotify_mark': 'Acpi-State', 'tcp_bind2_bucket': 'vmap_area', 'nsproxy': 'Acpi-Operand', 'shared_policy_node': 'ftrace_event_field', 'eventpoll_epi': 'pid', 'fib6_nodes': 'vmap_area', 'Acpi-Namespace': 'ftrace_event_field', 'posix_timers_cache': 'maple_node', 'inotify_inode_mark': 'Acpi-State', 'kernfs_iattrs_cache': 'trace_event_file', 'fs_cache': 'vmap_area', 'UDP-Lite': 'UDP', 'anon_vma_chain': 'vmap_area', 'ip6_dst_cache': 'maple_node', 'eventpoll_pwq': 'vmap_area', 'inet_peer_cache': 'uid_cache', 'fsnotify_mark_connector': 'numa_policy', 'ip_fib_trie': 'ftrace_event_field', 'filp': 'maple_node', 'dnotify_struct': 'numa_policy', 'UDPLITEv6': 'UDPv6', 'biovec-16': 'maple_node', 'PING': 'signal_cache', 'ep_head': 'blkdev_ioc', 'tcp_bind_bucket': 'pid', 'Acpi-ParseExt': 'Acpi-State', 'cred_jar': 'pid', 'ovl_aio_req': 'pid', 'pool_workqueue': 'maple_node', 'sigqueue': 'Acpi-State', 'file_lock_ctx': 'Acpi-Parse', 'kernfs_node_cache': 'pid'}

This means that if you’re looking for `io_kiocb` allocations, you actually need to look at the `maple_node` slab cache. Conversely, if you’re looking at the `maple_node` slab cache, you need to be aware that it also contains allocations from all of the following slab caches:

    >>> [merged for merged, canonical in get_slab_cache_aliases(prog).items() if canonical == "maple_node"]
    ['io_kiocb', 'posix_timers_cache', 'ip6_dst_cache', 'filp', 'biovec-16', 'pool_workqueue']

## Slab Address Information

This release extended `identify_address()` to show additional information about slab allocations:

    >>> ptr1 = 0xffffa3b601178438
    >>> ptr2 = 0xffffa3b601176cc0
    >>> identify_address(prog, ptr1)
    'slab object: task_struct+0x438'
    >>> identify_address(prog, ptr2)
    'free slab object: mm_struct+0x0'

This means that `ptr1` is an address 0x438 bytes into an allocated object from the `task_struct` slab cache, and `ptr2` is a free object from the `mm_struct` slab cache.

`slab_object_info()` provides the same information programmatically:

    >>> slab_object_info(prog, ptr1)
    SlabObjectInfo(slab_cache=Object(prog, 'struct kmem_cache *', value=0xffffa3b601045500), slab=Object(prog, 'struct slab *', value=0xffffe80840045e00), address=0xffffa3b601178000, allocated=True)
    >>> slab_object_info(prog, ptr2)
    SlabObjectInfo(slab_cache=Object(prog, 'struct kmem_cache *', value=0xffffa3b601045900), slab=Object(prog, 'struct slab *', value=0xffffe80840045c00), address=0xffffa3b601176cc0, allocated=False)

## Annotated Stack Memory

`print_annotated_stack()` prints a stack trace and all of its memory, identifying anything that it can:

    >>> print_annotated_stack(prog.stack_trace(1))
    STACK POINTER     VALUE
    [stack frame #0 at 0xffffffffaf8a68e9 (__schedule+0x429/0x488) in context_switch at ./kernel/sched/core.c:5209:2 (inlined)]
    [stack frame #1 at 0xffffffffaf8a68e9 (__schedule+0x429/0x488) in __schedule at ./kernel/sched/core.c:6521:8]
    ffffbb1ac0013d28: ffffffffaf4498f5 [function symbol: __flush_tlb_one_user+0x5]
    ffffbb1ac0013d30: 00000000af449feb
    ffffbb1ac0013d38: 0000000000000001
    ffffbb1ac0013d40: 0000000000000004
    ffffbb1ac0013d48: 25c5ff9539edc200
    ffffbb1ac0013d50: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013d58: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013d60: ffffbb1ac0013e10
    ffffbb1ac0013d68: ffffa3b601177ff0 [slab object: mm_struct+0x70]
    ffffbb1ac0013d70: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013d78: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013d80: ffffffffaf8a69d1 [function symbol: schedule+0x89]
    [stack frame #2 at 0xffffffffaf8a69d1 (schedule+0x89/0xc7) in schedule at ./kernel/sched/core.c:6597:3]
    ffffbb1ac0013d88: ffffbb1ac0013de8
    ffffbb1ac0013d90: 0000000000000000
    ffffbb1ac0013d98: ffffffffaf4595ee [function symbol: do_wait+0x231]
    [stack frame #3 at 0xffffffffaf4595ee (do_wait+0x231/0x2e3) in do_wait at ./kernel/exit.c:1562:4]
    ffffbb1ac0013da0: ffffa3b601178450 [slab object: task_struct+0x450]
    ffffbb1ac0013da8: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013db0: 0000000000000004
    ffffbb1ac0013db8: 0000000000000000
    ffffbb1ac0013dc0: 00007ffe0984a170
    ffffbb1ac0013dc8: 0000000000000000
    ffffbb1ac0013dd0: fffffffffffffffd
    ffffbb1ac0013dd8: 0000000000000004
    ffffbb1ac0013de0: ffffffffaf45a42f [function symbol: kernel_wait4+0xc2]
    [stack frame #4 at 0xffffffffaf45a42f (kernel_wait4+0xc2/0x11b) in kernel_wait4 at ./kernel/exit.c:1706:8]
    ffffbb1ac0013de8: 0000000400000004
    ffffbb1ac0013df0: 0000000000000000
    ffffbb1ac0013df8: 0000000000000000
    ffffbb1ac0013e00: 0000000000000000
    ffffbb1ac0013e08: 0000000000000000
    ffffbb1ac0013e10: ffffffff00000000
    ffffbb1ac0013e18: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013e20: ffffffffaf45890c [function symbol: child_wait_callback+0x0]
    ffffbb1ac0013e28: ffffa3b601188028 [slab object: signal_cache+0x28]
    ffffbb1ac0013e30: ffffa3b601188028 [slab object: signal_cache+0x28]
    ffffbb1ac0013e38: 000055d500000000
    ffffbb1ac0013e40: 25c5ff9539edc200
    ffffbb1ac0013e48: 0000000000000000
    ffffbb1ac0013e50: ffffbb1ac0013f30
    ffffbb1ac0013e58: ffffbb1ac0013f58
    ffffbb1ac0013e60: 0000000000000000
    ffffbb1ac0013e68: 0000000000000000
    ffffbb1ac0013e70: 0000000000000000
    ffffbb1ac0013e78: ffffffffaf45a4c0 [function symbol: __do_sys_wait4+0x38]
    [stack frame #5 at 0xffffffffaf45a4c0 (__do_sys_wait4+0x38/0x8c) in __do_sys_wait4 at ./kernel/exit.c:1734:13]
    ffffbb1ac0013e80: ffffffffaf8aaa21 [function symbol: _raw_spin_unlock_irq+0x10]
    ffffbb1ac0013e88: ffffffffaf46460c [function symbol: do_sigaction+0xf8]
    ffffbb1ac0013e90: ffffa3b601180020 [slab object: sighand_cache+0x20]
    ffffbb1ac0013e98: ffffa3b6028d02d0 [slab object: vm_area_struct+0x0]
    ffffbb1ac0013ea0: 25c5ff9539edc200
    ffffbb1ac0013ea8: 0000000000000002
    ffffbb1ac0013eb0: 00007ffe09849fb0
    ffffbb1ac0013eb8: ffffbb1ac0013f58
    ffffbb1ac0013ec0: 0000000000000000
    ffffbb1ac0013ec8: 0000000000000000
    ffffbb1ac0013ed0: 0000000000000046
    ffffbb1ac0013ed8: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013ee0: ffffa3b601178000 [slab object: task_struct+0x0]
    ffffbb1ac0013ee8: ffffbb1ac0013f58
    ffffbb1ac0013ef0: 0000000000000000
    ffffbb1ac0013ef8: ffffffffaf426def [function symbol: fpregs_assert_state_consistent+0x1b]
    ffffbb1ac0013f00: 0000000000000000
    ffffbb1ac0013f08: ffffffffaf4b2f53 [function symbol: exit_to_user_mode_prepare+0xa6]
    ffffbb1ac0013f10: 0000000000000000
    ffffbb1ac0013f18: 25c5ff9539edc200
    ffffbb1ac0013f20: ffffbb1ac0013f58
    ffffbb1ac0013f28: 0000000000000000
    ffffbb1ac0013f30: ffffbb1ac0013f48
    ffffbb1ac0013f38: ffffffffaf8a1573 [function symbol: do_syscall_64+0x70]
    [stack frame #6 at 0xffffffffaf8a1573 (do_syscall_64+0x70/0x8a) in do_syscall_x64 at ./arch/x86/entry/common.c:50:14 (inlined)]
    [stack frame #7 at 0xffffffffaf8a1573 (do_syscall_64+0x70/0x8a) in do_syscall_64 at ./arch/x86/entry/common.c:80:7]
    ffffbb1ac0013f40: 0000000000000000
    ffffbb1ac0013f48: 0000000000000000
    ffffbb1ac0013f50: ffffffffafa0009b [symbol: entry_SYSCALL_64+0x9b]
    [stack frame #8 at 0xffffffffafa0009b (entry_SYSCALL_64+0x9b/0x197) at ./arch/x86/entry/entry_64.S:120]
    ffffbb1ac0013f58: 0000000000000000
    [stack frame #9 at 0x7f6a34a00057]

Like `drgn.StackFrame.locals()`, this provides a nice overview of everything happening in a function, which might include useful hints. Keep in mind that it may identify “stale” addresses for anything that a function hasn’t reinitialized yet, and as always, be careful of slab cache merging.

This was inspired by the crash `bt -FF` command. It was contributed by Nhat Pham.

## XArray Helpers

XArrays were introduced in Linux 4.20 as a replacement for radix trees. drgn’s radix tree helpers also support XArrays in some cases, but this is awkward, not obvious, and doesn’t work for new, XArray-only functionality.

This release added dedicated XArray helpers like `xa_load()` and `xa_for_each()`.

## s390x Support

Sven Schnelle contributed s390x support for Linux kernel modules and stack traces. This is the state of architecture support in this release:

drgn 0.0.22 Architecture Support
Architecture | Linux Kernel Modules | Stack Traces | Virtual Address Translation
--- | --- | --- | ---
x86-64 | ✓ | ✓ | ✓
AArch64 | ✓ | ✓ | ✓
ppc64 | ✓ | ✓ |
s390x | ✓ | ✓ |
i386 | ✓ |  |
Arm | ✓ |  |
RISC-V | ✓ |  |

## Relicensing to LGPL

drgn was originally licensed as GPLv3+. In this release, it was changed to LGPLv2.1+. The motivation for this change was to enable the long term vision for drgn that more projects can use it as a library providing programmatic interfaces for debugger functionality. For example, Object Introspection, a userspace memory profiler recently open sourced by Meta, uses drgn to parse debugging information.