Skip to content

Commit 0c52f5b

Browse files
author
Delphix Engineering
committed
Merge branch 'refs/heads/upstream-HEAD' into repo-HEAD
2 parents f43b5e7 + 08193a9 commit 0c52f5b

File tree

6 files changed

+196
-25
lines changed

6 files changed

+196
-25
lines changed

libdrgn/kdump.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,61 @@ struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog)
139139
kdump_free(ctx);
140140
return err;
141141
}
142+
143+
struct drgn_error *drgn_program_cache_prstatus_kdump(struct drgn_program *prog)
144+
{
145+
struct drgn_error *err;
146+
kdump_num_t ncpus, i;
147+
kdump_status ks;
148+
149+
ks = kdump_get_number_attr(prog->kdump_ctx, "cpu.number", &ncpus);
150+
if (ks != KDUMP_OK) {
151+
return drgn_error_format(DRGN_ERROR_OTHER,
152+
"kdump_get_number_attr(cpu.number): %s",
153+
kdump_get_err(prog->kdump_ctx));
154+
}
155+
156+
/*
157+
* Note that in the following loop we never call kdump_attr_unref() on
158+
* prstatus_ref, nor kdump_blob_unpin() on the prstatus blob that we get
159+
* from libkdumpfile. Since drgn is completely read-only as a consumer
160+
* of that library, we "leak" both the attribute reference and blob pin
161+
* until kdump_free() is called which will clean up everything for us.
162+
*/
163+
for (i = 0; i < ncpus; i++) {
164+
/* Enough for the longest possible PRSTATUS attribute name. */
165+
char attr_name[64];
166+
kdump_attr_ref_t prstatus_ref;
167+
kdump_attr_t prstatus_attr;
168+
void *prstatus_data;
169+
size_t prstatus_size;
170+
171+
snprintf(attr_name, sizeof(attr_name),
172+
"cpu.%" PRIuFAST64 ".PRSTATUS", i);
173+
ks = kdump_attr_ref(prog->kdump_ctx, attr_name, &prstatus_ref);
174+
if (ks != KDUMP_OK) {
175+
return drgn_error_format(DRGN_ERROR_OTHER,
176+
"kdump_attr_ref(%s): %s",
177+
attr_name,
178+
kdump_get_err(prog->kdump_ctx));
179+
}
180+
181+
ks = kdump_attr_ref_get(prog->kdump_ctx, &prstatus_ref,
182+
&prstatus_attr);
183+
if (ks != KDUMP_OK) {
184+
return drgn_error_format(DRGN_ERROR_OTHER,
185+
"kdump_attr_ref_get(%s): %s",
186+
attr_name,
187+
kdump_get_err(prog->kdump_ctx));
188+
}
189+
190+
prstatus_data = kdump_blob_pin(prstatus_attr.val.blob);
191+
prstatus_size = kdump_blob_size(prstatus_attr.val.blob);
192+
err = drgn_program_cache_prstatus_entry(prog,
193+
prstatus_data,
194+
prstatus_size);
195+
if (err)
196+
return err;
197+
}
198+
return NULL;
199+
}

libdrgn/linux_kernel.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ linux_kernel_report_debug_info(struct drgn_program *prog,
3434
#define KDUMP_SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1)
3535

3636
#ifdef WITH_LIBKDUMPFILE
37+
struct drgn_error *drgn_program_cache_prstatus_kdump(struct drgn_program *prog);
3738
struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog);
3839
#else
3940
static inline struct drgn_error *
4041
drgn_program_set_kdump(struct drgn_program *prog)
4142
{
4243
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
43-
"drgn was built without libkdumpfile support");
44+
"drgn was built without libkdumpfile support");
4445
}
4546
#endif
4647

libdrgn/program.c

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -684,16 +684,45 @@ drgn_program_load_debug_info(struct drgn_program *prog, const char **paths,
684684
return err;
685685
}
686686

687-
static struct drgn_error *drgn_program_cache_prstatus(struct drgn_program *prog)
687+
struct drgn_error *drgn_program_cache_prstatus_entry(struct drgn_program *prog,
688+
char *data, size_t size)
688689
{
689-
size_t phnum, i;
690+
struct drgn_prstatus_map_entry entry;
690691
size_t pr_pid_offset;
692+
uint32_t pr_pid;
691693
bool bswap;
692694

693695
pr_pid_offset = drgn_program_is_64_bit(prog) ? 32 : 24;
694696
bswap = (drgn_program_is_little_endian(prog) !=
695697
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__));
696698

