Support 'info proc' for native FreeBSD processes.

- Command line arguments are fetched via the kern.proc.args.<pid>
  sysctl.
- The 'cwd' and 'exe' values are obtained from the per-process
  file descriptor table returned by kinfo_getfile() from libutil.
- 'mappings' is implemented by walking the array of VM map entries
  returned by kinfo_getvmmap() from libutil.
- 'status' output is generated by outputting fields from the structure
  returned by the kern.proc.pid.<pid> sysctl.
- 'stat' is aliased to 'status'.

gdb/ChangeLog:

	* configure.ac: Check for kinfo_getfile in libutil.
	* configure: Regenerate.
	* config.in: Regenerate.
	* fbsd-nat.c: Include "fbsd-tdep.h".
	(fbsd_fetch_cmdline): New.
	(fbsd_fetch_kinfo_proc): Move earlier and change to return a bool
	rather than calling error.
	(fbsd_info_proc): New.
	(fbsd_thread_name): Report error if fbsd_fetch_kinfo_proc fails.
	(fbsd_wait): Report warning if fbsd_fetch_kinfo_proc fails.
	(fbsd_nat_add_target): Set "to_info_proc" to "fbsd_info_proc".
This commit is contained in:
John Baldwin
2018-01-09 13:35:17 -08:00
parent 262f62f57d
commit 92fce24de2
5 changed files with 421 additions and 26 deletions

View File

