Skip to content

Commit 08193a9

Browse files
sdimitroosandov
authored andcommitted
Support stack traces for running threads on kdumps
1 parent d3f4f2b commit 08193a9

File tree

5 files changed

+112
-21
lines changed

5 files changed

+112
-21
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

0 commit comments

Comments
 (0)