699+
if (size < pr_pid_offset + sizeof(pr_pid))
700+
return NULL;
701+
702+
memcpy(&pr_pid, data + pr_pid_offset, sizeof(pr_pid));
703+
if (bswap)
704+
pr_pid = bswap_32(pr_pid);
705+
if (!pr_pid)
706+
return NULL;
707+
708+
entry.key = pr_pid;
709+
entry.value.str = data;
710+
entry.value.len = size;
711+
if (drgn_prstatus_map_insert(&prog->prstatus_cache, &entry,
712+
NULL) == -1) {
713+
return &drgn_enomem;
714+
}
715+
return NULL;
716+
}
717+
718+
static struct drgn_error *drgn_program_cache_prstatus(struct drgn_program *prog)
719+
{
720+
size_t phnum, i;
721+
722+
#ifdef WITH_LIBKDUMPFILE
723+
if (prog->kdump_ctx)
724+
return drgn_program_cache_prstatus_kdump(prog);
725+
#endif
697726
if (elf_getphdrnum(prog->core, &phnum) != 0)
698727
return drgn_error_libelf();
699728
for (i = 0; i < phnum; i++) {
@@ -720,28 +749,18 @@ static struct drgn_error *drgn_program_cache_prstatus(struct drgn_program *prog)
720749
(offset = gelf_getnote(data, offset, &nhdr, &name_offset,
721750
&desc_offset))) {
722751
const char *name;
723-
uint32_t pr_pid;
724-
struct drgn_prstatus_map_entry entry;
752+
struct drgn_error *err;
725753

726754
name = (char *)data->d_buf + name_offset;
727755
if (strncmp(name, "CORE", nhdr.n_namesz) != 0 ||
728-
nhdr.n_type != NT_PRSTATUS ||
729-
nhdr.n_descsz < pr_pid_offset + sizeof(pr_pid))
730-
continue;
731-
memcpy(&pr_pid,
732-
(char *)data->d_buf + desc_offset + pr_pid_offset,
733-
sizeof(pr_pid));
734-
if (bswap)
735-
pr_pid = bswap_32(pr_pid);
736-
if (!pr_pid)
756+
nhdr.n_type != NT_PRSTATUS)
737757
continue;
738758

739-
entry.key = pr_pid;
740-
entry.value.str = (char *)data->d_buf + desc_offset;
741-
entry.value.len = nhdr.n_descsz;
742-
if (drgn_prstatus_map_insert(&prog->prstatus_cache,
743-
&entry, NULL) == -1)
744-
return &drgn_enomem;
759+
err = drgn_program_cache_prstatus_entry(prog,
760+
(char *)data->d_buf + desc_offset,
761+
nhdr.n_descsz);
762+
if (err)
763+
return err;
745764
}
746765
}
747766
return NULL;

libdrgn/program.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ struct drgn_error *drgn_program_get_dwfl(struct drgn_program *prog, Dwfl **ret);
157157
struct drgn_error *drgn_program_find_prstatus(struct drgn_program *prog,
158158
uint32_t tid, struct string *ret);
159159

