Support 'info proc' for native FreeBSD processes.
authorJohn Baldwin <jhb@FreeBSD.org>
Tue, 9 Jan 2018 21:35:17 +0000 (13:35 -0800)
committerJohn Baldwin <jhb@FreeBSD.org>
Tue, 9 Jan 2018 21:35:17 +0000 (13:35 -0800)
- 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".

gdb/ChangeLog
gdb/config.in
gdb/configure
gdb/configure.ac
gdb/fbsd-nat.c

index a55b21f3335273e1fdc84206065293962c44afdb..c29a6ac6cd4d67612dd4961b431c6a50037984d6 100644 (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.
index 1d11a97080f6d793c52a43aeb8899c72704fcc4c..ad2cc1754edea3eecb4550f0b2a30190d97dbec5 100644 (file)
 /* 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
 
index db610f32fc26ccb043d0b50389e9ee120bbccdce..68b9aad02d27ea30bf96300ee421592246a89d50 100755 (executable)
@@ -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"
index 3db44ae758eaed61d39be9263c3406debf3d41e7..551afc727e7ad8c38c2934e34c30b9beebd2c245 100644 (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
index 81f8e27a2dd71935279839f5275af8fb71f07025..b3524188137f9bb2264e829693aca1ab026a356a 100644 (file)
 #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;