Changeset View
Changeset View
Standalone View
Standalone View
head/devel/gdb/files/kgdb/fbsd-kvm.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <kvm.h> | #include <kvm.h> | ||||
#include "kgdb.h" | #include "kgdb.h" | ||||
static CORE_ADDR stoppcbs; | static CORE_ADDR stoppcbs; | ||||
static LONGEST pcb_size; | static LONGEST pcb_size; | ||||
static void kgdb_core_cleanup(void *); | |||||
static char *vmcore; | static char *vmcore; | ||||
struct target_ops kgdb_trgt_ops; | |||||
/* Per-architecture data key. */ | /* Per-architecture data key. */ | ||||
static struct gdbarch_data *fbsd_vmcore_data; | static struct gdbarch_data *fbsd_vmcore_data; | ||||
struct fbsd_vmcore_ops | struct fbsd_vmcore_ops | ||||
{ | { | ||||
/* Supply registers for a pcb to a register cache. */ | /* Supply registers for a pcb to a register cache. */ | ||||
void (*supply_pcb)(struct regcache *, CORE_ADDR); | void (*supply_pcb)(struct regcache *, CORE_ADDR); | ||||
Show All 34 Lines | |||||
{ | { | ||||
struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | ||||
gdbarch_data (gdbarch, fbsd_vmcore_data); | gdbarch_data (gdbarch, fbsd_vmcore_data); | ||||
ops->cpu_pcb_addr = cpu_pcb_addr; | ops->cpu_pcb_addr = cpu_pcb_addr; | ||||
} | } | ||||
static CORE_ADDR kernstart; | static CORE_ADDR kernstart; | ||||
static kvm_t *kvm; | static kvm_t *kvm; | ||||
static char kvm_err[_POSIX2_LINE_MAX]; | |||||
int kgdb_quiet; | int kgdb_quiet; | ||||
static ptid_t | static ptid_t | ||||
fbsd_vmcore_ptid(int tid) | fbsd_vmcore_ptid(int tid) | ||||
{ | { | ||||
if (kvm == NULL) | if (kvm == NULL) | ||||
/* | /* | ||||
* The remote target stores the 'tid' in the lwp | * The remote target stores the 'tid' in the lwp | ||||
* field. | * field. | ||||
*/ | */ | ||||
return ptid_build(ptid_get_pid(inferior_ptid), tid, 0); | return ptid_t(inferior_ptid.pid(), tid, 0); | ||||
/* | /* | ||||
* This follows the model described in bsd-kvm.c except that | * This follows the model described in bsd-kvm.c except that | ||||
* in kernel tids are used as the tid of the ptid instead of a | * in kernel tids are used as the tid of the ptid instead of a | ||||
* process ID. | * process ID. | ||||
*/ | */ | ||||
return ptid_build(1, 1, tid); | return ptid_t(1, 1, tid); | ||||
} | } | ||||
#define MSGBUF_SEQ_TO_POS(size, seq) ((seq) % (size)) | #define MSGBUF_SEQ_TO_POS(size, seq) ((seq) % (size)) | ||||
static void | static void | ||||
kgdb_dmesg(void) | kgdb_dmesg(void) | ||||
{ | { | ||||
CORE_ADDR bufp; | CORE_ADDR bufp; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | fbsd_kernel_osabi_sniffer(bfd *abfd) | ||||
if (s != NULL && bfd_section_size(abfd, s) == sizeof(buf) && | if (s != NULL && bfd_section_size(abfd, s) == sizeof(buf) && | ||||
bfd_get_full_section_contents(abfd, s, &bufp) && | bfd_get_full_section_contents(abfd, s, &bufp) && | ||||
memcmp(buf, KERNEL_INTERP, sizeof(buf)) == 0) | memcmp(buf, KERNEL_INTERP, sizeof(buf)) == 0) | ||||
return (GDB_OSABI_FREEBSD_KERNEL); | return (GDB_OSABI_FREEBSD_KERNEL); | ||||
return (GDB_OSABI_UNKNOWN); | return (GDB_OSABI_UNKNOWN); | ||||
} | } | ||||
/* The FreeBSD libkvm target. */ | |||||
static const target_info fbsd_kvm_target_info = { | |||||
"vmcore", | |||||
N_("Kernel core dump file"), | |||||
N_("Use a vmcore file as a target.\n\ | |||||
If no filename is specified, /dev/mem is used to examine the running kernel.\n\ | |||||
target vmcore [-w] [filename]") | |||||
}; | |||||
class fbsd_kvm_target final : public target_ops | |||||
{ | |||||
public: | |||||
fbsd_kvm_target () | |||||
{ this->to_stratum = process_stratum; } | |||||
const target_info &info () const override | |||||
{ return fbsd_kvm_target_info; } | |||||
void close () override; | |||||
void fetch_registers (struct regcache *, int) override; | |||||
enum target_xfer_status xfer_partial (enum target_object object, | |||||
const char *annex, | |||||
gdb_byte *readbuf, | |||||
const gdb_byte *writebuf, | |||||
ULONGEST offset, ULONGEST len, | |||||
ULONGEST *xfered_len) override; | |||||
void files_info () override; | |||||
bool thread_alive (ptid_t ptid) override; | |||||
void update_thread_list () override; | |||||
const char *pid_to_str (ptid_t) override; | |||||
const char *extra_thread_info (thread_info *) override; | |||||
bool has_memory () override { return true; } | |||||
bool has_stack () override { return true; } | |||||
bool has_registers () override { return true; } | |||||
}; | |||||
/* Target ops for libkvm interface. */ | |||||
static fbsd_kvm_target fbsd_kvm_ops; | |||||
#ifdef HAVE_KVM_OPEN2 | #ifdef HAVE_KVM_OPEN2 | ||||
static int | static int | ||||
kgdb_resolve_symbol(const char *name, kvaddr_t *kva) | kgdb_resolve_symbol(const char *name, kvaddr_t *kva) | ||||
{ | { | ||||
struct bound_minimal_symbol ms; | struct bound_minimal_symbol ms; | ||||
ms = lookup_minimal_symbol (name, NULL, NULL); | ms = lookup_minimal_symbol (name, NULL, NULL); | ||||
if (ms.minsym == NULL) | if (ms.minsym == NULL) | ||||
return (1); | return (1); | ||||
*kva = BMSYMBOL_VALUE_ADDRESS (ms); | *kva = BMSYMBOL_VALUE_ADDRESS (ms); | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif | ||||
static void | static void | ||||
kgdb_trgt_open(const char *args, int from_tty) | fbsd_kvm_target_open (const char *args, int from_tty) | ||||
{ | { | ||||
struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | ||||
gdbarch_data (target_gdbarch(), fbsd_vmcore_data); | gdbarch_data (target_gdbarch(), fbsd_vmcore_data); | ||||
char kvm_err[_POSIX2_LINE_MAX]; | |||||
struct inferior *inf; | struct inferior *inf; | ||||
struct cleanup *old_chain; | struct cleanup *old_chain; | ||||
struct thread_info *ti; | struct thread_info *ti; | ||||
struct kthr *kt; | struct kthr *kt; | ||||
kvm_t *nkvm; | kvm_t *nkvm; | ||||
char *temp, *kernel, *filename; | char *temp, *kernel, *filename; | ||||
bool writeable; | bool writeable; | ||||
int ontop; | int ontop; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | #else | ||||
nkvm = kvm_openfiles(kernel, filename, NULL, | nkvm = kvm_openfiles(kernel, filename, NULL, | ||||
writeable ? O_RDWR : O_RDONLY, kvm_err); | writeable ? O_RDWR : O_RDONLY, kvm_err); | ||||
#endif | #endif | ||||
if (nkvm == NULL) | if (nkvm == NULL) | ||||
error ("Failed to open vmcore: %s", kvm_err); | error ("Failed to open vmcore: %s", kvm_err); | ||||
/* Don't free the filename now and close any previous vmcore. */ | /* Don't free the filename now and close any previous vmcore. */ | ||||
discard_cleanups(old_chain); | discard_cleanups(old_chain); | ||||
unpush_target(&kgdb_trgt_ops); | unpush_target(&fbsd_kvm_ops); | ||||
/* | /* | ||||
* Determine the first address in KVA. Newer kernels export | * Determine the first address in KVA. Newer kernels export | ||||
* VM_MAXUSER_ADDRESS and the first kernel address can be | * VM_MAXUSER_ADDRESS and the first kernel address can be | ||||
* determined by adding one. Older kernels do not provide a | * determined by adding one. Older kernels do not provide a | ||||
* symbol that is valid on all platforms, but kernbase is close | * symbol that is valid on all platforms, but kernbase is close | ||||
* for most platforms. | * for most platforms. | ||||
*/ | */ | ||||
Show All 26 Lines | |||||
#else | #else | ||||
pcb_size = sizeof(struct pcb); | pcb_size = sizeof(struct pcb); | ||||
#endif | #endif | ||||
} END_CATCH | } END_CATCH | ||||
} | } | ||||
kvm = nkvm; | kvm = nkvm; | ||||
vmcore = filename; | vmcore = filename; | ||||
old_chain = make_cleanup(kgdb_core_cleanup, NULL); | push_target (&fbsd_kvm_ops); | ||||
push_target (&kgdb_trgt_ops); | |||||
discard_cleanups (old_chain); | |||||
kgdb_dmesg(); | kgdb_dmesg(); | ||||
inf = current_inferior(); | inf = current_inferior(); | ||||
if (inf->pid == 0) { | if (inf->pid == 0) { | ||||
inferior_appeared(inf, 1); | inferior_appeared(inf, 1); | ||||
inf->fake_pid_p = 1; | inf->fake_pid_p = 1; | ||||
} | } | ||||
solib_create_inferior_hook(0); | solib_create_inferior_hook(0); | ||||
init_thread_list(); | init_thread_list(); | ||||
kt = kgdb_thr_init(ops->cpu_pcb_addr); | kt = kgdb_thr_init(ops->cpu_pcb_addr); | ||||
while (kt != NULL) { | while (kt != NULL) { | ||||
ti = add_thread_silent(fbsd_vmcore_ptid(kt->tid)); | ti = add_thread_silent(fbsd_vmcore_ptid(kt->tid)); | ||||
kt = kgdb_thr_next(kt); | kt = kgdb_thr_next(kt); | ||||
} | } | ||||
if (curkthr != 0) | if (curkthr != 0) | ||||
inferior_ptid = fbsd_vmcore_ptid(curkthr->tid); | inferior_ptid = fbsd_vmcore_ptid(curkthr->tid); | ||||
target_fetch_registers (get_current_regcache (), -1); | target_fetch_registers (get_current_regcache (), -1); | ||||
reinit_frame_cache (); | reinit_frame_cache (); | ||||
print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC, 1); | print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC, 1); | ||||
} | } | ||||
static void | void | ||||
kgdb_trgt_close(struct target_ops *self) | fbsd_kvm_target::close() | ||||
{ | { | ||||
if (kvm != NULL) { | if (kvm != NULL) { | ||||
clear_solib(); | clear_solib(); | ||||
if (kvm_close(kvm) != 0) | if (kvm_close(kvm) != 0) | ||||
warning("cannot close \"%s\": %s", vmcore, | warning("cannot close \"%s\": %s", vmcore, | ||||
kvm_geterr(kvm)); | kvm_geterr(kvm)); | ||||
kvm = NULL; | kvm = NULL; | ||||
xfree(vmcore); | xfree(vmcore); | ||||
vmcore = NULL; | vmcore = NULL; | ||||
} | } | ||||
inferior_ptid = null_ptid; | inferior_ptid = null_ptid; | ||||
} | } | ||||
#if 0 | |||||
static void | static void | ||||
kgdb_core_cleanup(void *arg) | |||||
{ | |||||
kgdb_trgt_close(0); | |||||
} | |||||
static void | |||||
kgdb_trgt_detach(struct target_ops *ops, const char *args, int from_tty) | kgdb_trgt_detach(struct target_ops *ops, const char *args, int from_tty) | ||||
{ | { | ||||
if (args) | if (args) | ||||
error ("Too many arguments"); | error ("Too many arguments"); | ||||
unpush_target(&kgdb_trgt_ops); | unpush_target(&kgdb_trgt_ops); | ||||
reinit_frame_cache(); | reinit_frame_cache(); | ||||
if (from_tty) | if (from_tty) | ||||
printf_filtered("No vmcore file now.\n"); | printf_filtered("No vmcore file now.\n"); | ||||
} | } | ||||
#endif | |||||
static const char * | const char * | ||||
kgdb_trgt_extra_thread_info(struct target_ops *ops, struct thread_info *ti) | fbsd_kvm_target::extra_thread_info(thread_info *ti) | ||||
{ | { | ||||
return (kgdb_thr_extra_thread_info(ptid_get_tid(ti->ptid))); | return (kgdb_thr_extra_thread_info(ti->ptid.tid())); | ||||
} | } | ||||
static void | void | ||||
kgdb_trgt_files_info(struct target_ops *target) | fbsd_kvm_target::files_info() | ||||
{ | { | ||||
printf_filtered ("\t`%s', ", vmcore); | printf_filtered ("\t`%s', ", vmcore); | ||||
wrap_here (" "); | wrap_here (" "); | ||||
printf_filtered ("file type %s.\n", "FreeBSD kernel vmcore"); | printf_filtered ("file type %s.\n", "FreeBSD kernel vmcore"); | ||||
} | } | ||||
static void | void | ||||
kgdb_trgt_update_thread_list(struct target_ops *ops) | fbsd_kvm_target::update_thread_list() | ||||
{ | { | ||||
/* | /* | ||||
* XXX: We should probably rescan the thread list here and update | * XXX: We should probably rescan the thread list here and update | ||||
* it if there are any changes. One nit though is that we'd have | * it if there are any changes. One nit though is that we'd have | ||||
* to detect exited threads. | * to detect exited threads. | ||||
*/ | */ | ||||
gdb_assert(kvm != NULL); | gdb_assert(kvm != NULL); | ||||
#if 0 | #if 0 | ||||
prune_threads(); | prune_threads(); | ||||
#endif | #endif | ||||
#if 0 | #if 0 | ||||
struct target_ops *tb; | struct target_ops *tb; | ||||
if (kvm != NULL) | if (kvm != NULL) | ||||
return; | return; | ||||
tb = find_target_beneath(ops); | tb = find_target_beneath(ops); | ||||
if (tb->to_update_thread_list != NULL) | if (tb->to_update_thread_list != NULL) | ||||
tb->to_update_thread_list(tb); | tb->to_update_thread_list(tb); | ||||
#endif | #endif | ||||
} | } | ||||
static const char * | const char * | ||||
kgdb_trgt_pid_to_str(struct target_ops *ops, ptid_t ptid) | fbsd_kvm_target::pid_to_str(ptid_t ptid) | ||||
{ | { | ||||
static char buf[33]; | static char buf[33]; | ||||
snprintf(buf, sizeof(buf), "Thread %ld", ptid_get_tid(ptid)); | snprintf(buf, sizeof(buf), "Thread %ld", ptid.tid()); | ||||
return (buf); | return (buf); | ||||
} | } | ||||
static int | bool | ||||
kgdb_trgt_thread_alive(struct target_ops *ops, ptid_t ptid) | fbsd_kvm_target::thread_alive(ptid_t ptid) | ||||
{ | { | ||||
return (kgdb_thr_lookup_tid(ptid_get_tid(ptid)) != NULL); | return (kgdb_thr_lookup_tid(ptid.tid()) != NULL); | ||||
} | } | ||||
static void | void | ||||
kgdb_trgt_fetch_registers(struct target_ops *tops, | fbsd_kvm_target::fetch_registers(struct regcache *regcache, int regnum) | ||||
struct regcache *regcache, int regnum) | |||||
{ | { | ||||
struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | ||||
gdbarch_data (target_gdbarch(), fbsd_vmcore_data); | gdbarch_data (target_gdbarch(), fbsd_vmcore_data); | ||||
struct kthr *kt; | struct kthr *kt; | ||||
if (ops->supply_pcb == NULL) | if (ops->supply_pcb == NULL) | ||||
return; | return; | ||||
kt = kgdb_thr_lookup_tid(ptid_get_tid(inferior_ptid)); | kt = kgdb_thr_lookup_tid(inferior_ptid.tid()); | ||||
if (kt == NULL) | if (kt == NULL) | ||||
return; | return; | ||||
ops->supply_pcb(regcache, kt->pcb); | ops->supply_pcb(regcache, kt->pcb); | ||||
} | } | ||||
static enum target_xfer_status | enum target_xfer_status | ||||
kgdb_trgt_xfer_partial(struct target_ops *ops, enum target_object object, | fbsd_kvm_target::xfer_partial(enum target_object object, | ||||
const char *annex, gdb_byte *readbuf, | const char *annex, gdb_byte *readbuf, | ||||
const gdb_byte *writebuf, | const gdb_byte *writebuf, | ||||
ULONGEST offset, ULONGEST len, ULONGEST *xfered_len) | ULONGEST offset, ULONGEST len, ULONGEST *xfered_len) | ||||
{ | { | ||||
ssize_t nbytes; | ssize_t nbytes; | ||||
gdb_assert(kvm != NULL); | gdb_assert(kvm != NULL); | ||||
switch (object) { | switch (object) { | ||||
Show All 13 Lines | if (nbytes == 0) | ||||
return TARGET_XFER_EOF; | return TARGET_XFER_EOF; | ||||
*xfered_len = nbytes; | *xfered_len = nbytes; | ||||
return TARGET_XFER_OK; | return TARGET_XFER_OK; | ||||
default: | default: | ||||
return TARGET_XFER_E_IO; | return TARGET_XFER_E_IO; | ||||
} | } | ||||
} | } | ||||
#if 0 | |||||
static int | static int | ||||
kgdb_trgt_insert_breakpoint(struct target_ops *ops, struct gdbarch *gdbarch, | kgdb_trgt_insert_breakpoint(struct target_ops *ops, struct gdbarch *gdbarch, | ||||
struct bp_target_info *bp_tgt) | struct bp_target_info *bp_tgt) | ||||
{ | { | ||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
kgdb_trgt_remove_breakpoint(struct target_ops *ops, struct gdbarch *gdbarch, | kgdb_trgt_remove_breakpoint(struct target_ops *ops, struct gdbarch *gdbarch, | ||||
struct bp_target_info *bp_tgt, enum remove_bp_reason reason) | struct bp_target_info *bp_tgt, enum remove_bp_reason reason) | ||||
{ | { | ||||
return 0; | return 0; | ||||
} | } | ||||
#endif | |||||
static void | static void | ||||
kgdb_switch_to_thread(const char *arg, int tid) | kgdb_switch_to_thread(const char *arg, int tid) | ||||
{ | { | ||||
struct thread_info *tp; | struct thread_info *tp; | ||||
tp = find_thread_ptid (fbsd_vmcore_ptid (tid)); | tp = find_thread_ptid (fbsd_vmcore_ptid (tid)); | ||||
if (tp == NULL) | if (tp == NULL) | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (kvm != NULL && addr >= kernstart) { | ||||
thr = kgdb_thr_lookup_taddr(addr); | thr = kgdb_thr_lookup_taddr(addr); | ||||
if (thr == NULL) | if (thr == NULL) | ||||
error("invalid thread address"); | error("invalid thread address"); | ||||
addr = thr->tid; | addr = thr->tid; | ||||
} | } | ||||
kgdb_switch_to_thread(arg, addr); | kgdb_switch_to_thread(arg, addr); | ||||
} | } | ||||
static int | |||||
kgdb_trgt_return_one(struct target_ops *ops) | |||||
{ | |||||
return 1; | |||||
} | |||||
void | void | ||||
_initialize_kgdb_target(void) | _initialize_kgdb_target(void) | ||||
{ | { | ||||
kgdb_trgt_ops.to_magic = OPS_MAGIC; | add_target(fbsd_kvm_target_info, fbsd_kvm_target_open); | ||||
kgdb_trgt_ops.to_shortname = "vmcore"; | |||||
kgdb_trgt_ops.to_longname = "kernel core dump file"; | |||||
kgdb_trgt_ops.to_doc = "Use a vmcore file as a target.\n\ | |||||
If no filename is specified, /dev/mem is used to examine the running kernel.\n\ | |||||
target vmcore [-w] [filename]"; | |||||
kgdb_trgt_ops.to_stratum = process_stratum; | |||||
kgdb_trgt_ops.to_has_memory = kgdb_trgt_return_one; | |||||
kgdb_trgt_ops.to_has_registers = kgdb_trgt_return_one; | |||||
kgdb_trgt_ops.to_has_stack = kgdb_trgt_return_one; | |||||
kgdb_trgt_ops.to_open = kgdb_trgt_open; | |||||
kgdb_trgt_ops.to_close = kgdb_trgt_close; | |||||
kgdb_trgt_ops.to_detach = kgdb_trgt_detach; | |||||
kgdb_trgt_ops.to_extra_thread_info = kgdb_trgt_extra_thread_info; | |||||
kgdb_trgt_ops.to_fetch_registers = kgdb_trgt_fetch_registers; | |||||
kgdb_trgt_ops.to_files_info = kgdb_trgt_files_info; | |||||
kgdb_trgt_ops.to_update_thread_list = kgdb_trgt_update_thread_list; | |||||
kgdb_trgt_ops.to_pid_to_str = kgdb_trgt_pid_to_str; | |||||
kgdb_trgt_ops.to_thread_alive = kgdb_trgt_thread_alive; | |||||
kgdb_trgt_ops.to_xfer_partial = kgdb_trgt_xfer_partial; | |||||
kgdb_trgt_ops.to_insert_breakpoint = kgdb_trgt_insert_breakpoint; | |||||
kgdb_trgt_ops.to_remove_breakpoint = kgdb_trgt_remove_breakpoint; | |||||
add_target(&kgdb_trgt_ops); | |||||
fbsd_vmcore_data = gdbarch_data_register_pre_init(fbsd_vmcore_init); | fbsd_vmcore_data = gdbarch_data_register_pre_init(fbsd_vmcore_init); | ||||
add_com ("proc", class_obscure, kgdb_set_proc_cmd, | add_com ("proc", class_obscure, kgdb_set_proc_cmd, | ||||
"Set current process context"); | "Set current process context"); | ||||
add_com ("tid", class_obscure, kgdb_set_tid_cmd, | add_com ("tid", class_obscure, kgdb_set_tid_cmd, | ||||
"Set current thread context"); | "Set current thread context"); | ||||
} | } | ||||
Show All 10 Lines |