@@ -32,14 +32,16 @@
#include <sys/signal.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#ifdef HAVE_KINFO_GETVMMAP
#if defined(HAVE_KINFO_GETFILE) || defined(HAVE_KINFO_GETVMMAP)
#include <libutil.h>
#else
#endif
#if !defined(HAVE_KINFO_GETVMMAP)
#include "filestuff.h"
#endif
#include "elf-bfd.h"
#include "fbsd-nat.h"
#include "fbsd-tdep.h"
#include <list>
@@ -205,6 +207,331 @@ fbsd_find_memory_regions (struct target_ops *self,
}
#endif
/* Fetch the command line for a running process. */
static gdb::unique_xmalloc_ptr<char>
fbsd_fetch_cmdline (pid_t pid)
{
size_t len;
int mib[4];
len = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ARGS;
mib[3] = pid;
if (sysctl (mib, 4, NULL, &len, NULL, 0) == -1)
return nullptr;
if (len == 0)
return nullptr;
gdb::unique_xmalloc_ptr<char> cmdline ((char *) xmalloc (len));
if (sysctl (mib, 4, cmdline.get (), &len, NULL, 0) == -1)
return nullptr;
return cmdline;
}
/* Fetch the external variant of the kernel's internal process
structure for the process PID into KP. */
static bool
fbsd_fetch_kinfo_proc (pid_t pid, struct kinfo_proc *kp)
{
size_t len;
int mib[4];
len = sizeof *kp;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
return (sysctl (mib, 4, kp, &len, NULL, 0) == 0);
}
/* Implement the "to_info_proc target_ops" method. */
static void
fbsd_info_proc (struct target_ops *ops, const char *args,
enum info_proc_what what)
{
#ifdef HAVE_KINFO_GETFILE
gdb::unique_xmalloc_ptr<struct kinfo_file> fdtbl;
int nfd = 0;
#endif
struct kinfo_proc kp;
char *tmp;
pid_t pid;
bool do_cmdline = false;
bool do_cwd = false;
bool do_exe = false;
#ifdef HAVE_KINFO_GETVMMAP
bool do_mappings = false;
#endif
bool do_status = false;
switch (what)
{
case IP_MINIMAL:
do_cmdline = true;
do_cwd = true;
do_exe = true;
break;
#ifdef HAVE_KINFO_GETVMMAP
case IP_MAPPINGS:
do_mappings = true;
break;
#endif
case IP_STATUS:
case IP_STAT:
do_status = true;
break;
case IP_CMDLINE:
do_cmdline = true;
break;
case IP_EXE:
do_exe = true;
break;
case IP_CWD:
do_cwd = true;
break;
case IP_ALL:
do_cmdline = true;
do_cwd = true;
do_exe = true;
#ifdef HAVE_KINFO_GETVMMAP
do_mappings = true;
#endif
do_status = true;
break;
default:
error (_("Not supported on this target."));
}
gdb_argv built_argv (args);
if (built_argv.count () == 0)
{
pid = ptid_get_pid (inferior_ptid);
if (pid == 0)
error (_("No current process: you must name one."));
}
else if (built_argv.count () == 1 && isdigit (built_argv[0][0]))
pid = strtol (built_argv[0], NULL, 10);
else
error (_("Invalid arguments."));
printf_filtered (_("process %d\n"), pid);
#ifdef HAVE_KINFO_GETFILE
if (do_cwd || do_exe)
fdtbl.reset (kinfo_getfile (pid, &nfd));
#endif
if (do_cmdline)
{
gdb::unique_xmalloc_ptr<char> cmdline = fbsd_fetch_cmdline (pid);
if (cmdline != nullptr)
printf_filtered ("cmdline = '%s'\n", cmdline.get ());
else
warning (_("unable to fetch command line"));
}
if (do_cwd)
{
const char *cwd = NULL;
#ifdef HAVE_KINFO_GETFILE
struct kinfo_file *kf = fdtbl.get ();
for (int i = 0; i < nfd; i++, kf++)
{
if (kf->kf_type == KF_TYPE_VNODE && kf->kf_fd == KF_FD_TYPE_CWD)
{
cwd = kf->kf_path;
break;
}
}
#endif
if (cwd != NULL)
printf_filtered ("cwd = '%s'\n", cwd);
else
warning (_("unable to fetch current working directory"));
}
if (do_exe)
{
const char *exe = NULL;
#ifdef HAVE_KINFO_GETFILE
struct kinfo_file *kf = fdtbl.get ();
for (int i = 0; i < nfd; i++, kf++)
{
if (kf->kf_type == KF_TYPE_VNODE && kf->kf_fd == KF_FD_TYPE_TEXT)
{
exe = kf->kf_path;
break;
}
}
#endif
if (exe == NULL)
exe = fbsd_pid_to_exec_file (ops, pid);
if (exe != NULL)
printf_filtered ("exe = '%s'\n", exe);
else
warning (_("unable to fetch executable path name"));
}
#ifdef HAVE_KINFO_GETVMMAP
if (do_mappings)
{
int nvment;
gdb::unique_xmalloc_ptr<struct kinfo_vmentry>
vmentl (kinfo_getvmmap (pid, &nvment));
if (vmentl != nullptr)
{
printf_filtered (_("Mapped address spaces:\n\n"));
#ifdef __LP64__
printf_filtered (" %18s %18s %10s %10s %9s %s\n",
"Start Addr",
" End Addr",
" Size", " Offset", "Flags ", "File");
#else
printf_filtered ("\t%10s %10s %10s %10s %9s %s\n",
"Start Addr",
" End Addr",
" Size", " Offset", "Flags ", "File");
#endif
struct kinfo_vmentry *kve = vmentl.get ();
for (int i = 0; i < nvment; i++, kve++)
{
ULONGEST start, end;
start = kve->kve_start;
end = kve->kve_end;
#ifdef __LP64__
printf_filtered (" %18s %18s %10s %10s %9s %s\n",
hex_string (start),
hex_string (end),
hex_string (end - start),
hex_string (kve->kve_offset),
fbsd_vm_map_entry_flags (kve->kve_flags,
kve->kve_protection),
kve->kve_path);
#else
printf_filtered ("\t%10s %10s %10s %10s %9s %s\n",
hex_string (start),
hex_string (end),
hex_string (end - start),
hex_string (kve->kve_offset),
fbsd_vm_map_entry_flags (kve->kve_flags,
kve->kve_protection),
kve->kve_path);
#endif
}
}
else
warning (_("unable to fetch virtual memory map"));
}
#endif
if (do_status)
{
if (!fbsd_fetch_kinfo_proc (pid, &kp))
warning (_("Failed to fetch process information"));
else
{
const char *state;
int pgtok;
printf_filtered ("Name: %s\n", kp.ki_comm);
switch (kp.ki_stat)
{
case SIDL:
state = "I (idle)";
break;
case SRUN:
state = "R (running)";
break;
case SSTOP:
state = "T (stopped)";
break;
case SZOMB:
state = "Z (zombie)";
break;
case SSLEEP:
state = "S (sleeping)";
break;
case SWAIT:
state = "W (interrupt wait)";
break;
case SLOCK:
state = "L (blocked on lock)";
break;
default:
state = "? (unknown)";
break;
}
printf_filtered ("State: %s\n", state);
printf_filtered ("Parent process: %d\n", kp.ki_ppid);
printf_filtered ("Process group: %d\n", kp.ki_pgid);
printf_filtered ("Session id: %d\n", kp.ki_sid);
printf_filtered ("TTY: %ju\n", (uintmax_t) kp.ki_tdev);
printf_filtered ("TTY owner process group: %d\n", kp.ki_tpgid);
printf_filtered ("User IDs (real, effective, saved): %d %d %d\n",
kp.ki_ruid, kp.ki_uid, kp.ki_svuid);
printf_filtered ("Group IDs (real, effective, saved): %d %d %d\n",
kp.ki_rgid, kp.ki_groups[0], kp.ki_svgid);
printf_filtered ("Groups: ");
for (int i = 0; i < kp.ki_ngroups; i++)
printf_filtered ("%d ", kp.ki_groups[i]);
printf_filtered ("\n");
printf_filtered ("Minor faults (no memory page): %ld\n",
kp.ki_rusage.ru_minflt);
printf_filtered ("Minor faults, children: %ld\n",
kp.ki_rusage_ch.ru_minflt);
printf_filtered ("Major faults (memory page faults): %ld\n",
kp.ki_rusage.ru_majflt);
printf_filtered ("Major faults, children: %ld\n",
kp.ki_rusage_ch.ru_majflt);
printf_filtered ("utime: %jd.%06ld\n",
(intmax_t) kp.ki_rusage.ru_utime.tv_sec,
kp.ki_rusage.ru_utime.tv_usec);
printf_filtered ("stime: %jd.%06ld\n",
(intmax_t) kp.ki_rusage.ru_stime.tv_sec,
kp.ki_rusage.ru_stime.tv_usec);
printf_filtered ("utime, children: %jd.%06ld\n",
(intmax_t) kp.ki_rusage_ch.ru_utime.tv_sec,
kp.ki_rusage_ch.ru_utime.tv_usec);
printf_filtered ("stime, children: %jd.%06ld\n",
(intmax_t) kp.ki_rusage_ch.ru_stime.tv_sec,
kp.ki_rusage_ch.ru_stime.tv_usec);
printf_filtered ("'nice' value: %d\n", kp.ki_nice);
printf_filtered ("Start time: %jd.%06ld\n", kp.ki_start.tv_sec,
kp.ki_start.tv_usec);
pgtok = getpagesize () / 1024;
printf_filtered ("Virtual memory size: %ju kB\n",
(uintmax_t) kp.ki_size / 1024);
printf_filtered ("Data size: %ju kB\n",
(uintmax_t) kp.ki_dsize * pgtok);
printf_filtered ("Stack size: %ju kB\n",
(uintmax_t) kp.ki_ssize * pgtok);
printf_filtered ("Text size: %ju kB\n",
(uintmax_t) kp.ki_tsize * pgtok);
printf_filtered ("Resident set size: %ju kB\n",
(uintmax_t) kp.ki_rssize * pgtok);
printf_filtered ("Maximum RSS: %ju kB\n",
(uintmax_t) kp.ki_rusage.ru_maxrss);
printf_filtered ("Pending Signals: ");
for (int i = 0; i < _SIG_WORDS; i++)
printf_filtered ("%08x ", kp.ki_siglist.__bits[i]);
printf_filtered ("\n");
printf_filtered ("Ignored Signals: ");
for (int i = 0; i < _SIG_WORDS; i++)
printf_filtered ("%08x ", kp.ki_sigignore.__bits[i]);
printf_filtered ("\n");
printf_filtered ("Caught Signals: ");
for (int i = 0; i < _SIG_WORDS; i++)
printf_filtered ("%08x ", kp.ki_sigcatch.__bits[i]);
printf_filtered ("\n");
}
}
}
#ifdef KERN_PROC_AUXV
static enum target_xfer_status (*super_xfer_partial) (struct target_ops *ops,
enum target_object object,
@@ -455,26 +782,6 @@ show_fbsd_lwp_debug (struct ui_file *file, int from_tty,
fprintf_filtered (file, _("Debugging of FreeBSD lwp module is %s.\n"), value);
}
#if defined(TDP_RFPPWAIT) || defined(HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME)
/* Fetch the external variant of the kernel's internal process
structure for the process PID into KP. */
static void
fbsd_fetch_kinfo_proc (pid_t pid, struct kinfo_proc *kp)
{
size_t len;
int mib[4];
len = sizeof *kp;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
if (sysctl (mib, 4, kp, &len, NULL, 0) == -1)
perror_with_name (("sysctl"));
}
#endif
/*
FreeBSD's first thread support was via a "reentrant" version of libc
(libc_r) that first shipped in 2.2.7. This library multiplexed all
@@ -560,7 +867,8 @@ fbsd_thread_name (struct target_ops *self, struct thread_info *thr)
/* Note that ptrace_lwpinfo returns the process command in pl_tdname
if a name has not been set explicitly. Return a NULL name in
that case. */
fbsd_fetch_kinfo_proc (pid, &kp);
if (!fbsd_fetch_kinfo_proc (pid, &kp))
perror_with_name (_("Failed to fetch process information"));
if (ptrace (PT_LWPINFO, lwp, (caddr_t) &pl, sizeof pl) == -1)
perror_with_name (("ptrace"));
if (strcmp (kp.ki_comm, pl.pl_tdname) == 0)
@@ -970,9 +1278,13 @@ fbsd_wait (struct target_ops *ops,
#ifndef PTRACE_VFORK
/* For vfork, the child process will have the P_PPWAIT
flag set. */
fbsd_fetch_kinfo_proc (child, &kp);
if (kp.ki_flag & P_PPWAIT)
ourstatus->kind = TARGET_WAITKIND_VFORKED;
if (fbsd_fetch_kinfo_proc (child, &kp))
{
if (kp.ki_flag & P_PPWAIT)
ourstatus->kind = TARGET_WAITKIND_VFORKED;
}
else
warning (_("Failed to fetch process information"));
#endif
ourstatus->value.related_pid = child_ptid;
@@ -1176,6 +1488,7 @@ fbsd_nat_add_target (struct target_ops *t)
{
t->to_pid_to_exec_file = fbsd_pid_to_exec_file;
t->to_find_memory_regions = fbsd_find_memory_regions;
t->to_info_proc = fbsd_info_proc;
#ifdef KERN_PROC_AUXV
super_xfer_partial = t->to_xfer_partial;
t->to_xfer_partial = fbsd_xfer_partial;