Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libdrgn: read kdumps (new dependecy: libkdumpfile) #7

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Install the following dependencies:
* Python 3.6 or newer
* elfutils development libraries (libelf and libdw)
* GNU autotools (autoconf, automake, and libtool) and pkgconf
* `libkdumpfile <https://github.com/ptesarik/libkdumpfile>`_

Then, run:

Expand Down
2 changes: 2 additions & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Installation
============

XXX: Something about libkdumpfile

.. highlight:: console

drgn depends on `Python <https://www.python.org/>`_ 3.6 or newer as well as
Expand Down
7 changes: 7 additions & 0 deletions libdrgn/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ libdrgnimpl_la_SOURCES = binary_search_tree.h \
hash_table.h \
internal.c \
internal.h \
kdump_reader.h \
language.h \
language_c.c \
lexer.c \
Expand Down Expand Up @@ -54,6 +55,12 @@ libdrgnimpl_la_CFLAGS = -fvisibility=hidden -fopenmp $(libelf_CFLAGS) \
libdrgnimpl_la_CPPFLAGS = -D_GNU_SOURCE
libdrgnimpl_la_LIBADD = $(libelf_LIBS) $(libdw_LIBS)

if WITH_LIBKDUMPFILE
libdrgnimpl_la_SOURCES += kdump_reader.c
libdrgnimpl_la_CFLAGS += $(libkdumpfile_CFLAGS)
libdrgnimpl_la_LIBADD += $(libkdumpfile_LIBS)
endif

lib_LTLIBRARIES = libdrgn.la

libdrgn_la_SOURCES =
Expand Down
14 changes: 14 additions & 0 deletions libdrgn/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ python3-devel or python3-dev) or specify the location of the Python development
headers by setting the PYTHON_CPPFLAGS environment variable.])])
CPPFLAGS="$save_CPPFLAGS"])

AC_ARG_WITH([libkdumpfile],
[AS_HELP_STRING([--with-libkdumpfile],
[build with support for the makedumpfile kernel
core dump format using libkdumpfile
@<:@default=auto@:>@])],
[], [with_libkdumpfile=auto])
AS_CASE(["x$with_libkdumpfile"],
[xyes], [PKG_CHECK_MODULES(libkdumpfile, [libkdumpfile])],
[xauto], [PKG_CHECK_MODULES(libkdumpfile, [libkdumpfile],
[with_libkdumpfile=yes],
[with_libkdumpfile=no])])
AM_CONDITIONAL([WITH_LIBKDUMPFILE], [test "x$with_libkdumpfile" != xno])
AM_COND_IF([WITH_LIBKDUMPFILE], [AC_DEFINE(WITH_LIBKDUMPFILE)])

PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES(libelf, [libelf])
AC_SUBST([libelf_CFLAGS])
Expand Down
209 changes: 209 additions & 0 deletions libdrgn/kdump_reader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright 2019 - Serapheim Dimitropoulos
// SPDX-License-Identifier: GPL-3.0+

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libkdumpfile/kdumpfile.h>

#include "kdump_reader.h"
#include "linux_kernel.h"
#include "program.h"

static struct drgn_error *drgn_kdump_init(kdump_ctx_t **ctx, int fd)
{
kdump_ctx_t *context = kdump_new();
if (!ctx) {
return drgn_error_create(DRGN_ERROR_OTHER,
"kdump_new() failed\n");
}

kdump_status ks = kdump_set_number_attr(context,
KDUMP_ATTR_FILE_FD, fd);
if (ks != KDUMP_OK) {
kdump_free(context);
return drgn_error_format(DRGN_ERROR_OTHER,
"setting kdump fd attribute: %s\n",
kdump_get_err(context));
}

kdump_attr_t attr;
attr.type = KDUMP_STRING;
attr.val.string = "linux";
ks = kdump_set_attr(context, KDUMP_ATTR_OSTYPE, &attr);
if (ks != KDUMP_OK) {
kdump_free(context);
return drgn_error_format(DRGN_ERROR_OTHER,
"setting kdump xlat attribute: %s\n",
kdump_get_err(context));
}
*ctx = context;
return NULL;
}

static void drgn_kdump_close(kdump_ctx_t *ctx)
{
kdump_free(ctx);
}

static struct drgn_error *
drgn_kdump_get_raw_vmcoreinfo(kdump_ctx_t *ctx, const char **ret)
{
const char *raw = NULL;
kdump_status ks = kdump_vmcoreinfo_raw(ctx, &raw);
if (ks != KDUMP_OK) {
return drgn_error_format(DRGN_ERROR_OTHER,
"kdump_vmcoreinfo_raw() failed: %s\n",
kdump_get_err(ctx));
}
*ret = raw;
return NULL;

}

