From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 128751 invoked by alias); 27 Dec 2017 02:23:03 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 128724 invoked by uid 89); 27 Dec 2017 02:23:03 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_HELO_PASS,SPF_PASS,T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy= X-Spam-User: qpsmtpd, 2 recipients X-HELO: simark.ca Received: from simark.ca (HELO simark.ca) (158.69.221.121) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 27 Dec 2017 02:22:54 +0000 Received: from [10.0.0.11] (192-222-251-162.qc.cable.ebox.net [192.222.251.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by simark.ca (Postfix) with ESMTPSA id B6E691E519; Tue, 26 Dec 2017 21:22:52 -0500 (EST) Subject: Re: [PATCH 3/4] Support 'info proc' for native FreeBSD processes. To: John Baldwin , gdb-patches@sourceware.org, binutils@sourceware.org References: <20171222220513.54983-1-jhb@FreeBSD.org> <20171222220513.54983-4-jhb@FreeBSD.org> From: Simon Marchi Message-ID: Date: Wed, 27 Dec 2017 02:23:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.5.0 MIME-Version: 1.0 In-Reply-To: <20171222220513.54983-4-jhb@FreeBSD.org> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-SW-Source: 2017-12/txt/msg00508.txt.bz2 On 2017-12-22 05:05 PM, John Baldwin wrote: > - 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); > + } Unnecessary curly braces. > + > + 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> Is there a reason to have and use free_deleter rather than gdb::unique_xmalloc_ptr? > + vmentl (kinfo_getvmmap (pid, &nvment)); > + > + if (vmentl) vmentl != NULL There are a few other instances of if (ptr) that should be changed to if (ptr != NULL). > + { > + 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 This leaves an empty #if/#endif. Should it be removed or moved with the function? > > /* > @@ -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; > Thanks, Simon