From patchwork Fri Dec 22 22:05:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Baldwin X-Patchwork-Id: 25083 Received: (qmail 83473 invoked by alias); 22 Dec 2017 22:05:50 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 83305 invoked by uid 89); 22 Dec 2017 22:05:50 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.2 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_HELO_PASS, SPF_SOFTFAIL autolearn=ham version=3.3.2 spammy=18s, sleeping, 8286, 9759 X-Spam-User: qpsmtpd, 2 recipients X-HELO: mail.baldwin.cx Received: from bigwig.baldwin.cx (HELO mail.baldwin.cx) (96.47.65.170) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 22 Dec 2017 22:05:46 +0000 Received: from ralph.baldwin.cx.com (astound-66-234-202-155.ca.astound.net [66.234.202.155]) by mail.baldwin.cx (Postfix) with ESMTPSA id 861DD10A8C2; Fri, 22 Dec 2017 17:05:44 -0500 (EST) From: John Baldwin To: gdb-patches@sourceware.org, binutils@sourceware.org Subject: [PATCH 3/4] Support 'info proc' for native FreeBSD processes. Date: Fri, 22 Dec 2017 14:05:12 -0800 Message-Id: <20171222220513.54983-4-jhb@FreeBSD.org> In-Reply-To: <20171222220513.54983-1-jhb@FreeBSD.org> References: <20171222220513.54983-1-jhb@FreeBSD.org> X-IsSubscribed: yes - Command line arguments are fetched via the kern.proc.args. 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. - 'stat' and 'status' output is generated by outputting fields from the structure returned by the kern.proc.pid. sysctl. 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 | 14 ++ gdb/config.in | 3 + gdb/configure | 60 +++++++++ gdb/configure.ac | 5 + gdb/fbsd-nat.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 462 insertions(+), 24 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2311749de7..3d580c1c9e 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,17 @@ +2017-12-22 John Baldwin + + * 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". + 2017-12-22 John Baldwin * fbsd-tdep.c (KVE_STRUCTSIZE, KVE_START, KVE_END, KVE_OFFSET) diff --git a/gdb/config.in b/gdb/config.in index 1d11a97080..ad2cc1754e 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -219,6 +219,9 @@ /* Define to 1 if you have the 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 diff --git a/gdb/configure b/gdb/configure index 7b250079de..a0baa7a53a 100755 --- a/gdb/configure +++ b/gdb/configure @@ -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" diff --git a/gdb/configure.ac b/gdb/configure.ac index 8e706b6e27..96ec77e0af 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -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 diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c index 29b7ee5e33..e460c23d95 100644 --- a/gdb/fbsd-nat.c +++ b/gdb/fbsd-nat.c @@ -32,14 +32,16 @@ #include #include #include -#ifdef HAVE_KINFO_GETVMMAP +#if defined(HAVE_KINFO_GETFILE) || defined(HAVE_KINFO_GETVMMAP) #include -#else +#endif +#if !defined(HAVE_KINFO_GETVMMAP) #include "filestuff.h" #endif #include "elf-bfd.h" #include "fbsd-nat.h" +#include "fbsd-tdep.h" #include @@ -77,7 +79,7 @@ fbsd_pid_to_exec_file (struct target_ops *self, int pid) return NULL; } -#ifdef HAVE_KINFO_GETVMMAP +#if defined(HAVE_KINFO_GETFILE) || defined(HAVE_KINFO_GETVMMAP) /* Deleter for std::unique_ptr that invokes free. */ template @@ -85,7 +87,9 @@ struct free_deleter { void operator() (T *ptr) const { free (ptr); } }; +#endif +#ifdef HAVE_KINFO_GETVMMAP /* Iterate over all the memory regions in the current inferior, calling FUNC for each memory region. OBFD is passed as the last argument to FUNC. */ @@ -210,6 +214,369 @@ fbsd_find_memory_regions (struct target_ops *self, } #endif +/* Fetch the command line for a running process. */ + +static gdb::unique_xmalloc_ptr +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; + + gdb::unique_xmalloc_ptr 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 + std::unique_ptr> 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; + bool do_stat = false; + bool kp_valid = 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: + do_status = true; + break; + case IP_STAT: + do_stat = 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; + do_stat = 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 + { + pid = strtol (built_argv[0], NULL, 10); + } + + printf_filtered (_("process %d\n"), pid); +#ifdef HAVE_KINFO_GETFILE + if (do_cwd || do_exe) + fdtbl.reset (kinfo_getfile (pid, &nfd)); +#endif + if (do_stat || do_status) + { + kp_valid = fbsd_fetch_kinfo_proc(pid, &kp); + if (!kp_valid) + warning (_("Failed to fetch process information")); + } + + if (do_cmdline) + { + gdb::unique_xmalloc_ptr cmdline = fbsd_fetch_cmdline (pid); + if (cmdline) + 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) + 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) + printf_filtered ("exe = '%s'\n", exe); + else + warning (_("unable to fetch executable path name")); + } +#ifdef HAVE_KINFO_GETVMMAP + if (do_mappings) + { + int nvment; + std::unique_ptr> + vmentl (kinfo_getvmmap (pid, &nvment)); + + if (vmentl) + { + 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 && kp_valid) + { + const char *state; + int pgtok; + + printf_filtered ("Name:\t%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:\t%s\n", state); + printf_filtered ("Pid:\t%d\n", kp.ki_pid); + printf_filtered ("PPid:\t%d\n", kp.ki_ppid); + printf_filtered ("Uid:\t%d %d %d\n", kp.ki_ruid, kp.ki_uid, kp.ki_svuid); + printf_filtered ("Gid:\t%d %d %d\n", kp.ki_rgid, kp.ki_groups[0], + kp.ki_svgid); + printf_filtered ("Groups:\t"); + for (int i = 0; i < kp.ki_ngroups; i++) + printf_filtered ("%d ", kp.ki_groups[i]); + printf_filtered ("\n"); + pgtok = getpagesize () / 1024; + printf_filtered ("VmSize:\t%8ju kB\n", (uintmax_t) kp.ki_size / 1024); + printf_filtered ("VmRSS:\t%8ju kB\n", (uintmax_t) kp.ki_rssize * pgtok); + printf_filtered ("VmData:\t%8ju kB\n", (uintmax_t) kp.ki_dsize * pgtok); + printf_filtered ("VmStk:\t%8ju kB\n", (uintmax_t) kp.ki_ssize * pgtok); + printf_filtered ("VmExe:\t%8ju kB\n", (uintmax_t) kp.ki_tsize * pgtok); + printf_filtered ("SigPnd:\t"); + for (int i = 0; i < _SIG_WORDS; i++) + printf_filtered ("%08x ", kp.ki_siglist.__bits[i]); + printf_filtered ("\n"); + printf_filtered ("SigIgn:\t"); + for (int i = 0; i < _SIG_WORDS; i++) + printf_filtered ("%08x ", kp.ki_sigignore.__bits[i]); + printf_filtered ("\n"); + printf_filtered ("SigCgt:\t"); + for (int i = 0; i < _SIG_WORDS; i++) + printf_filtered ("%08x ", kp.ki_sigcatch.__bits[i]); + printf_filtered ("\n"); + } + if (do_stat && kp_valid) + { + char state; + int pgtok; + + printf_filtered ("Exec file: %s\n", kp.ki_comm); + switch (kp.ki_stat) + { + case SIDL: + state = 'I'; + break; + case SRUN: + state = 'R'; + break; + case SSTOP: + state = 'T'; + break; + case SZOMB: + state = 'Z'; + break; + case SSLEEP: + state = 'S'; + break; + case SWAIT: + state = 'W'; + break; + case SLOCK: + state = 'L'; + break; + default: + state = '?'; + break; + } + printf_filtered ("State: %c\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 ("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 ("Resident set size: %ju kB\n", + (uintmax_t) kp.ki_rssize * pgtok); + printf_filtered ("rlim: %ju kB\n", (uintmax_t) kp.ki_rusage.ru_maxrss); + } +} + #ifdef KERN_PROC_AUXV static enum target_xfer_status (*super_xfer_partial) (struct target_ops *ops, enum target_object object, @@ -461,23 +828,6 @@ show_fbsd_lwp_debug (struct ui_file *file, int from_tty, } #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 /* @@ -565,7 +915,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) @@ -975,9 +1326,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; @@ -1181,6 +1536,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;