static bool drgn_kdump_is_64bits(const char *kdump_arch_attr)
{
return (!strcmp(kdump_arch_attr, KDUMP_ARCH_X86_64) ||
!strcmp(kdump_arch_attr, KDUMP_ARCH_AARCH64) ||
!strcmp(kdump_arch_attr, KDUMP_ARCH_ALPHA) ||
!strcmp(kdump_arch_attr, KDUMP_ARCH_IA64) ||
!strcmp(kdump_arch_attr, KDUMP_ARCH_PPC64) ||
!strcmp(kdump_arch_attr, KDUMP_ARCH_S390X));
}

static struct drgn_error *
drgn_kdump_get_arch(kdump_ctx_t *ctx, enum drgn_architecture_flags *arch)
{
/*
* We first look in the architecture name and from there we
* we determine whether the architecture is 32 or 64 bits.
* We could also attempt to guess the endianess that way but
* we once again query the kdump for the rare case of
* bi-endian architectures.
*/
*arch = 0;

const char *key = KDUMP_ATTR_ARCH_NAME;
kdump_attr_ref_t root;
kdump_status ks = kdump_attr_ref(ctx, key, &root);
if (ks != KDUMP_OK) {
return drgn_error_format(DRGN_ERROR_OTHER,
"kdump_attr_ref(%s) failed: %s\n",
key, kdump_get_err(ctx));
}

kdump_attr_t attr;
ks = kdump_attr_ref_get(ctx, &root, &attr);
if (ks != KDUMP_OK) {
kdump_attr_unref(ctx, &root);
return drgn_error_format(DRGN_ERROR_OTHER,
"kdump_attr_ref_get(%s) failed: %s\n",
key, kdump_get_err(ctx));
}

switch (attr.type) {
case KDUMP_STRING:
if (drgn_kdump_is_64bits(attr.val.string))
*arch |= DRGN_ARCH_IS_64_BIT;
break;
default:
return drgn_error_format(DRGN_ERROR_OTHER,
"%s - unexpected attr type: %d\n",
key, attr.type);
}
kdump_attr_unref(ctx, &root);

key = KDUMP_ATTR_BYTE_ORDER;
ks = kdump_attr_ref(ctx, key, &root);
if (ks != KDUMP_OK) {
return drgn_error_format(DRGN_ERROR_OTHER,
"kdump_attr_ref(%s) failed: %s\n",
key, kdump_get_err(ctx));
}

ks = kdump_attr_ref_get(ctx, &root, &attr);
if (ks != KDUMP_OK) {
kdump_attr_unref(ctx, &root);
return drgn_error_format(DRGN_ERROR_OTHER,
"kdump_attr_ref_get(%s) failed: %s\n",
key, kdump_get_err(ctx));
}

switch (attr.type) {
case KDUMP_NUMBER:
if (attr.val.number == KDUMP_LITTLE_ENDIAN)
*arch |= DRGN_ARCH_IS_LITTLE_ENDIAN;
break;
default:
return drgn_error_format(DRGN_ERROR_OTHER,
"%s - unexpected attr type: %d\n",
key, attr.type);
}
kdump_attr_unref(ctx, &root);

return NULL;
}

static struct drgn_error *
drgn_read_kdump(void *buf, uint64_t address, size_t count,
uint64_t offset, void *arg, bool physical)
{
kdump_ctx_t *ctx = arg;
size_t nread = count;

kdump_addrspace_t as = (physical) ? KDUMP_KPHYSADDR : KDUMP_KVADDR;
kdump_status ks = kdump_read(ctx, as, address, buf, &nread);
if (ks != KDUMP_OK) {
return drgn_error_format(DRGN_ERROR_OTHER,
"kdump_read failed: %s",
kdump_get_err(ctx));
}
return NULL;
}

