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

@ -1,3 +1,17 @@
2018-01-09 John Baldwin <jhb@FreeBSD.org>
* 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".
2018-01-09 John Baldwin <jhb@FreeBSD.org>
* fbsd-nat.c (struct free_deleter): Remove.

View File

@ -219,6 +219,9 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if your system has the kinfo_getfile function. */
#undef HAVE_KINFO_GETFILE
/* Define to 1 if your system has the kinfo_getvmmap function. */
#undef HAVE_KINFO_GETVMMAP

60
gdb/configure vendored
View File

@ -7927,6 +7927,66 @@ $as_echo "#define HAVE_KINFO_GETVMMAP 1" >>confdefs.h
fi
# fbsd-nat.c can also use kinfo_getfile.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing kinfo_getfile" >&5
$as_echo_n "checking for library containing kinfo_getfile... " >&6; }
if test "${ac_cv_search_kinfo_getfile+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char kinfo_getfile ();
int
main ()
{
return kinfo_getfile ();
;
return 0;
}
_ACEOF
for ac_lib in '' util util-freebsd; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_kinfo_getfile=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if test "${ac_cv_search_kinfo_getfile+set}" = set; then :
break
fi
done
if test "${ac_cv_search_kinfo_getfile+set}" = set; then :
else
ac_cv_search_kinfo_getfile=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_kinfo_getfile" >&5
$as_echo "$ac_cv_search_kinfo_getfile" >&6; }
ac_res=$ac_cv_search_kinfo_getfile
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
$as_echo "#define HAVE_KINFO_GETFILE 1" >>confdefs.h
fi
if test "X$prefix" = "XNONE"; then
acl_final_prefix="$ac_default_prefix"

View File

@ -523,6 +523,11 @@ AC_SEARCH_LIBS(kinfo_getvmmap, util util-freebsd,
[AC_DEFINE(HAVE_KINFO_GETVMMAP, 1,
[Define to 1 if your system has the kinfo_getvmmap function. ])])
# fbsd-nat.c can also use kinfo_getfile.
AC_SEARCH_LIBS(kinfo_getfile, util util-freebsd,
[AC_DEFINE(HAVE_KINFO_GETFILE, 1,
[Define to 1 if your system has the kinfo_getfile function. ])])
AM_ICONV
# GDB may fork/exec the iconv program to get the list of supported character

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;