gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gdb / nbsd-nat.c
index 1bb35f82c1853cd9f6e37eceb6c0f10f6341d27c..254a768eb370a3116df4c3b530f6a02d4f30a738 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 #include <sys/ptrace.h>
 #include <sys/sysctl.h>
+#include <sys/wait.h>
 
 /* Return the name of a file that can be opened to get the symbols for
    the child process identified by PID.  */
@@ -58,6 +59,46 @@ nbsd_pid_to_cwd (int pid)
   return buf;
 }
 
+/* Return the kinfo_proc2 structure for the process identified by PID.  */
+
+static bool
+nbsd_pid_to_kinfo_proc2 (pid_t pid, struct kinfo_proc2 *kp)
+{
+  gdb_assert (kp != nullptr);
+
+  size_t size = sizeof (*kp);
+  int mib[6] = {CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid,
+               static_cast<int> (size), 1};
+  return !sysctl (mib, ARRAY_SIZE (mib), kp, &size, NULL, 0);
+}
+
+/* Return the command line for the process identified by PID.  */
+
+static gdb::unique_xmalloc_ptr<char[]>
+nbsd_pid_to_cmdline (int pid)
+{
+  int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
+
+  size_t size = 0;
+  if (sysctl (mib, ARRAY_SIZE (mib), NULL, &size, NULL, 0) == -1 || size == 0)
+    return nullptr;
+
+  gdb::unique_xmalloc_ptr<char[]> args (XNEWVAR (char, size));
+
+  if (sysctl (mib, ARRAY_SIZE (mib), args.get (), &size, NULL, 0) == -1
+      || size == 0)
+    return nullptr;
+
+  /* Arguments are returned as a flattened string with NUL separators.
+     Join the arguments with spaces to form a single string.  */
+  for (size_t i = 0; i < size - 1; i++)
+    if (args[i] == '\0')
+      args[i] = ' ';
+  args[size - 1] = '\0';
+
+  return args;
+}
+
 /* Generic thread (LWP) lister within a specified process.  The callback
    parameters is a C++ function that is called for each detected thread.  */
 
@@ -181,11 +222,37 @@ nbsd_add_threads (nbsd_nat_target *target, pid_t pid)
   nbsd_thread_lister (pid, fn);
 }
 
+/* Enable additional event reporting on new processes.  */
+
+static void
+nbsd_enable_proc_events (pid_t pid)
+{
+  int events;
+
+  if (ptrace (PT_GET_EVENT_MASK, pid, &events, sizeof (events)) == -1)
+    perror_with_name (("ptrace"));
+
+  events |= PTRACE_LWP_CREATE;
+  events |= PTRACE_LWP_EXIT;
+
+  if (ptrace (PT_SET_EVENT_MASK, pid, &events, sizeof (events)) == -1)
+    perror_with_name (("ptrace"));
+}
+
+/* Implement the "post_startup_inferior" target_ops method.  */
+
+void
+nbsd_nat_target::post_startup_inferior (ptid_t ptid)
+{
+  nbsd_enable_proc_events (ptid.pid ());
+}
+
 /* Implement the "post_attach" target_ops method.  */
 
 void
 nbsd_nat_target::post_attach (int pid)
 {
+  nbsd_enable_proc_events (pid);
   nbsd_add_threads (this, pid);
 }
 
@@ -194,9 +261,7 @@ nbsd_nat_target::post_attach (int pid)
 void
 nbsd_nat_target::update_thread_list ()
 {
-  prune_threads ();
-
-  nbsd_add_threads (this, inferior_ptid.pid ());
+  delete_exited_threads ();
 }
 
 /* Convert PTID to a string.  */