160+
/**
161+
* Cache the @c NT_PRSTATUS note provided by @p data in @p prog.
162+
*
163+
* @param[in] data The pointer to the note data.
164+
* @param[in] size Size of data in note.
165+
*/
166+
struct drgn_error *drgn_program_cache_prstatus_entry(struct drgn_program *prog,
167+
char *data, size_t size);
168+
160169
/*
161170
* Like @ref drgn_program_find_symbol_by_address(), but @p ret is already
162171
* allocated, we may already know the module, and doesn't return a @ref

libdrgn/stack_trace.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,12 @@ static bool drgn_thread_set_initial_registers(Dwfl_Thread *thread,
398398
}
399399
}
400400

401-
/* Then, try the core dump. */
401+
/* Then, try the core dump (and/or kdump if supported). */
402+
#ifdef WITH_LIBKDUMPFILE
403+
if (prog->core || prog->kdump_ctx) {
404+
#else
402405
if (prog->core) {
406+
#endif
403407
uint32_t tid;
404408
struct string prstatus;
405409

tests/helpers/linux/test_block.py

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import errno
2+
from fcntl import ioctl
13
import os
24
import os.path
5+
import sys
6+
import tempfile
37

48
from drgn.helpers.linux.block import (
59
disk_devt,
@@ -9,11 +13,63 @@
913
part_devt,
1014
part_name,
1115
)
12-
from drgn.helpers.linux.device import MAJOR, MINOR
16+
from drgn.helpers.linux.device import MAJOR, MINOR, MKDEV
1317
from tests.helpers.linux import LinuxHelperTestCase
1418

1519

20+
LOOP_SET_FD = 0x4C00
21+
LOOP_SET_STATUS64 = 0x4C04
22+
LOOP_GET_STATUS64 = 0x4C05
23+
LOOP_CTL_GET_FREE = 0x4C82
24+
25+
LO_FLAGS_AUTOCLEAR = 4
26+
27+
1628
class TestBlock(LinuxHelperTestCase):
29+
@staticmethod
30+
def _losetup():
31+
with tempfile.TemporaryFile() as temp:
32+
os.truncate(temp.fileno(), 1024 * 1024 * 1024)
33+
with open("/dev/loop-control", "r") as loop_control:
34+
while True:
35+
index = ioctl(loop_control.fileno(), LOOP_CTL_GET_FREE)
36+
close_loop = True
37+
loop = open(f"/dev/loop{index}", "r")
38+
try:
39+
try:
40+
ioctl(loop.fileno(), LOOP_SET_FD, temp.fileno())
41+
except OSError as e:
42+
if e.errno == errno.EBUSY:
43+
continue
44+
raise
45+
info = bytearray(232) # sizeof(struct loop_info64)
46+
ioctl(loop.fileno(), LOOP_GET_STATUS64, info)
47+
lo_flags = int.from_bytes(info[52:56], sys.byteorder)
48+
lo_flags |= LO_FLAGS_AUTOCLEAR
49+
info[52:56] = lo_flags.to_bytes(4, sys.byteorder)
50+
ioctl(loop.fileno(), LOOP_SET_STATUS64, info, False)
51+
close_loop = False
52+
return loop
53+
finally:
54+
if close_loop:
55+
loop.close()
56+
57+
@classmethod
58+
def setUpClass(cls):
59+
super().setUpClass()
60+
# Try to set up a loop device so that there's at least one block
61+
# device.
62+
try:
63+
cls.loop = cls._losetup()
64+
except OSError:
65+
cls.loop = None
66+
67+
@classmethod
68+
def tearDownClass(cls):
69+
if cls.loop:
70+
cls.loop.close()
71+
super().tearDownClass()
72+
1773
def test_disk_devt(self):
1874
for disk in for_each_disk(self.prog):
1975
path = os.path.join(b"/sys/block", disk_name(disk), b"dev")
@@ -24,8 +80,8 @@ def test_disk_devt(self):
2480

2581
def test_for_each_disk(self):
2682
self.assertEqual(
27-
set(os.listdir("/sys/block")),
2883
{disk_name(disk).decode() for disk in for_each_disk(self.prog)},
84+
set(os.listdir("/sys/block")),
2985
)
3086

3187
def test_part_devt(self):
@@ -36,8 +92,32 @@ def test_part_devt(self):
3692
devt = part_devt(part).value_()
3793
self.assertEqual(f"{MAJOR(devt)}:{MINOR(devt)}", expected)
3894

39-
def test_for_each_part(self):
95+
def test_for_each_partition(self):
4096
self.assertEqual(
41-
set(os.listdir("/sys/class/block")),
4297
{part_name(part).decode() for part in for_each_partition(self.prog)},
98+
set(os.listdir("/sys/class/block")),
4399
)
100+
101+
def test_loop_disk(self):
102+
if not self.loop:
103+
self.skipTest("could not create loop device")
104+
rdev = os.stat(self.loop.fileno()).st_rdev
105+
devt = MKDEV(os.major(rdev), os.minor(rdev))
106+
for disk in for_each_disk(self.prog):
107+
if disk_devt(disk) == devt:
108+
break
109+
else:
110+
self.fail("loop disk not found")
111+
self.assertEqual(disk_name(disk), os.path.basename(self.loop.name).encode())
112+
113+
def test_loop_part(self):
114+
if not self.loop:
115+
self.skipTest("could not create loop device")
116+
rdev = os.stat(self.loop.fileno()).st_rdev
117+
devt = MKDEV(os.major(rdev), os.minor(rdev))
118+
for part in for_each_partition(self.prog):
119+
if part_devt(part) == devt:
120+
break
121+
else:
122+
self.fail("loop partition not found")
123+
self.assertEqual(part_name(part), os.path.basename(self.loop.name).encode())

0 commit comments

Comments
 (0)