struct drgn_error *
drgn_program_set_kdump(struct drgn_program *prog)
{
kdump_ctx_t *ctx = NULL;
struct drgn_error *err = drgn_kdump_init(&ctx, prog->core_fd);
if (err)
goto out_fd;

const char *vmcoreinfo = NULL;
err = drgn_kdump_get_raw_vmcoreinfo(ctx, &vmcoreinfo);
if (err)
goto out_kdump;

err = parse_vmcoreinfo(vmcoreinfo, strlen(vmcoreinfo)+1,
&prog->vmcoreinfo);
if (err)
goto out_kdump;

enum drgn_architecture_flags arch;
err = drgn_kdump_get_arch(ctx, &arch);
if (err)
goto out_kdump;
drgn_program_update_arch(prog, arch);

/*
* Add a single memory segment rerpresenting the whole dump
* and let libkdumpfile do the work for us there.
*/
err = drgn_program_add_memory_segment(prog, 0, UINT64_MAX,
drgn_read_kdump,
ctx, false);
if (err)
goto out_kdump;

prog->flags |= DRGN_PROGRAM_IS_LINUX_KERNEL;
return NULL;

out_kdump:
drgn_kdump_close(ctx);
out_fd:
close(prog->core_fd);
prog->core_fd = -1;
return err;
}
56 changes: 56 additions & 0 deletions libdrgn/kdump_reader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2019 - Serapheim Dimitropoulos
// SPDX-License-Identifier: GPL-3.0+

/**
* @file
*
* Makedumpfile format reader that implements the MemoryReader interface.
*
* Other implementation references:
* - sourceforge.net/p/makedumpfile/code/ci/master/tree/IMPLEMENTATION
* - github.com/ptesarik/libkdumpfile
*
* Note that this is only really used when --with-libkdumpfile=yes is
* configured.
*
* See @ref KdumpReader.
*/

#ifndef DRGN_KDUMP_READER_H
#define DRGN_KDUMP_READER_H

#include "internal.h"

/**
* @ingroup Internals
*
* @defgroup KdumpReader kdump reader
*
* Logic for setting up drgn for reading kdump crash dumps.
*
* @{
*/

#define KDUMP_SIGNATURE "KDUMP "
#define KDUMP_SIG_LEN (sizeof (KDUMP_SIGNATURE) - 1)

/**
* Setup program-related context leveraging libkdumpfile so
* we are able to read from makedumpfile program dumps.
*
* @return @c NULL on success, non-@c NULL on error.
*/
#ifdef WITH_LIBKDUMPFILE
struct drgn_error *drgn_program_set_kdump(struct drgn_program *prog);
#else
static inline struct drgn_error *
drgn_program_set_kdump(struct drgn_program *prog)
{
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"drgn configured without libkdumpfile");
}
#endif

/** @} */

#endif /* DRGN_KDUMP_READER_H */
39 changes: 36 additions & 3 deletions libdrgn/program.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "internal.h"
#include "dwarf_index.h"
#include "dwarf_info_cache.h"
#include "kdump_reader.h"
#include "language.h"
#include "linux_kernel.h"
#include "memory_reader.h"
Expand Down Expand Up @@ -53,8 +54,8 @@ drgn_program_architecture(struct drgn_program *prog)
return prog->arch;
}

static void drgn_program_update_arch(struct drgn_program *prog,
enum drgn_architecture_flags arch)
void drgn_program_update_arch(struct drgn_program *prog,
enum drgn_architecture_flags arch)
{
if (prog->arch == DRGN_ARCH_AUTO) {
prog->arch = arch;
Expand Down Expand Up @@ -165,14 +166,40 @@ drgn_program_check_initialized(struct drgn_program *prog)
return NULL;
}

static struct drgn_error *has_kdump_signature(int fd, bool *is_kdump)
{
char signature[KDUMP_SIG_LEN];

uint64_t file_offset = 0;
uint64_t file_count = sizeof (signature);
while (file_count) {
ssize_t ret = read(fd, signature + file_offset,
file_count);
if (ret == -1) {
if (errno == EINTR)
continue;
*is_kdump = false;
return drgn_error_create_os("read", errno, NULL);
} else if (ret == 0) {
*is_kdump = false;
return NULL;
}

file_offset += ret;
file_count -= ret;
}
*is_kdump = !(memcmp(signature, KDUMP_SIGNATURE, sizeof (signature)));
return NULL;
}

LIBDRGN_PUBLIC struct drgn_error *
drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
{
struct drgn_error *err;
Elf *elf;
GElf_Ehdr ehdr_mem, *ehdr;
enum drgn_architecture_flags arch;
bool is_64_bit;
bool is_kdump, is_64_bit;
size_t phnum, i;
bool have_non_zero_phys_addr = false;
struct drgn_memory_file_segment *current_file_segment;
Expand All @@ -186,6 +213,12 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
if (prog->core_fd == -1)
return drgn_error_create_os("open", errno, path);

err = has_kdump_signature(prog->core_fd, &is_kdump);
osandov marked this conversation as resolved.
Show resolved Hide resolved
if (is_kdump)
return drgn_program_set_kdump(prog);
if (err)
goto out_fd;

elf_version(EV_CURRENT);

elf = elf_begin(prog->core_fd, ELF_C_READ, NULL);
Expand Down
Loading