@@ -313,21 +378,42 @@ bool
 nbsd_nat_target::info_proc (const char *args, enum info_proc_what what)
 {
   pid_t pid;
+  bool do_cmdline = false;
   bool do_cwd = false;
   bool do_exe = false;
   bool do_mappings = false;
+  bool do_status = false;
 
   switch (what)
     {
+    case IP_MINIMAL:
+      do_cmdline = true;
+      do_cwd = true;
+      do_exe = true;
+      break;
+    case IP_STAT:
+    case IP_STATUS:
+      do_status = true;
+      break;
     case IP_MAPPINGS:
       do_mappings = 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;
+      do_mappings = true;
+      do_status = true;
+      break;
     default:
       error (_("Not supported on this target."));
     }
@@ -346,6 +432,14 @@ nbsd_nat_target::info_proc (const char *args, enum info_proc_what what)
 
   printf_filtered (_("process %d\n"), pid);
 
+  if (do_cmdline)
+    {
+      gdb::unique_xmalloc_ptr<char[]> cmdline = nbsd_pid_to_cmdline (pid);
+      if (cmdline != nullptr)
+       printf_filtered ("cmdline = '%s'\n", cmdline.get ());
+      else
+       warning (_("unable to fetch command line"));
+    }
   if (do_cwd)
     {
       std::string cwd = nbsd_pid_to_cwd (pid);
@@ -383,6 +477,363 @@ nbsd_nat_target::info_proc (const char *args, enum info_proc_what what)
       else
        warning (_("unable to fetch virtual memory map"));
     }
+  if (do_status)
+    {
+      struct kinfo_proc2 kp;
+      if (!nbsd_pid_to_kinfo_proc2 (pid, &kp))
+       warning (_("Failed to fetch process information"));
+      else
+       {
+         auto process_status
+           = [] (int8_t stat)
+             {
+               switch (stat)
+                 {
+                 case SIDL:
+                   return "IDL";
+                 case SACTIVE:
+                   return "ACTIVE";
+                 case SDYING:
+                   return "DYING";
+                 case SSTOP:
+                   return "STOP";
+                 case SZOMB:
+                   return "ZOMB";
+                 case SDEAD:
+                   return "DEAD";
+                 default:
+                   return "? (unknown)";
+                 }
+             };
+
+         printf_filtered ("Name: %s\n", kp.p_comm);
+         printf_filtered ("State: %s\n", process_status(kp.p_realstat));
+         printf_filtered ("Parent process: %" PRId32 "\n", kp.p_ppid);
+         printf_filtered ("Process group: %" PRId32 "\n", kp.p__pgid);
+         printf_filtered ("Session id: %" PRId32 "\n", kp.p_sid);
+         printf_filtered ("TTY: %" PRId32 "\n", kp.p_tdev);
+         printf_filtered ("TTY owner process group: %" PRId32 "\n", kp.p_tpgid);
+         printf_filtered ("User IDs (real, effective, saved): "
+                          "%" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
+                          kp.p_ruid, kp.p_uid, kp.p_svuid);
+         printf_filtered ("Group IDs (real, effective, saved): "
+                          "%" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
+                          kp.p_rgid, kp.p_gid, kp.p_svgid);
+
+         printf_filtered ("Groups:");
+         for (int i = 0; i < kp.p_ngroups; i++)
+           printf_filtered (" %" PRIu32, kp.p_groups[i]);
+         printf_filtered ("\n");
+         printf_filtered ("Minor faults (no memory page): %" PRIu64 "\n",
+                          kp.p_uru_minflt);
+         printf_filtered ("Major faults (memory page faults): %" PRIu64 "\n",
+                          kp.p_uru_majflt);
+         printf_filtered ("utime: %" PRIu32 ".%06" PRIu32 "\n",
+                          kp.p_uutime_sec, kp.p_uutime_usec);
+         printf_filtered ("stime: %" PRIu32 ".%06" PRIu32 "\n",
+                          kp.p_ustime_sec, kp.p_ustime_usec);
+         printf_filtered ("utime+stime, children: %" PRIu32 ".%06" PRIu32 "\n",
+                          kp.p_uctime_sec, kp.p_uctime_usec);
+         printf_filtered ("'nice' value: %" PRIu8 "\n", kp.p_nice);
+         printf_filtered ("Start time: %" PRIu32 ".%06" PRIu32 "\n",
+                          kp.p_ustart_sec, kp.p_ustart_usec);
+         int pgtok = getpagesize () / 1024;
+         printf_filtered ("Data size: %" PRIuMAX " kB\n",
+                          (uintmax_t) kp.p_vm_dsize * pgtok);
+         printf_filtered ("Stack size: %" PRIuMAX " kB\n",
+                          (uintmax_t) kp.p_vm_ssize * pgtok);
+         printf_filtered ("Text size: %" PRIuMAX " kB\n",
+                          (uintmax_t) kp.p_vm_tsize * pgtok);
+         printf_filtered ("Resident set size: %" PRIuMAX " kB\n",
+                          (uintmax_t) kp.p_vm_rssize * pgtok);
+         printf_filtered ("Maximum RSS: %" PRIu64 " kB\n", kp.p_uru_maxrss);
+         printf_filtered ("Pending Signals:");
+         for (size_t i = 0; i < ARRAY_SIZE (kp.p_siglist.__bits); i++)
+           printf_filtered (" %08" PRIx32, kp.p_siglist.__bits[i]);
+         printf_filtered ("\n");
+         printf_filtered ("Ignored Signals:");
+         for (size_t i = 0; i < ARRAY_SIZE (kp.p_sigignore.__bits); i++)
+           printf_filtered (" %08" PRIx32, kp.p_sigignore.__bits[i]);
+         printf_filtered ("\n");
+         printf_filtered ("Caught Signals:");
+         for (size_t i = 0; i < ARRAY_SIZE (kp.p_sigcatch.__bits); i++)
+           printf_filtered (" %08" PRIx32, kp.p_sigcatch.__bits[i]);
+         printf_filtered ("\n");
+       }
+    }
 
   return true;
 }
