Skip to content

Commit dd48031

Browse files
author
Delphix Engineering
committed
Merge branch 'refs/heads/upstream-HEAD' into repo-HEAD
2 parents e247f03 + 6cff072 commit dd48031

File tree

6 files changed

+245
-16
lines changed

6 files changed

+245
-16
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
matrix:
4141
python-version: ${{ (github.event_name == 'push' || inputs.test_all_python_versions)
4242
&& fromJSON('["3.14", "3.13", "3.12", "3.11", "3.10", "3.9", "3.8"]')
43-
|| fromJSON('["3.13", "3.8"]')}}
43+
|| fromJSON('["3.14", "3.8"]')}}
4444
cc: [gcc, clang]
4545
fail-fast: false
4646
env:

contrib/search_kernel_memory.py

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import argparse
1111
import sys
1212

13-
from drgn import Object
13+
from drgn import FaultError, Object
1414
from drgn.helpers.common.memory import identify_address
1515
from drgn.helpers.linux.list import list_for_each_entry
1616
from drgn.helpers.linux.mm import for_each_vmap_area, virt_to_page
@@ -36,17 +36,35 @@ def virt_to_vmap_address(prog, addr):
3636

3737

3838
def search_memory(prog, needle):
39+
if isinstance(needle, Object):
40+
needle = needle.to_bytes_()
41+
3942
KCORE_RAM = prog["KCORE_RAM"]
4043
CHUNK_SIZE = 1024 * 1024
44+
PAGE_SIZE = prog["PAGE_SIZE"].value_()
4145
for kc in list_for_each_entry(
4246
"struct kcore_list", prog["kclist_head"].address_of_(), "list"
4347
):
4448
if kc.type != KCORE_RAM:
4549
continue
46-
start = kc.addr.value_()
47-
end = start + kc.size.value_()
48-
for addr in range(start, end, CHUNK_SIZE):
49-
buf = prog.read(addr, min(CHUNK_SIZE, end - addr))
50+
addr = kc.addr.value_()
51+
end = addr + kc.size.value_()
52+
while addr < end:
53+
try:
54+
buf = prog.read(addr, min(CHUNK_SIZE, end - addr))
55+
except FaultError:
56+
# We start with a large chunk size to reduce the overhead of
57+
# reading memory. However, if we're reading from a core dump,
58+
# reading with a large chunk size may fault on excluded pages.
59+
if CHUNK_SIZE > PAGE_SIZE:
60+
# We faulted with a large chunk size. Fall back to
61+
# page-by-page and retry.
62+
CHUNK_SIZE = PAGE_SIZE
63+
else:
64+
# We're already reading page-by-page. Skip this page.
65+
addr += CHUNK_SIZE
66+
continue
67+
5068
i = 0
5169
while i < len(buf):
5270
i = buf.find(needle, i)
@@ -65,19 +83,31 @@ def search_memory(prog, needle):
6583
print(hex(addr + i), identity)
6684
i += 1
6785

86+
addr += CHUNK_SIZE
87+
6888

