Changeset View
Changeset View
Standalone View
Standalone View
devel/gdb/files/kgdb/fbsd-kvm.c
Show All 35 Lines | |||||
#include "osabi.h" | #include "osabi.h" | ||||
#include "process-stratum-target.h" | #include "process-stratum-target.h" | ||||
#include "solib.h" | #include "solib.h" | ||||
#include "target.h" | #include "target.h" | ||||
#include "value.h" | #include "value.h" | ||||
#include "readline/tilde.h" | #include "readline/tilde.h" | ||||
#include "gdbsupport/buildargv.h" | #include "gdbsupport/buildargv.h" | ||||
#include "gdbsupport/pathstuff.h" | #include "gdbsupport/pathstuff.h" | ||||
#include "gdbsupport/gdb_tilde_expand.h" | |||||
#include <sys/user.h> | #include <sys/user.h> | ||||
#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 char *vmcore; | static std::string vmcore; | ||||
/* Per-architecture data key. */ | |||||
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) = nullptr; | ||||
/* Return address of pcb for thread running on a CPU. */ | /* Return address of pcb for thread running on a CPU. */ | ||||
CORE_ADDR (*cpu_pcb_addr)(u_int); | CORE_ADDR (*cpu_pcb_addr)(u_int) = nullptr; | ||||
}; | }; | ||||
static void * | /* Per-architecture data key. */ | ||||
fbsd_vmcore_init (struct obstack *obstack) | static const registry<gdbarch>::key<struct fbsd_vmcore_ops> fbsd_vmcore_data; | ||||
{ | |||||
struct fbsd_vmcore_ops *ops; | |||||
ops = OBSTACK_ZALLOC (obstack, struct fbsd_vmcore_ops); | static struct fbsd_vmcore_ops * | ||||
get_fbsd_vmcore_ops (struct gdbarch *gdbarch) | |||||
{ | |||||
struct fbsd_vmcore_ops *ops = fbsd_vmcore_data.get (gdbarch); | |||||
if (ops == nullptr) | |||||
ops = fbsd_vmcore_data.emplace (gdbarch); | |||||
return ops; | return ops; | ||||
} | } | ||||
/* Set the function that supplies registers from a pcb | /* Set the function that supplies registers from a pcb | ||||
for architecture GDBARCH to SUPPLY_PCB. */ | for architecture GDBARCH to SUPPLY_PCB. */ | ||||
void | void | ||||
fbsd_vmcore_set_supply_pcb (struct gdbarch *gdbarch, | fbsd_vmcore_set_supply_pcb (struct gdbarch *gdbarch, | ||||
void (*supply_pcb) (struct regcache *, | void (*supply_pcb) (struct regcache *, | ||||
CORE_ADDR)) | CORE_ADDR)) | ||||
{ | { | ||||
struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | struct fbsd_vmcore_ops *ops = get_fbsd_vmcore_ops (gdbarch); | ||||
gdbarch_data (gdbarch, fbsd_vmcore_data); | |||||
ops->supply_pcb = supply_pcb; | ops->supply_pcb = supply_pcb; | ||||
} | } | ||||
/* Set the function that returns the address of the pcb for a thread | /* Set the function that returns the address of the pcb for a thread | ||||
running on a CPU for | running on a CPU for | ||||
architecture GDBARCH to CPU_PCB_ADDR. */ | architecture GDBARCH to CPU_PCB_ADDR. */ | ||||
void | void | ||||
fbsd_vmcore_set_cpu_pcb_addr (struct gdbarch *gdbarch, | fbsd_vmcore_set_cpu_pcb_addr (struct gdbarch *gdbarch, | ||||
CORE_ADDR (*cpu_pcb_addr) (u_int)) | CORE_ADDR (*cpu_pcb_addr) (u_int)) | ||||
{ | { | ||||
struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | struct fbsd_vmcore_ops *ops = get_fbsd_vmcore_ops (gdbarch); | ||||
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; | ||||
int kgdb_quiet; | int kgdb_quiet; | ||||
static ptid_t | static ptid_t | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | enum target_xfer_status xfer_partial (enum target_object object, | ||||
const char *annex, | const char *annex, | ||||
gdb_byte *readbuf, | gdb_byte *readbuf, | ||||
const gdb_byte *writebuf, | const gdb_byte *writebuf, | ||||
ULONGEST offset, ULONGEST len, | ULONGEST offset, ULONGEST len, | ||||
ULONGEST *xfered_len) override; | ULONGEST *xfered_len) override; | ||||
void files_info () override; | void files_info () override; | ||||
bool thread_alive (ptid_t ptid) override; | bool thread_alive (ptid_t ptid) override; | ||||
void update_thread_list () override; | |||||
std::string pid_to_str (ptid_t) override; | std::string pid_to_str (ptid_t) override; | ||||
const char *extra_thread_info (thread_info *) override; | const char *extra_thread_info (thread_info *) override; | ||||
bool has_all_memory () override { return false; } | bool has_all_memory () override { return false; } | ||||
bool has_memory () override; | bool has_memory () override; | ||||
bool has_stack () override; | bool has_stack () override; | ||||
bool has_registers () override; | bool has_registers () override; | ||||
bool has_execution (inferior *inf) override { return false; } | bool has_execution (inferior *inf) override { return false; } | ||||
}; | }; | ||||
/* Target ops for libkvm interface. */ | /* Target ops for libkvm interface. */ | ||||
static fbsd_kvm_target fbsd_kvm_ops; | 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 = ms.value_address (); | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif | ||||
static void | static void | ||||
fbsd_kvm_target_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 = get_fbsd_vmcore_ops (target_gdbarch ()); | ||||
gdbarch_data (target_gdbarch(), fbsd_vmcore_data); | |||||
char kvm_err[_POSIX2_LINE_MAX]; | char kvm_err[_POSIX2_LINE_MAX]; | ||||
struct inferior *inf; | struct inferior *inf; | ||||
struct cleanup *old_chain; | struct cleanup *old_chain; | ||||
struct kthr *kt; | struct kthr *kt; | ||||
kvm_t *nkvm; | kvm_t *nkvm; | ||||
const char *kernel; | const char *kernel; | ||||
char *temp, *filename; | std::string filename; | ||||
bool writeable; | bool writeable; | ||||
if (ops == NULL || ops->supply_pcb == NULL || ops->cpu_pcb_addr == NULL) | if (ops == NULL || ops->supply_pcb == NULL || ops->cpu_pcb_addr == NULL) | ||||
error ("ABI doesn't support a vmcore target"); | error ("ABI doesn't support a vmcore target"); | ||||
target_preopen (from_tty); | target_preopen (from_tty); | ||||
kernel = get_exec_file (0); | kernel = get_exec_file (0); | ||||
if (kernel == NULL) | if (kernel == NULL) | ||||
error ("Can't open a vmcore without a kernel"); | error ("Can't open a vmcore without a kernel"); | ||||
writeable = false; | writeable = false; | ||||
filename = NULL; | |||||
if (args != NULL) { | if (args != NULL) { | ||||
gdb_argv built_argv (args); | gdb_argv built_argv (args); | ||||
for (char **argv = built_argv.get (); *argv != NULL; argv++) { | for (char **argv = built_argv.get (); *argv != NULL; argv++) { | ||||
if (**argv == '-') { | if (**argv == '-') { | ||||
if (strcmp (*argv, "-w") == 0) | if (strcmp (*argv, "-w") == 0) | ||||
writeable = true; | writeable = true; | ||||
else | else | ||||
error (_("Invalid argument")); | error (_("Invalid argument")); | ||||
} else { | } else { | ||||
if (filename != NULL) | if (!filename.empty ()) | ||||
error (_("Invalid argument")); | error (_("Invalid argument")); | ||||
filename = tilde_expand (*argv); | filename = gdb_tilde_expand (*argv); | ||||
if (filename[0] != '/') { | if (!IS_ABSOLUTE_PATH (filename)) | ||||
gdb::unique_xmalloc_ptr<char> temp (gdb_abspath (filename)); | filename = gdb_abspath (filename.c_str ()); | ||||
xfree (filename); | |||||
filename = temp.release (); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
#ifdef HAVE_KVM_OPEN2 | #ifdef HAVE_KVM_OPEN2 | ||||
nkvm = kvm_open2(kernel, filename, | nkvm = kvm_open2(kernel, filename.c_str (), | ||||
writeable ? O_RDWR : O_RDONLY, kvm_err, kgdb_resolve_symbol); | writeable ? O_RDWR : O_RDONLY, kvm_err, kgdb_resolve_symbol); | ||||
#else | #else | ||||
nkvm = kvm_openfiles(kernel, filename, NULL, | nkvm = kvm_openfiles(kernel, filename.c_str (), NULL, | ||||
writeable ? O_RDWR : O_RDONLY, kvm_err); | writeable ? O_RDWR : O_RDONLY, kvm_err); | ||||
#endif | #endif | ||||
if (nkvm == NULL) { | if (nkvm == NULL) { | ||||
xfree (filename); | |||||
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. */ | ||||
current_inferior ()->unpush_target (&fbsd_kvm_ops); | current_inferior ()->unpush_target (&fbsd_kvm_ops); | ||||
#ifdef HAVE_KVM_DISP | #ifdef HAVE_KVM_DISP | ||||
/* Relocate kernel objfile if needed. */ | /* Relocate kernel objfile if needed. */ | ||||
struct objfile *symfile_objfile = | struct objfile *symfile_objfile = | ||||
current_program_space->symfile_object_file; | current_program_space->symfile_object_file; | ||||
if (symfile_objfile != nullptr && | if (symfile_objfile != nullptr && | ||||
(bfd_get_file_flags(symfile_objfile->obfd) & | (bfd_get_file_flags(symfile_objfile->obfd.get ()) & | ||||
(EXEC_P | DYNAMIC)) != 0) { | (EXEC_P | DYNAMIC)) != 0) { | ||||
CORE_ADDR displacement = kvm_kerndisp(nkvm); | CORE_ADDR displacement = kvm_kerndisp(nkvm); | ||||
if (displacement != 0) { | if (displacement != 0) { | ||||
section_offsets new_offsets (symfile_objfile->section_offsets.size (), | section_offsets new_offsets (symfile_objfile->section_offsets.size (), | ||||
displacement); | displacement); | ||||
objfile_relocate(symfile_objfile, new_offsets); | objfile_relocate(symfile_objfile, new_offsets); | ||||
} | } | ||||
} | } | ||||
Show All 34 Lines | #ifdef HAVE_KVM_OPEN2 | ||||
pcb_size = 0; | pcb_size = 0; | ||||
#else | #else | ||||
pcb_size = sizeof(struct pcb); | pcb_size = sizeof(struct pcb); | ||||
#endif | #endif | ||||
} | } | ||||
} | } | ||||
kvm = nkvm; | kvm = nkvm; | ||||
vmcore = filename; | vmcore = std::move(filename); | ||||
current_inferior()->push_target (&fbsd_kvm_ops); | current_inferior()->push_target (&fbsd_kvm_ops); | ||||
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; | ||||
Show All 21 Lines | |||||
{ | { | ||||
if (kvm != NULL) { | if (kvm != NULL) { | ||||
switch_to_no_thread (); | switch_to_no_thread (); | ||||
exit_inferior_silent (current_inferior ()); | exit_inferior_silent (current_inferior ()); | ||||
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.c_str (), | ||||
kvm_geterr(kvm)); | kvm_geterr(kvm)); | ||||
kvm = NULL; | kvm = NULL; | ||||
xfree(vmcore); | vmcore.clear (); | ||||
vmcore = NULL; | |||||
} | } | ||||
} | } | ||||
#if 0 | #if 0 | ||||
static void | 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"); | gdb_printf("No vmcore file now.\n"); | ||||
} | } | ||||
#endif | #endif | ||||
const char * | const char * | ||||
fbsd_kvm_target::extra_thread_info(thread_info *ti) | fbsd_kvm_target::extra_thread_info(thread_info *ti) | ||||
{ | { | ||||
return (kgdb_thr_extra_thread_info(ti->ptid.tid())); | return (kgdb_thr_extra_thread_info(ti->ptid.tid())); | ||||
Show All 16 Lines | |||||
{ | { | ||||
return (kvm != NULL); | return (kvm != NULL); | ||||
} | } | ||||
void | void | ||||
fbsd_kvm_target::files_info() | fbsd_kvm_target::files_info() | ||||
{ | { | ||||
printf_filtered ("\t`%s', ", vmcore); | gdb_printf ("\t`%s', ", vmcore.c_str ()); | ||||
gdb_stdout->wrap_here (8); | gdb_stdout->wrap_here (8); | ||||
printf_filtered ("file type %s.\n", "FreeBSD kernel vmcore"); | gdb_printf ("file type %s.\n", "FreeBSD kernel vmcore"); | ||||
} | } | ||||
void | |||||
fbsd_kvm_target::update_thread_list() | |||||
{ | |||||
/* | |||||
* 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 | |||||
* to detect exited threads. | |||||
*/ | |||||
gdb_assert(kvm != NULL); | |||||
#if 0 | |||||
prune_threads(); | |||||
#endif | |||||
#if 0 | |||||
struct target_ops *tb; | |||||
if (kvm != NULL) | |||||
return; | |||||
tb = find_target_beneath(ops); | |||||
if (tb->to_update_thread_list != NULL) | |||||
tb->to_update_thread_list(tb); | |||||
#endif | |||||
} | |||||
std::string | std::string | ||||
fbsd_kvm_target::pid_to_str(ptid_t ptid) | fbsd_kvm_target::pid_to_str(ptid_t ptid) | ||||
{ | { | ||||
return string_printf (_("Thread %llu"), ptid.tid ()); | return string_printf (_("Thread %ld"), ptid.tid ()); | ||||
} | } | ||||
bool | bool | ||||
fbsd_kvm_target::thread_alive(ptid_t ptid) | fbsd_kvm_target::thread_alive(ptid_t ptid) | ||||
{ | { | ||||
return (kgdb_thr_lookup_tid(ptid.tid()) != NULL); | return (kgdb_thr_lookup_tid(ptid.tid()) != NULL); | ||||
} | } | ||||
void | void | ||||
fbsd_kvm_target::fetch_registers(struct regcache *regcache, int regnum) | fbsd_kvm_target::fetch_registers(struct regcache *regcache, int regnum) | ||||
{ | { | ||||
struct fbsd_vmcore_ops *ops = (struct fbsd_vmcore_ops *) | struct fbsd_vmcore_ops *ops = get_fbsd_vmcore_ops (target_gdbarch ()); | ||||
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(regcache->ptid().tid()); | kt = kgdb_thr_lookup_tid(regcache->ptid().tid()); | ||||
if (kt == NULL) | if (kt == NULL) | ||||
return; | return; | ||||
ops->supply_pcb(regcache, kt->pcb); | ops->supply_pcb(regcache, kt->pcb); | ||||
Show All 25 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 | |||||
kgdb_trgt_insert_breakpoint(struct target_ops *ops, struct gdbarch *gdbarch, | |||||
struct bp_target_info *bp_tgt) | |||||
{ | |||||
return 0; | |||||
} | |||||
static int | |||||
kgdb_trgt_remove_breakpoint(struct target_ops *ops, struct gdbarch *gdbarch, | |||||
struct bp_target_info *bp_tgt, enum remove_bp_reason reason) | |||||
{ | |||||
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_kvm_ops, fbsd_vmcore_ptid (tid)); | tp = find_thread_ptid (&fbsd_kvm_ops, fbsd_vmcore_ptid (tid)); | ||||
if (tp == NULL) | if (tp == NULL) | ||||
error ("invalid tid"); | error ("invalid tid"); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
void _initialize_kgdb_target (); | void _initialize_kgdb_target (); | ||||
void | void | ||||
_initialize_kgdb_target () | _initialize_kgdb_target () | ||||
{ | { | ||||
add_target(fbsd_kvm_target_info, fbsd_kvm_target_open, | add_target(fbsd_kvm_target_info, fbsd_kvm_target_open, | ||||
filename_completer); | filename_completer); | ||||
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"); | ||||
} | } | ||||
CORE_ADDR | CORE_ADDR | ||||
kgdb_trgt_stop_pcb(u_int cpuid) | kgdb_trgt_stop_pcb(u_int cpuid) | ||||
{ | { | ||||
if (stoppcbs == 0 || pcb_size == 0) | if (stoppcbs == 0 || pcb_size == 0) | ||||
return 0; | return 0; | ||||
return (stoppcbs + pcb_size * cpuid); | return (stoppcbs + pcb_size * cpuid); | ||||
} | } |