+
+/* Resume execution of a specified PTID, that points to a process or a thread
+   within a process.  If one thread is specified, all other threads are
+   suspended.  If STEP is nonzero, single-step it.  If SIGNAL is nonzero,
+   give it that signal.  */
+
+static void
+nbsd_resume(nbsd_nat_target *target, ptid_t ptid, int step,
+           enum gdb_signal signal)
+{
+  int request;
+
+  gdb_assert (minus_one_ptid != ptid);
+
+  if (ptid.lwp_p ())
+    {
+      /* If ptid is a specific LWP, suspend all other LWPs in the process.  */
+      inferior *inf = find_inferior_ptid (target, ptid);
+
+      for (thread_info *tp : inf->non_exited_threads ())
+        {
+          if (tp->ptid.lwp () == ptid.lwp ())
+            request = PT_RESUME;
+          else
+            request = PT_SUSPEND;
+
+          if (ptrace (request, tp->ptid.pid (), NULL, tp->ptid.lwp ()) == -1)
+            perror_with_name (("ptrace"));
+        }
+    }
+  else
+    {
+      /* If ptid is a wildcard, resume all matching threads (they won't run
+         until the process is continued however).  */
+      for (thread_info *tp : all_non_exited_threads (target, ptid))
+        if (ptrace (PT_RESUME, tp->ptid.pid (), NULL, tp->ptid.lwp ()) == -1)
+          perror_with_name (("ptrace"));
+    }
+
+  if (step)
+    {
+      for (thread_info *tp : all_non_exited_threads (target, ptid))
+       if (ptrace (PT_SETSTEP, tp->ptid.pid (), NULL, tp->ptid.lwp ()) == -1)
+         perror_with_name (("ptrace"));
+    }
+  else
+    {
+      for (thread_info *tp : all_non_exited_threads (target, ptid))
+       if (ptrace (PT_CLEARSTEP, tp->ptid.pid (), NULL, tp->ptid.lwp ()) == -1)
+         perror_with_name (("ptrace"));
+    }
+
+  if (catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
+  /* An address of (void *)1 tells ptrace to continue from
+     where it was.  If GDB wanted it to start some other way, we have
+     already written a new program counter value to the child.  */
+  if (ptrace (request, ptid.pid (), (void *)1, gdb_signal_to_host (signal)) == -1)
+    perror_with_name (("ptrace"));
+}
+
+/* Resume execution of thread PTID, or all threads of all inferiors
+   if PTID is -1.  If STEP is nonzero, single-step it.  If SIGNAL is nonzero,
+   give it that signal.  */
+
+void
+nbsd_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
+{
+  if (minus_one_ptid != ptid)
+    nbsd_resume (this, ptid, step, signal);
+  else
+    {
+      for (inferior *inf : all_non_exited_inferiors (this))
+       nbsd_resume (this, ptid_t (inf->pid, 0, 0), step, signal);
+    }
+}
+
+/* Implement a safe wrapper around waitpid().  */
+
+static pid_t
+nbsd_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
+{
+  pid_t pid;
+  int status;
+
+  set_sigint_trap ();
+
+  do
+    {
+      /* The common code passes WNOHANG that leads to crashes, overwrite it.  */
+      pid = waitpid (ptid.pid (), &status, 0);
+    }
+  while (pid == -1 && errno == EINTR);
+
+  clear_sigint_trap ();
+
+  if (pid == -1)
+    perror_with_name (_("Child process unexpectedly missing"));
+
+  store_waitstatus (ourstatus, status);
+  return pid;
+}
+
+/* Wait for the child specified by PTID to do something.  Return the
+   process ID of the child, or MINUS_ONE_PTID in case of error; store
+   the status in *OURSTATUS.  */
+
+ptid_t
+nbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+                      int target_options)
+{
+  pid_t pid = nbsd_wait (ptid, ourstatus, target_options);
+  ptid_t wptid = ptid_t (pid);
+
+  /* If the child stopped, keep investigating its status.  */
+  if (ourstatus->kind != TARGET_WAITKIND_STOPPED)
+    return wptid;
+
+  /* Extract the event and thread that received a signal.  */
+  ptrace_siginfo_t psi;
+  if (ptrace (PT_GET_SIGINFO, pid, &psi, sizeof (psi)) == -1)
+    perror_with_name (("ptrace"));
+
+  /* Pick child's siginfo_t.  */
+  siginfo_t *si = &psi.psi_siginfo;
+
+  int lwp = psi.psi_lwpid;
+
+  int signo = si->si_signo;
+  const int code = si->si_code;
+
+  /* Construct PTID with a specified thread that received the event.
+     If a signal was targeted to the whole process, lwp is 0.  */
+  wptid = ptid_t (pid, lwp, 0);
+
+  /* Bail out on non-debugger oriented signals..  */
+  if (signo != SIGTRAP)
+    return wptid;
+
+  /* Stop examining non-debugger oriented SIGTRAP codes.  */
+  if (code <= SI_USER || code == SI_NOINFO)
+    return wptid;
+
+  /* Process state for threading events */
+  ptrace_state_t pst = {};
+  if (code == TRAP_LWP)
+    {
+      if (ptrace (PT_GET_PROCESS_STATE, pid, &pst, sizeof (pst)) == -1)
+       perror_with_name (("ptrace"));
+    }
+
+  if (code == TRAP_LWP && pst.pe_report_event == PTRACE_LWP_EXIT)
+    {
+      /* If GDB attaches to a multi-threaded process, exiting
+        threads might be skipped during post_attach that
+        have not yet reported their PTRACE_LWP_EXIT event.
+        Ignore exited events for an unknown LWP.  */
+      thread_info *thr = find_thread_ptid (this, wptid);
+      if (thr == nullptr)
+         ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+      else
+       {
+         ourstatus->kind = TARGET_WAITKIND_THREAD_EXITED;
+         /* NetBSD does not store an LWP exit status.  */
+         ourstatus->value.integer = 0;
+
+         if (print_thread_events)
+           printf_unfiltered (_("[%s exited]\n"),
+                              target_pid_to_str (wptid).c_str ());
+         delete_thread (thr);
+       }
+
+      /* The GDB core expects that the rest of the threads are running.  */
+      if (ptrace (PT_CONTINUE, pid, (void *) 1, 0) == -1)
+       perror_with_name (("ptrace"));
+
+      return wptid;
+    }
+
+  if (in_thread_list (this, ptid_t (pid)))
+      thread_change_ptid (this, ptid_t (pid), wptid);
+
+  if (code == TRAP_LWP && pst.pe_report_event == PTRACE_LWP_CREATE)
+    {
+      /* If GDB attaches to a multi-threaded process, newborn
+        threads might be added by nbsd_add_threads that have
+        not yet reported their PTRACE_LWP_CREATE event.  Ignore
+        born events for an already-known LWP.  */
+      if (in_thread_list (this, wptid))
+         ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+      else
+       {
+         add_thread (this, wptid);
+         ourstatus->kind = TARGET_WAITKIND_THREAD_CREATED;
+       }
+      return wptid;
+    }
+
+  if (code == TRAP_EXEC)
+    {
+      ourstatus->kind = TARGET_WAITKIND_EXECD;
+      ourstatus->value.execd_pathname = xstrdup (pid_to_exec_file (pid));
+      return wptid;
+    }
+
+  if (code == TRAP_TRACE)
+    {
+      /* Unhandled at this level.  */
+      return wptid;
+    }
+
+  if (code == TRAP_SCE || code == TRAP_SCX)
+    {
+      int sysnum = si->si_sysnum;
+
+      if (!catch_syscall_enabled () || !catching_syscall_number (sysnum))
+       {
+         /* If the core isn't interested in this event, ignore it.  */
+         ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+         return wptid;
+       }
+
+      ourstatus->kind =
+       (code == TRAP_SCE) ? TARGET_WAITKIND_SYSCALL_ENTRY :
+       TARGET_WAITKIND_SYSCALL_RETURN;
+      ourstatus->value.syscall_number = sysnum;
+      return wptid;
+    }
+
+  if (code == TRAP_BRKPT)
+    {
+      /* Unhandled at this level.  */
+      return wptid;
+    }
+
+  /* Unclassified SIGTRAP event.  */
+  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+  return wptid;
+}
+
+/* Implement the "insert_exec_catchpoint" target_ops method.  */
+
+int
+nbsd_nat_target::insert_exec_catchpoint (int pid)
+{
+  /* Nothing to do.  */
+  return 0;
+}
+
+/* Implement the "remove_exec_catchpoint" target_ops method.  */
+
+int
+nbsd_nat_target::remove_exec_catchpoint (int pid)
+{
+  /* Nothing to do.  */
+  return 0;
+}
+
+/* Implement the "set_syscall_catchpoint" target_ops method.  */
+
+int
+nbsd_nat_target::set_syscall_catchpoint (int pid, bool needed,
+                                         int any_count,
+                                         gdb::array_view<const int> syscall_counts)
+{
+  /* Ignore the arguments.  inf-ptrace.c will use PT_SYSCALL which
+     will catch all system call entries and exits.  The system calls
+     are filtered by GDB rather than the kernel.  */
+  return 0;
+}
This page took 0.02907 seconds and 4 git commands to generate.