6989
if __name__ == "__main__":
7090
parser = argparse.ArgumentParser(
71-
description="Search kernel memory for a byte string"
91+
description="Search kernel memory for a value and print the addresses where it is found"
7292
)
73-
parser.add_argument(
74-
"bytes",
75-
nargs="?",
76-
help="hexadecimal bytes to read; if omitted, read byte string from stdin",
93+
group = parser.add_argument_group(
94+
title="search target",
95+
description="If none of these are given, search for a byte string read from standard input.",
96+
).add_mutually_exclusive_group()
97+
group.add_argument("--string", help="search for an ASCII/UTF-8 string")
98+
group.add_argument("--hex", help="search for a byte string, given in hexadecimal")
99+
group.add_argument(
100+
"--address", help="search for an address-sized integer, given in hexadecimal"
77101
)
78102
args = parser.parse_args()
79-
if args.bytes is None:
80-
needle = sys.stdin.buffer.read()
103+
104+
if args.string is not None:
105+
needle = args.string.encode()
106+
elif args.hex is not None:
107+
needle = bytes.fromhex(args.hex)
108+
elif args.address is not None:
109+
needle = Object(prog, "void *", int(args.address, 16))
81110
else:
82-
needle = bytes.fromhex(args.bytes)
111+
needle = sys.stdin.buffer.read()
112+
83113
search_memory(prog, needle)

docs/release_highlights.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ from the full `release notes <https://github.com/osandov/drgn/releases>`_.
66

77
.. toctree::
88

9+
release_highlights/0.0.33.rst
910
release_highlights/0.0.32.rst
1011
release_highlights/0.0.31.rst
1112
release_highlights/0.0.30.rst

docs/release_highlights/0.0.33.rst

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
0.0.33 (Released October 30th, 2025)
2+
====================================
3+
4+
These are some of the highlights of drgn 0.0.33. See the `GitHub release
5+
<https://github.com/osandov/drgn/releases/tag/v0.0.33>`_ for the full release
6+
notes, including more improvements and bug fixes.
7+
8+
.. highlight:: pycon
9+
.. program:: drgn
10+
11+
Lots of New Helpers
12+
-------------------
13+
14+
This release adds over 80 new helpers! The majority are for the Linux kernel
15+
memory management subsystem:
16+
17+
- :func:`~drgn.helpers.common.memory.identify_address_all()`
18+
- :func:`~drgn.helpers.linux.block.nr_blockdev_pages()`
19+
- :func:`~drgn.helpers.linux.hugetlb.for_each_hstate()`
20+
- :func:`~drgn.helpers.linux.hugetlb.huge_page_size()`
21+
- :func:`~drgn.helpers.linux.hugetlb.hugetlb_total_pages()`
22+
- :func:`~drgn.helpers.linux.hugetlb.hugetlb_total_usage()`
23+
- :func:`~drgn.helpers.linux.mm.decode_memory_block_state()`
24+
- :func:`~drgn.helpers.linux.mm.decode_page_flags_value()`
25+
- :func:`~drgn.helpers.linux.mm.for_each_memory_block()`
26+
- :func:`~drgn.helpers.linux.mm.for_each_valid_page_range()`
27+
- :func:`~drgn.helpers.linux.mm.for_each_valid_pfn_and_page()`
28+
- :func:`~drgn.helpers.linux.mm.memory_block_size_bytes()`
29+
- :func:`~drgn.helpers.linux.mm.page_flags()`
30+
- :func:`~drgn.helpers.linux.mm.page_index()`
31+
- :func:`~drgn.helpers.linux.mm.task_rss()`
32+
- :func:`~drgn.helpers.linux.mm.vm_commit_limit()`
33+
- :func:`~drgn.helpers.linux.mm.vm_memory_committed()`
34+
- :func:`~drgn.helpers.linux.mmzone.NODE_DATA()`
35+
- :func:`~drgn.helpers.linux.mmzone.decode_section_flags()`
36+
- :func:`~drgn.helpers.linux.mmzone.early_section_nr()`
37+
- :func:`~drgn.helpers.linux.mmzone.early_section()`
38+
- :func:`~drgn.helpers.linux.mmzone.for_each_online_pgdat()`
39+
- :func:`~drgn.helpers.linux.mmzone.for_each_present_section()`
40+
- :func:`~drgn.helpers.linux.mmzone.high_wmark_pages()`
41+
- :func:`~drgn.helpers.linux.mmzone.low_wmark_pages()`
42+
- :func:`~drgn.helpers.linux.mmzone.min_wmark_pages()`
43+
- :func:`~drgn.helpers.linux.mmzone.nr_to_section()`
44+
- :func:`~drgn.helpers.linux.mmzone.online_section_nr()`
45+
- :func:`~drgn.helpers.linux.mmzone.online_section()`
46+
- :func:`~drgn.helpers.linux.mmzone.pfn_to_section_nr()`
47+
- :func:`~drgn.helpers.linux.mmzone.pfn_to_section()`
48+
- :func:`~drgn.helpers.linux.mmzone.present_section_nr()`
49+
- :func:`~drgn.helpers.linux.mmzone.present_section()`
50+
- :func:`~drgn.helpers.linux.mmzone.section_decode_mem_map()`
51+
- :func:`~drgn.helpers.linux.mmzone.section_mem_map_addr()`
52+
- :func:`~drgn.helpers.linux.mmzone.section_nr_to_pfn()`
53+
- :func:`~drgn.helpers.linux.mmzone.valid_section_nr()`
54+
- :func:`~drgn.helpers.linux.mmzone.valid_section()`
55+
- :func:`~drgn.helpers.linux.mmzone.wmark_pages()`
56+
- :func:`~drgn.helpers.linux.slab.slab_cache_objects_per_slab()`
57+
- :func:`~drgn.helpers.linux.slab.slab_cache_order()`
58+
- :func:`~drgn.helpers.linux.slab.slab_cache_pages_per_slab()`
59+
- :func:`~drgn.helpers.linux.slab.slab_cache_usage()`
60+
- :func:`~drgn.helpers.linux.slab.slab_total_usage()`
61+
- :func:`~drgn.helpers.linux.swap.for_each_swap_info()`
62+
- :func:`~drgn.helpers.linux.swap.swap_file_path()`
63+
- :func:`~drgn.helpers.linux.swap.swap_is_file()`
64+
- :func:`~drgn.helpers.linux.swap.swap_total_usage()`
65+
- :func:`~drgn.helpers.linux.swap.swap_usage_in_pages()`
66+
- :func:`~drgn.helpers.linux.swap.total_swapcache_pages()`
67+
- :func:`~drgn.helpers.linux.vmstat.global_node_page_state()`
68+
- :func:`~drgn.helpers.linux.vmstat.global_numa_event_state()`
69+
- :func:`~drgn.helpers.linux.vmstat.global_vm_event_state()`
70+
- :func:`~drgn.helpers.linux.vmstat.global_zone_page_state()`
71+
- :func:`~drgn.helpers.linux.vmstat.nr_free_pages()`
72+
- :func:`~drgn.helpers.linux.vmstat.zone_page_state()`
73+
74+
But there are many others for devices, the CPU scheduler, and more:
75+
76+
- :func:`~drgn.helpers.common.format.double_quote_ascii_string()`
77+
- :func:`~drgn.helpers.common.type.typeof_member()`
78+
- :func:`~drgn.helpers.linux.device.bus_for_each_dev()`
79+
- :func:`~drgn.helpers.linux.device.bus_to_subsys()`
80+
- :func:`~drgn.helpers.linux.device.class_for_each_device()`
81+
- :func:`~drgn.helpers.linux.device.class_to_subsys()`
82+
- :func:`~drgn.helpers.linux.device.dev_name()`
83+
- :func:`~drgn.helpers.linux.fs.super_block_for_each_mount()`
84+
- :func:`~drgn.helpers.linux.kallsyms.module_kallsyms()`
85+
- :func:`~drgn.helpers.linux.kernfs.kernfs_children()`
86+
- :func:`~drgn.helpers.linux.list.validate_list_count_nodes()`
87+
- :func:`~drgn.helpers.linux.module.module_taints()`
88+
- :func:`~drgn.helpers.linux.nodemask.nr_node_ids()`
89+
- :func:`~drgn.helpers.linux.panic.panic_message()`
90+
- :func:`~drgn.helpers.linux.panic.panic_task()`
91+
- :func:`~drgn.helpers.linux.panic.tainted()`
92+
- :func:`~drgn.helpers.linux.percpu.percpu_counter_sum_positive()`
93+
- :func:`~drgn.helpers.linux.pid.for_each_task_in_group()`
94+
- :func:`~drgn.helpers.linux.rbtree.rbtree_preorder_for_each_entry()`
95+
- :func:`~drgn.helpers.linux.rbtree.rbtree_preorder_for_each()`
96+
- :func:`~drgn.helpers.linux.sched.cpu_rq()`
97+
- :func:`~drgn.helpers.linux.sched.get_task_state()`
98+
- :func:`~drgn.helpers.linux.sched.task_on_cpu()`
99+
- :func:`~drgn.helpers.linux.sched.task_rq()`
100+
- :func:`~drgn.helpers.linux.sched.task_since_last_arrival_ns()`
101+
- :func:`~drgn.helpers.experimental.kmodify.set_bit()`
102+
- :func:`~drgn.helpers.experimental.kmodify.clear_bit()`
103+
104+
Address Identification Improvements
105+
-----------------------------------
106+
107+
:func:`~drgn.helpers.common.memory.identify_address()` can now identify
108+
addresses from the kernel memory map (i.e., addresses in a ``struct page``) and
109+
addresses in a ``struct task_struct``::
110+
111+
>>> identify_address(pfn_to_page(0))
112+
'page: pfn 0'
113+
>>> identify_address(find_task(4))
114+
'task: 4 (kworker/R-rcu_g)'
115+
116+
The new :func:`~drgn.helpers.common.memory.identify_address_all()` helper
117+
provides a programmatic interface for identifying addresses:
118+
119+
120+
>>> for identity in identify_address_all(find_task(4)):
121+
... print(repr(identity))
122+
...
123+
IdentifiedTaskStruct(address=18446622477834301568, task=Object(prog, 'struct task_struct *', value=0xffff9168c10cb080))
124+
IdentifiedSlabObject(address=18446622477834301568, slab_object_info=SlabObjectInfo(slab_cache=Object(prog, 'struct kmem_cache *', value=0xffff9168c0206c00), slab=Object(prog, 'struct slab *', value=0xffffd26b04043200), address=0xffff9168c10cb080, allocated=True))
125+
126+
Array Slices
127+
------------
128+
129+
Array and pointer objects can now be sliced. This is especially useful for
130+
converting a flexible array member or pointer to a fixed-length array::
131+
132+
>>> poll_list
133+
*(struct poll_list *)0xffffad92459a39a0 = {
134+
.next = (struct poll_list *)0x0,
135+
.len = (unsigned int)2,
136+
.entries = (struct pollfd []){},
137+
}
138+
>>> poll_list.entries[:poll_list.len]
139+
(struct pollfd [2]){
140+
{
141+
.fd = (int)4,
142+
.events = (short)1,
143+
.revents = (short)0,
144+
},
145+
{
146+
.fd = (int)9,
147+
.events = (short)1,
148+
.revents = (short)0,
149+
},
150+
}
151+
152+
More Reliable Interrupt Stack Traces
153+
------------------------------------
154+
155+
Stephen Brennan improved stack tracing on x86-64 kernels that use the frame
156+
pointer unwinder (Ubuntu kernels, for example) and AArch64 so that it reliably
157+
unwinds through interrupts.
158+
159+
Kmodify Bit Field Fix
160+
---------------------
161+
162+
:func:`drgn.helpers.experimental.kmodify.write_object()` was found to have a
163+
major bug when writing to bit fields. It didn't take the field's bit offset or
164+
bit size into account, meaning that it wrote to the wrong bits and overwrote
165+
additional memory, too.
166+
167+
This release fixes it to handle bit fields of size 1 (atomically) and reject
168+
larger bit fields. Support for larger bit fields can be added if requested.
169+
170+
Linux 6.17 and 6.18 Support
171+
---------------------------
172+
173+
A change in Linux 6.17 broke drgn's timekeeping helpers. This error is fixed in
174+
this release::
175+
176+
KeyError: 'tk_core'
177+
178+
A change in Linux 6.17 broke ``tools/fsrefs.py --super-block-on-block-device``
179+
on Btrfs. This error is fixed in this release::
180+
181+
no filesystem found on /dev/...
182+
183+
A change in Linux 6.18 broke :func:`~drgn.helpers.linux.fs.d_path()` when
184+
passing only a ``struct dentry *``. This error is fixed in this release::
185+
186+
AttributeError: '_drgn.Object' object has no attribute 'next'
187+
188+
A change in Linux 6.18 broke
189+
:func:`~drgn.helpers.linux.net.get_net_ns_by_inode()`. This error is fixed in
190+
this release::
191+
192+
AttributeError: 'struct proc_ns_operations' has no member 'type'. Did you mean: 'type_'?
193+
194+
No More Python 3.6 & 3.7 Support
195+
--------------------------------
196+
197+
As `previously announced <https://github.com/osandov/drgn/issues/467>`_, this
198+
release dropped support for Python 3.6 and 3.7.

libdrgn/configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
dnl Copyright (c) Meta Platforms, Inc. and affiliates.
22
dnl SPDX-License-Identifier: LGPL-2.1-or-later
33

4-
AC_INIT([libdrgn], [0.0.32],
4+
AC_INIT([libdrgn], [0.0.33],
55
[https://github.com/osandov/drgn/issues],,
66
[https://github.com/osandov/drgn])
77

libdrgn/drgn.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
/** Minor version of drgn. */
4545
#define DRGN_VERSION_MINOR 0
4646
/** Patch level of drgn. */
47-
#define DRGN_VERSION_PATCH 32
47+
#define DRGN_VERSION_PATCH 33
4848

4949
/**
5050
* @defgroup ErrorHandling Error handling

0 commit comments

Comments
 (0)