[Lldb-commits] [lldb] 134d7f9 - Store a by name list of signals with their actions in the Target

Jim Ingham via lldb-commits lldb-commits at lists.llvm.org
Thu May 26 14:50:42 PDT 2022


Author: Jim Ingham
Date: 2022-05-26T14:50:33-07:00
New Revision: 134d7f9a4b97e9035150d970bd9e376043c4577e

URL: https://github.com/llvm/llvm-project/commit/134d7f9a4b97e9035150d970bd9e376043c4577e
DIFF: https://github.com/llvm/llvm-project/commit/134d7f9a4b97e9035150d970bd9e376043c4577e.diff

LOG: Store a by name list of signals with their actions in the Target
so that they can be used to prime new Process runs.  "process handle"
was also changed to populate the dummy target if there's no selected
target, so that the settings will get copied into new targets.

Differential Revision: https://reviews.llvm.org/D126259

Added: 
    lldb/test/API/commands/process/handle/Makefile
    lldb/test/API/commands/process/handle/TestProcessHandle.py
    lldb/test/API/commands/process/handle/main.cpp

Modified: 
    lldb/include/lldb/Target/Target.h
    lldb/include/lldb/Target/UnixSignals.h
    lldb/packages/Python/lldbsuite/test/lldbutil.py
    lldb/source/Commands/CommandObjectProcess.cpp
    lldb/source/Commands/Options.td
    lldb/source/Target/Process.cpp
    lldb/source/Target/Target.cpp
    lldb/source/Target/UnixSignals.cpp
    lldb/test/API/functionalities/signal/raise/TestRaise.py
    lldb/unittests/Signals/UnixSignalsTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index d6193ea7166f3..fb2914064ce45 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1414,6 +1414,42 @@ class Target : public std::enable_shared_from_this<Target>,
     return *m_frame_recognizer_manager_up;
   }
 
+  /// Add a signal for the target.  This will get copied over to the process
+  /// if the signal exists on that target.  Only the values with Yes and No are
+  /// set, Calculate values will be ignored.
+protected:
+  struct DummySignalValues {
+    LazyBool pass = eLazyBoolCalculate;
+    LazyBool notify = eLazyBoolCalculate;
+    LazyBool stop = eLazyBoolCalculate;
+    DummySignalValues(LazyBool pass, LazyBool notify, LazyBool stop) : 
+        pass(pass), notify(notify), stop(stop) {}
+    DummySignalValues() = default;
+  };
+  using DummySignalElement = llvm::StringMapEntry<DummySignalValues>;
+  static bool UpdateSignalFromDummy(lldb::UnixSignalsSP signals_sp, 
+      const DummySignalElement &element);
+  static bool ResetSignalFromDummy(lldb::UnixSignalsSP signals_sp, 
+      const DummySignalElement &element);
+
+public:
+  /// Add a signal to the Target's list of stored signals/actions.  These
+  /// values will get copied into any processes launched from
+  /// this target.
+  void AddDummySignal(llvm::StringRef name, LazyBool pass, LazyBool print, 
+                      LazyBool stop);
+  /// Updates the signals in signals_sp using the stored dummy signals.
+  /// If warning_stream_sp is not null, if any stored signals are not found in
+  /// the current process, a warning will be emitted here.
+  void UpdateSignalsFromDummy(lldb::UnixSignalsSP signals_sp, 
+                              lldb::StreamSP warning_stream_sp);
+  /// Clear the dummy signals in signal_names from the target, or all signals
+  /// if signal_names is empty.  Also remove the behaviors they set from the
+  /// process's signals if it exists. 
+  void ClearDummySignals(Args &signal_names);
+  /// Print all the signals set in this target.
+  void PrintDummySignals(Stream &strm, Args &signals);
+
 protected:
   /// Implementing of ModuleList::Notifier.
 
@@ -1443,6 +1479,7 @@ class Target : public std::enable_shared_from_this<Target>,
     ArchSpec m_spec;
     std::unique_ptr<Architecture> m_plugin_up;
   };
+
   // Member variables.
   Debugger &m_debugger;
   lldb::PlatformSP m_platform_sp; ///< The platform for this target.
@@ -1493,6 +1530,10 @@ class Target : public std::enable_shared_from_this<Target>,
   lldb::TraceSP m_trace_sp;
   /// Stores the frame recognizers of this target.
   lldb::StackFrameRecognizerManagerUP m_frame_recognizer_manager_up;
+  /// These are used to set the signal state when you don't have a process and 
+  /// more usefully in the Dummy target where you can't know exactly what
+  /// signals you will have.
+  llvm::StringMap<DummySignalValues> m_dummy_signals;
 
   static void ImageSearchPathsChanged(const PathMappingList &path_list,
                                       void *baton);

diff  --git a/lldb/include/lldb/Target/UnixSignals.h b/lldb/include/lldb/Target/UnixSignals.h
index 1c91c9fdd4895..34f4e30d13505 100644
--- a/lldb/include/lldb/Target/UnixSignals.h
+++ b/lldb/include/lldb/Target/UnixSignals.h
@@ -55,6 +55,9 @@ class UnixSignals {
   bool SetShouldNotify(int32_t signo, bool value);
 
   bool SetShouldNotify(const char *signal_name, bool value);
+  
+  bool ResetSignal(int32_t signo, bool reset_stop = true, 
+                   bool reset_notify = true, bool reset_suppress = true);
 
   // These provide an iterator through the signals available on this system.
   // Call GetFirstSignalNumber to get the first entry, then iterate on
@@ -114,11 +117,13 @@ class UnixSignals {
     std::string m_description;
     uint32_t m_hit_count = 0;
     bool m_suppress : 1, m_stop : 1, m_notify : 1;
+    bool m_default_suppress : 1, m_default_stop : 1, m_default_notify : 1;
 
     Signal(const char *name, bool default_suppress, bool default_stop,
            bool default_notify, const char *description, const char *alias);
 
     ~Signal() = default;
+    void Reset(bool reset_stop, bool reset_notify, bool reset_suppress);
   };
 
   virtual void Reset();

diff  --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py
index af2c41d1afa61..e3e81a7f59358 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbutil.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbutil.py
@@ -1527,6 +1527,42 @@ def get_signal_number(signal_name):
     # No remote platform; fall back to using local python signals.
     return getattr(signal, signal_name)
 
+def get_actions_for_signal(testcase, signal_name, from_target=False, expected_absent=False):
+    """Returns a triple of (pass, stop, notify)"""
+    return_obj = lldb.SBCommandReturnObject()
+    command = "process handle {0}".format(signal_name)
+    if from_target:
+        command += " -t"
+    testcase.dbg.GetCommandInterpreter().HandleCommand(
+        command, return_obj)
+    match = re.match(
+        'NAME *PASS *STOP *NOTIFY.*(false|true|not set) *(false|true|not set) *(false|true|not set)',
+        return_obj.GetOutput(),
+        re.IGNORECASE | re.DOTALL)
+    if match and expected_absent:
+        testcase.fail('Signal "{0}" was supposed to be absent'.format(signal_name))
+    if not match:
+        if expected_absent:
+            return (None, None, None)
+        testcase.fail('Unable to retrieve default signal disposition.')
+    return (match.group(1), match.group(2), match.group(3))
+
+
+
+def set_actions_for_signal(testcase, signal_name, pass_action, stop_action, notify_action, expect_success=True):
+        return_obj = lldb.SBCommandReturnObject()
+        command = "process handle {0}".format(signal_name)
+        if pass_action != None:
+            command += " -p {0}".format(pass_action)
+        if stop_action != None:
+            command += " -s {0}".format(stop_action)
+        if notify_action != None:
+            command +=" -n {0}".format(notify_action)
+            
+        testcase.dbg.GetCommandInterpreter().HandleCommand(command, return_obj)
+        testcase.assertEqual(expect_success,
+            return_obj.Succeeded(), 
+            "Setting signal handling for {0} worked as expected".format(signal_name))
 
 class PrintableRegex(object):
 

diff  --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 484ef6aa6036b..f5961d9b1c00d 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -1432,6 +1432,12 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
       const int short_option = m_getopt_table[option_idx].val;
 
       switch (short_option) {
+      case 'c':
+        do_clear = true;
+        break;
+      case 'd':
+        dummy = true;
+        break;
       case 's':
         stop = std::string(option_arg);
         break;
@@ -1441,6 +1447,9 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
       case 'p':
         pass = std::string(option_arg);
         break;
+      case 't':
+        only_target_values = true;
+        break;
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -1451,6 +1460,9 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
       stop.clear();
       notify.clear();
       pass.clear();
+      only_target_values = false;
+      do_clear = false;
+      dummy = false;
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -1462,6 +1474,9 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
     std::string stop;
     std::string notify;
     std::string pass;
+    bool only_target_values = false;
+    bool do_clear = false;
+    bool dummy = false;
   };
 
   CommandObjectProcessHandle(CommandInterpreter &interpreter)
@@ -1469,9 +1484,19 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
                             "Manage LLDB handling of OS signals for the "
                             "current target process.  Defaults to showing "
                             "current policy.",
-                            nullptr, eCommandRequiresTarget) {
-    SetHelpLong("\nIf no signals are specified, update them all.  If no update "
-                "option is specified, list the current values.");
+                            nullptr) {
+    SetHelpLong("\nIf no signals are specified but one or more actions are, "
+                "and there is a live process, update them all.  If no action "
+                "is specified, list the current values.\n"
+                "If you specify actions with no target (e.g. in an init file) "
+                "or in a target with no process "
+                "the values will get copied into subsequent targets, but "
+                "lldb won't be able to spell-check the options since it can't "
+                "know which signal set will later be in force."
+                "\nYou can see the signal modifications held by the target"
+                "by passing the -t option."
+                "\nYou can also clear the target modification for a signal"
+                "by passing the -c option");
     CommandArgumentEntry arg;
     CommandArgumentData signal_arg;
 
@@ -1554,15 +1579,13 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
 
 protected:
   bool DoExecute(Args &signal_args, CommandReturnObject &result) override {
-    Target *target_sp = &GetSelectedTarget();
+    Target &target = GetSelectedOrDummyTarget();
 
-    ProcessSP process_sp = target_sp->GetProcessSP();
-
-    if (!process_sp) {
-      result.AppendError("No current process; cannot handle signals until you "
-                         "have a valid process.\n");
-      return false;
-    }
+    // Any signals that are being set should be added to the Target's 
+    // DummySignals so they will get applied on rerun, etc.
+    // If we have a process, however, we can do a more accurate job of vetting
+    // the user's options.
+    ProcessSP process_sp = target.GetProcessSP();
 
     int stop_action = -1;   // -1 means leave the current setting alone
     int pass_action = -1;   // -1 means leave the current setting alone
@@ -1588,35 +1611,99 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
                          "true or false.\n");
       return false;
     }
+    
+    bool no_actions = (stop_action == -1 && pass_action == -1 
+        && notify_action == -1);
+    if (m_options.only_target_values && !no_actions) {
+      result.AppendError("-t is for reporting, not setting, target values.");
+      return false;
+    }
 
     size_t num_args = signal_args.GetArgumentCount();
-    UnixSignalsSP signals_sp = process_sp->GetUnixSignals();
+    UnixSignalsSP signals_sp;
+    if (process_sp)
+      signals_sp = process_sp->GetUnixSignals();
+
     int num_signals_set = 0;
 
+    // If we were just asked to print the target values, do that here and
+    // return:
+    if (m_options.only_target_values) {
+      target.PrintDummySignals(result.GetOutputStream(), signal_args);
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+      return true;
+    }
+
+    // This handles clearing values:
+    if (m_options.do_clear) {
+      target.ClearDummySignals(signal_args);
+      if (m_options.dummy)
+        GetDummyTarget().ClearDummySignals(signal_args);
+      result.SetStatus(eReturnStatusSuccessFinishNoResult);
+      return true;
+    }
+
+    // This rest handles setting values:
     if (num_args > 0) {
       for (const auto &arg : signal_args) {
-        int32_t signo = signals_sp->GetSignalNumberFromName(arg.c_str());
-        if (signo != LLDB_INVALID_SIGNAL_NUMBER) {
-          // Casting the actions as bools here should be okay, because
-          // VerifyCommandOptionValue guarantees the value is either 0 or 1.
-          if (stop_action != -1)
-            signals_sp->SetShouldStop(signo, stop_action);
-          if (pass_action != -1) {
-            bool suppress = !pass_action;
-            signals_sp->SetShouldSuppress(signo, suppress);
+        // Do the process first.  If we have a process we can catch
+        // invalid signal names, which we do here.
+        if (signals_sp) {
+          int32_t signo = signals_sp->GetSignalNumberFromName(arg.c_str());
+          if (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+            // Casting the actions as bools here should be okay, because
+            // VerifyCommandOptionValue guarantees the value is either 0 or 1.
+            if (stop_action != -1)
+              signals_sp->SetShouldStop(signo, stop_action);
+            if (pass_action != -1) {
+              bool suppress = !pass_action;
+              signals_sp->SetShouldSuppress(signo, suppress);
+            }
+            if (notify_action != -1)
+              signals_sp->SetShouldNotify(signo, notify_action);
+            ++num_signals_set;
+          } else {
+            result.AppendErrorWithFormat("Invalid signal name '%s'\n",
+                                          arg.c_str());
+            continue;
           }
-          if (notify_action != -1)
-            signals_sp->SetShouldNotify(signo, notify_action);
-          ++num_signals_set;
         } else {
-          result.AppendErrorWithFormat("Invalid signal name '%s'\n",
-                                       arg.c_str());
+          // If there's no process we can't check, so we just set them all.
+          // But since the map signal name -> signal number across all platforms
+          // is not 1-1, we can't sensibly set signal actions by number before
+          // we have a process.  Check that here:
+          int32_t signo;
+          if (llvm::to_integer(arg.c_str(), signo)) {
+            result.AppendErrorWithFormat("Can't set signal handling by signal "
+                                         "number with no process");
+            return false;
+          }
+         num_signals_set = num_args;
         }
+        auto set_lazy_bool = [] (int action) -> LazyBool {
+          LazyBool lazy;
+          if (action == -1) 
+            lazy = eLazyBoolCalculate;
+          else if (action) 
+            lazy = eLazyBoolYes;
+          else
+            lazy = eLazyBoolNo;
+          return lazy;
+        };
+
+        // If there were no actions, we're just listing, don't add the dummy:
+        if (!no_actions)
+          target.AddDummySignal(arg.ref(),
+                                set_lazy_bool(pass_action),
+                                set_lazy_bool(notify_action),
+                                set_lazy_bool(stop_action));
       }
     } else {
       // No signal specified, if any command options were specified, update ALL
-      // signals.
-      if ((notify_action != -1) || (stop_action != -1) || (pass_action != -1)) {
+      // signals.  But we can't do this without a process since we don't know
+      // all the possible signals that might be valid for this target.
+      if (((notify_action != -1) || (stop_action != -1) || (pass_action != -1))
+          && process_sp) {
         if (m_interpreter.Confirm(
                 "Do you really want to update all the signals?", false)) {
           int32_t signo = signals_sp->GetFirstSignalNumber();
@@ -1635,11 +1722,15 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
       }
     }
 
-    PrintSignalInformation(result.GetOutputStream(), signal_args,
-                           num_signals_set, signals_sp);
+    if (signals_sp)
+      PrintSignalInformation(result.GetOutputStream(), signal_args,
+                             num_signals_set, signals_sp);
+    else
+      target.PrintDummySignals(result.GetOutputStream(), 
+          signal_args);
 
     if (num_signals_set > 0)
-      result.SetStatus(eReturnStatusSuccessFinishNoResult);
+      result.SetStatus(eReturnStatusSuccessFinishResult);
     else
       result.SetStatus(eReturnStatusFailed);
 

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index c326f8a320748..5a1c4bcd2f3a5 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -742,6 +742,8 @@ let Command = "process load" in {
 }
 
 let Command = "process handle" in {
+  def process_handle_clear : Option<"clear", "c">, Group<2>,
+    Desc<"Removes the signals listed from the Target signal handlers">;
   def process_handle_stop : Option<"stop", "s">, Group<1>, Arg<"Boolean">,
     Desc<"Whether or not the process should be stopped if the signal is "
     "received.">;
@@ -750,6 +752,10 @@ let Command = "process handle" in {
     "received.">;
   def process_handle_pass : Option<"pass", "p">, Group<1>, Arg<"Boolean">,
     Desc<"Whether or not the signal should be passed to the process.">;
+  def process_handle_only_target : Option<"target", "t">, Group<1>,
+    Desc<"Show only the signals with behaviors modified in this target">;
+  def process_handle_dummy : Option<"dummy", "d">, Group<2>,
+    Desc<"Also clear the values in the dummy target so they won't be inherited by new targets.">;
 }
 
 let Command = "process status" in {

diff  --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 1f62e95377f4d..0467066376913 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2566,6 +2566,13 @@ Status Process::LaunchPrivate(ProcessLaunchInfo &launch_info, StateType &state,
 
   if (state == eStateStopped || state == eStateCrashed) {
     DidLaunch();
+    
+    // Now that we know the process type, update its signal responses from the 
+    // ones stored in the Target:
+    if (m_unix_signals_sp) {
+      StreamSP warning_strm = GetTarget().GetDebugger().GetAsyncErrorStream();
+      GetTarget().UpdateSignalsFromDummy(m_unix_signals_sp, warning_strm);
+    }
 
     DynamicLoader *dyld = GetDynamicLoader();
     if (dyld)
@@ -2928,6 +2935,12 @@ void Process::CompleteAttach() {
       }
     }
   }
+  // Now that we know the process type, update its signal responses from the 
+  // ones stored in the Target:
+  if (m_unix_signals_sp) {
+    StreamSP warning_strm = GetTarget().GetDebugger().GetAsyncErrorStream();
+    GetTarget().UpdateSignalsFromDummy(m_unix_signals_sp, warning_strm);
+  }
 
   // We have completed the attach, now it is time to find the dynamic loader
   // plug-in

diff  --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 993e7efd02ba7..ca9754d40af07 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -51,6 +51,7 @@
 #include "lldb/Target/SystemRuntime.h"
 #include "lldb/Target/Thread.h"
 #include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/Event.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/LLDBAssert.h"
@@ -106,7 +107,7 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch,
   SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded");
   SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed");
   SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded");
-
+  
   CheckInWithManager();
 
   LLDB_LOG(GetLog(LLDBLog::Object), "{0} Target::Target()",
@@ -147,6 +148,8 @@ void Target::PrimeFromDummyTarget(Target &target) {
 
   m_frame_recognizer_manager_up = std::make_unique<StackFrameRecognizerManager>(
       *target.m_frame_recognizer_manager_up);
+
+  m_dummy_signals = target.m_dummy_signals;
 }
 
 void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) {
@@ -287,6 +290,8 @@ void Target::Destroy() {
   m_stop_hooks.clear();
   m_stop_hook_next_id = 0;
   m_suppress_stop_hooks = false;
+  Args signal_args;
+  ClearDummySignals(signal_args);
 }
 
 llvm::StringRef Target::GetABIName() const {
@@ -3357,6 +3362,129 @@ void Target::FinalizeFileActions(ProcessLaunchInfo &info) {
   }
 }
 
+void Target::AddDummySignal(llvm::StringRef name, LazyBool pass, LazyBool notify, 
+                            LazyBool stop) {
+    if (name.empty())
+      return;
+    // Don't add a signal if all the actions are trivial:
+    if (pass == eLazyBoolCalculate && notify == eLazyBoolCalculate
+        && stop == eLazyBoolCalculate)
+      return;
+
+    auto& elem = m_dummy_signals[name];
+    elem.pass = pass;
+    elem.notify = notify;
+    elem.stop = stop;
+}
+
+bool Target::UpdateSignalFromDummy(UnixSignalsSP signals_sp, 
+                                          const DummySignalElement &elem) {
+  if (!signals_sp)
+    return false;
+
+  int32_t signo 
+      = signals_sp->GetSignalNumberFromName(elem.first().str().c_str());
+  if (signo == LLDB_INVALID_SIGNAL_NUMBER)
+    return false;
+    
+  if (elem.second.pass == eLazyBoolYes)
+    signals_sp->SetShouldSuppress(signo, false);
+  else if (elem.second.pass == eLazyBoolNo)
+    signals_sp->SetShouldSuppress(signo, true);
+  
+  if (elem.second.notify == eLazyBoolYes)
+    signals_sp->SetShouldNotify(signo, true);
+  else if (elem.second.notify == eLazyBoolNo)
+    signals_sp->SetShouldNotify(signo, false);
+  
+  if (elem.second.stop == eLazyBoolYes)
+    signals_sp->SetShouldStop(signo, true);
+  else if (elem.second.stop == eLazyBoolNo)
+    signals_sp->SetShouldStop(signo, false);
+  return true;  
+}
+
+bool Target::ResetSignalFromDummy(UnixSignalsSP signals_sp, 
+                                          const DummySignalElement &elem) {
+  if (!signals_sp)
+    return false;
+  int32_t signo 
+      = signals_sp->GetSignalNumberFromName(elem.first().str().c_str());
+  if (signo == LLDB_INVALID_SIGNAL_NUMBER)
+    return false;
+  bool do_pass = elem.second.pass != eLazyBoolCalculate;
+  bool do_stop = elem.second.stop != eLazyBoolCalculate;
+  bool do_notify = elem.second.notify != eLazyBoolCalculate;
+  signals_sp->ResetSignal(signo, do_stop, do_notify, do_pass);
+  return true;
+}
+
+void Target::UpdateSignalsFromDummy(UnixSignalsSP signals_sp, 
+                                    StreamSP warning_stream_sp) {
+  if (!signals_sp)
+    return;
+
+  for (const auto &elem : m_dummy_signals) {
+    if (!UpdateSignalFromDummy(signals_sp, elem))
+      warning_stream_sp->Printf("Target signal '%s' not found in process\n", 
+          elem.first().str().c_str());
+  }
+}
+
+void Target::ClearDummySignals(Args &signal_names) {
+  ProcessSP process_sp = GetProcessSP();
+  // The simplest case, delete them all with no process to update.
+  if (signal_names.GetArgumentCount() == 0 && !process_sp) {
+    m_dummy_signals.clear();
+    return;
+  }
+  UnixSignalsSP signals_sp;
+  if (process_sp)
+    signals_sp = process_sp->GetUnixSignals();
+
+  for (const Args::ArgEntry &entry : signal_names) {
+    const char *signal_name = entry.c_str();
+    auto elem = m_dummy_signals.find(signal_name);
+    // If we didn't find it go on.
+    // FIXME: Should I pipe error handling through here?
+    if (elem == m_dummy_signals.end()) {
+      continue;
+    }
+    if (signals_sp)
+      ResetSignalFromDummy(signals_sp, *elem);
+    m_dummy_signals.erase(elem);
+  }
+}
+
+void Target::PrintDummySignals(Stream &strm, Args &signal_args) {
+  strm.Printf("NAME         PASS     STOP     NOTIFY\n");
+  strm.Printf("===========  =======  =======  =======\n");
+  
+  auto str_for_lazy = [] (LazyBool lazy) -> const char * {
+    switch (lazy) {
+      case eLazyBoolCalculate: return "not set";
+      case eLazyBoolYes: return "true   ";
+      case eLazyBoolNo: return "false  ";
+    }
+  };
+  size_t num_args = signal_args.GetArgumentCount();
+  for (const auto &elem : m_dummy_signals) {
+    bool print_it = false;
+    for (size_t idx = 0; idx < num_args; idx++) {
+      if (elem.first() == signal_args.GetArgumentAtIndex(idx)) {
+        print_it = true;
+        break;
+      }
+    }
+    if (print_it) {
+      strm.Printf("%-11s  ", elem.first().str().c_str());
+      strm.Printf("%s  %s  %s\n", str_for_lazy(elem.second.pass),
+                  str_for_lazy(elem.second.stop),
+                  str_for_lazy(elem.second.notify));
+    }
+  }
+}
+
 // Target::StopHook
 Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid)
     : UserID(uid), m_target_sp(target_sp), m_specifier_sp(),

diff  --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp
index 26ff0bbd3825f..de1fdb8cc4202 100644
--- a/lldb/source/Target/UnixSignals.cpp
+++ b/lldb/source/Target/UnixSignals.cpp
@@ -22,7 +22,9 @@ UnixSignals::Signal::Signal(const char *name, bool default_suppress,
                             const char *description, const char *alias)
     : m_name(name), m_alias(alias), m_description(),
       m_suppress(default_suppress), m_stop(default_stop),
-      m_notify(default_notify) {
+      m_notify(default_notify),
+      m_default_suppress(default_suppress), m_default_stop(default_stop),
+      m_default_notify(default_notify) {
   if (description)
     m_description.assign(description);
 }
@@ -330,3 +332,23 @@ json::Value UnixSignals::GetHitCountStatistics() const {
   }
   return std::move(json_signals);
 }
+
+void UnixSignals::Signal::Reset(bool reset_stop, bool reset_notify, 
+                                bool reset_suppress) {
+  if (reset_stop)
+    m_stop = m_default_stop;
+  if (reset_notify)
+    m_notify = m_default_notify;
+  if (reset_suppress)
+    m_suppress = m_default_suppress;
+}
+
+bool UnixSignals::ResetSignal(int32_t signo, bool reset_stop, 
+                                 bool reset_notify, bool reset_suppress) {
+    auto elem = m_signals.find(signo);
+    if (elem == m_signals.end())
+      return false;
+    (*elem).second.Reset(reset_stop, reset_notify, reset_suppress);
+    return true;
+}
+

diff  --git a/lldb/test/API/commands/process/handle/Makefile b/lldb/test/API/commands/process/handle/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/process/handle/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/commands/process/handle/TestProcessHandle.py b/lldb/test/API/commands/process/handle/TestProcessHandle.py
new file mode 100644
index 0000000000000..e026d4b74a35b
--- /dev/null
+++ b/lldb/test/API/commands/process/handle/TestProcessHandle.py
@@ -0,0 +1,136 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestProcessHandle(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    @no_debug_info_test
+    @skipIfWindows
+    def test_process_handle(self):
+        """Test that calling process handle before we have a target, and before we
+           have a process will affect the process.  Also that the signal settings
+           are preserved on rerun."""
+        self.build()
+
+        # Make sure we don't accept signal values by signo with no process - we don't know what the
+        # mapping will be so we can't do the right thing with bare numbers:
+        lldbutil.set_actions_for_signal(self, "9", "true", None, None, expect_success=False)
+
+        # First, I need a reference value so I can see whether changes actually took:
+        (target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here', lldb.SBFileSpec("main.cpp"))
+        (default_pass, default_stop, default_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV")
+        
+        # Let's change the value here, then exit and make sure the changed value sticks:
+        new_value = "false"
+        if default_pass == "true":
+            new_value = "false"
+
+        # First make sure we get an error for bogus values when running:
+        lldbutil.set_actions_for_signal(self, "NOTSIGSEGV", new_value, None, None, expect_success=False)
+
+        # Then set the one we intend to change.
+        lldbutil.set_actions_for_signal(self, "SIGSEGV", new_value, None, None)
+
+        process.Continue()
+        
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+        self.assertEqual(process.GetExitStatus(), 0)
+        
+        # Check that we preserved the setting:
+        (curr_pass, curr_stop, curr_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV",from_target=True)
+        self.assertEqual(curr_pass, new_value, "Pass was set correctly")
+        self.assertEqual(curr_stop, "not set", "Stop was not set by us")
+        self.assertEqual(curr_notify, "not set", "Notify was not set by us")
+
+        # Run again and make sure that we prime the new process with these settings:
+        process = lldbutil.run_to_breakpoint_do_run(self, target, bkpt)
+
+        # We check the process settings now, to see what got copied into the process:
+        (curr_pass, curr_stop, curr_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV")
+        self.assertEqual(curr_pass, new_value, "Pass was set correctly")
+        self.assertEqual(curr_stop, default_stop, "Stop was its default value")
+        self.assertEqual(curr_notify, default_notify, "Notify was its default value")
+
+        # Now kill this target, set the handling and make sure the values get copied from the dummy into the new target.
+        success = self.dbg.DeleteTarget(target)
+        self.assertTrue(success, "Deleted the target")
+        self.assertEqual(self.dbg.GetNumTargets(), 0, "We did delete all the targets.")
+
+        # The signal settings should be back at their default - we were only setting this on the target:
+        lldbutil.get_actions_for_signal(self, "SIGSEGV", from_target=True, expected_absent=True)
+        # Set a valid one:
+        lldbutil.set_actions_for_signal(self, "SIGSEGV", new_value, None, None)
+        # Set a bogus one - we don't have a way to check pre-run so this is allowed
+        # but we should get an error message when launching:
+        lldbutil.set_actions_for_signal(self, "SIGNOTSIG", new_value, None, None)
+
+        out_filename = self.getBuildArtifact('output')
+        success = True
+        try:
+            f = open(out_filename, 'w')
+        except:
+            success = False
+
+        if not success:
+            self.fail("Couldn't open error output file for writing.")
+
+        self.dbg.SetErrorFileHandle(f, False)
+        # Now make a new process and make sure the right values got copied into the new target
+        (target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here', lldb.SBFileSpec("main.cpp"))
+        f.write("TESTPATTERN\n")
+        f.flush()
+        f.close()
+
+        try:
+            f = open(out_filename, 'r')
+        except:
+            success = False
+
+        if not success:
+            self.fail("Couldn't open error output file for reading")
+        errors = f.read()
+        f.close()
+        
+        self.assertIn("SIGNOTSIG", errors, "We warned about the unset signal")
+        # Also make sure we didn't accidentally add this bogus setting to the process.
+        lldbutil.set_actions_for_signal(self, "SIGNOTSIG", "true", "true", "true", expect_success=False)
+        
+        # Check that they went into the target:
+        (curr_pass, curr_stop, curr_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV",from_target=True)
+        self.assertEqual(curr_pass, new_value, "Pass was set correctly")
+        self.assertEqual(curr_stop, "not set", "Stop was not set by us")
+        self.assertEqual(curr_notify, "not set", "Notify was not set by us")
+
+        # And the process:
+        # Check that they went into the target:
+        (curr_pass, curr_stop, curr_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV")
+        self.assertEqual(curr_pass, new_value, "Pass was set correctly")
+        self.assertEqual(curr_stop, default_stop, "Stop was its default value")
+        self.assertEqual(curr_notify, default_notify, "Notify was its default value")
+
+        # Now clear the handling, and make sure that we get the right signal values again:
+        self.runCmd("process handle -c SIGSEGV")
+        # Check that there is no longer configuration for SIGSEGV in the target:
+        lldbutil.get_actions_for_signal(self, "SIGSEGV",from_target=True, expected_absent=True)
+        # Make a new process, to make sure we did indeed reset the values:
+        (target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here', lldb.SBFileSpec("main.cpp"))
+        (curr_pass, curr_stop, curr_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV")
+        self.assertEqual(curr_pass, new_value, "Pass was set correctly")
+        self.assertEqual(curr_stop, default_stop, "Stop was its default value")
+        self.assertEqual(curr_notify, default_notify, "Notify was its default value")
+
+        # Finally remove this from the dummy target as well, and make sure it was cleared from there:
+        self.runCmd("process handle -c -d SIGSEGV")
+        error = process.Kill()
+        self.assertSuccess(error, "Killed the process")
+        success = self.dbg.DeleteTarget(target)
+        self.assertTrue(success, "Destroyed the target.")
+        
+        (target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here', lldb.SBFileSpec("main.cpp"))
+        (curr_pass, curr_stop, curr_notify) = lldbutil.get_actions_for_signal(self, "SIGSEGV")
+        self.assertEqual(curr_pass, default_pass, "Pass was set correctly")
+        self.assertEqual(curr_stop, default_stop, "Stop was its default value")
+        self.assertEqual(curr_notify, default_notify, "Notify was its default value")

diff  --git a/lldb/test/API/commands/process/handle/main.cpp b/lldb/test/API/commands/process/handle/main.cpp
new file mode 100644
index 0000000000000..ba45ee316cd42
--- /dev/null
+++ b/lldb/test/API/commands/process/handle/main.cpp
@@ -0,0 +1,3 @@
+int main() {
+  return 0; // break here
+}

diff  --git a/lldb/test/API/functionalities/signal/raise/TestRaise.py b/lldb/test/API/functionalities/signal/raise/TestRaise.py
index 70271e43cff59..4a81c69577537 100644
--- a/lldb/test/API/functionalities/signal/raise/TestRaise.py
+++ b/lldb/test/API/functionalities/signal/raise/TestRaise.py
@@ -36,6 +36,10 @@ def test_sigtrap(self):
 
     def launch(self, target, signal):
         # launch the process, do not stop at entry point.
+        # If we have gotten the default for this signal, reset that as well.
+        if len(self.default_pass) != 0:
+            lldbutil.set_actions_for_signal(self, signal, self.default_pass, self.default_stop, self.default_notify)
+
         process = target.LaunchSimple(
             [signal], None, self.get_process_working_directory())
         self.assertTrue(process, PROCESS_IS_VALID)
@@ -64,27 +68,19 @@ def signal_test(self, signal, test_passing):
         target = self.dbg.CreateTarget(exe)
         self.assertTrue(target, VALID_TARGET)
         lldbutil.run_break_set_by_symbol(self, "main")
+        self.default_pass = ""
+        self.default_stop = ""
+        self.default_notify = ""
 
         # launch
         process = self.launch(target, signal)
         signo = process.GetUnixSignals().GetSignalNumberFromName(signal)
 
         # retrieve default signal disposition
-        return_obj = lldb.SBCommandReturnObject()
-        self.dbg.GetCommandInterpreter().HandleCommand(
-            "process handle %s " % signal, return_obj)
-        match = re.match(
-            'NAME *PASS *STOP *NOTIFY.*(false|true) *(false|true) *(false|true)',
-            return_obj.GetOutput(),
-            re.IGNORECASE | re.DOTALL)
-        if not match:
-            self.fail('Unable to retrieve default signal disposition.')
-        default_pass = match.group(1)
-        default_stop = match.group(2)
-        default_notify = match.group(3)
+        (self.default_pass, self.default_stop, self.default_notify) = lldbutil.get_actions_for_signal(self, signal)
 
         # Make sure we stop at the signal
-        self.set_handle(signal, "false", "true", "true")
+        lldbutil.set_actions_for_signal(self, signal, "false", "true", "true")
         process.Continue()
         self.assertEqual(process.GetState(), lldb.eStateStopped)
         thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
@@ -102,12 +98,11 @@ def signal_test(self, signal, test_passing):
         self.assertEqual(process.GetState(), lldb.eStateExited)
         self.assertEqual(process.GetExitStatus(), 0)
 
-        # launch again
         process = self.launch(target, signal)
 
         # Make sure we do not stop at the signal. We should still get the
         # notification.
-        self.set_handle(signal, "false", "false", "true")
+        lldbutil.set_actions_for_signal(self, signal, "false", "false", "true")
         self.expect(
             "process continue",
             substrs=[
@@ -121,7 +116,7 @@ def signal_test(self, signal, test_passing):
 
         # Make sure we do not stop at the signal, and we do not get the
         # notification.
-        self.set_handle(signal, "false", "false", "false")
+        lldbutil.set_actions_for_signal(self, signal, "false", "false", "false")
         self.expect(
             "process continue",
             substrs=["stopped and restarted"],
@@ -131,14 +126,14 @@ def signal_test(self, signal, test_passing):
 
         if not test_passing:
             # reset signal handling to default
-            self.set_handle(signal, default_pass, default_stop, default_notify)
+            lldbutil.set_actions_for_signal(self, signal, self.default_pass, self.default_stop, self.default_notify)
             return
 
         # launch again
         process = self.launch(target, signal)
 
         # Make sure we stop at the signal
-        self.set_handle(signal, "true", "true", "true")
+        lldbutil.set_actions_for_signal(self, signal, "true", "true", "true")
         process.Continue()
         self.assertEqual(process.GetState(), lldb.eStateStopped)
         thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
@@ -164,7 +159,7 @@ def signal_test(self, signal, test_passing):
 
         # Make sure we do not stop at the signal. We should still get the notification. Process
         # should receive the signal.
-        self.set_handle(signal, "true", "false", "true")
+        lldbutil.set_actions_for_signal(self, signal, "true", "false", "true")
         self.expect(
             "process continue",
             substrs=[
@@ -178,7 +173,7 @@ def signal_test(self, signal, test_passing):
 
         # Make sure we do not stop at the signal, and we do not get the notification. Process
         # should receive the signal.
-        self.set_handle(signal, "true", "false", "false")
+        lldbutil.set_actions_for_signal(self, signal, "true", "false", "false")
         self.expect(
             "process continue",
             substrs=["stopped and restarted"],
@@ -187,4 +182,4 @@ def signal_test(self, signal, test_passing):
         self.assertEqual(process.GetExitStatus(), signo)
 
         # reset signal handling to default
-        self.set_handle(signal, default_pass, default_stop, default_notify)
+        lldbutil.set_actions_for_signal(self, signal, self.default_pass, self.default_stop, self.default_notify)

diff  --git a/lldb/unittests/Signals/UnixSignalsTest.cpp b/lldb/unittests/Signals/UnixSignalsTest.cpp
index 1a53f0a249516..5b07cf45d099b 100644
--- a/lldb/unittests/Signals/UnixSignalsTest.cpp
+++ b/lldb/unittests/Signals/UnixSignalsTest.cpp
@@ -53,6 +53,29 @@ TEST(UnixSignalsTest, Iteration) {
   EXPECT_EQ(LLDB_INVALID_SIGNAL_NUMBER, signals.GetNextSignalNumber(16));
 }
 
+TEST(UnixSignalsTest, Reset) {
+  TestSignals signals;
+  bool stop_val     = signals.GetShouldStop(2);
+  bool notify_val   = signals.GetShouldNotify(2);
+  bool suppress_val = signals.GetShouldSuppress(2);
+  
+  // Change two, then reset one and make sure only that one was reset:
+  EXPECT_EQ(true, signals.SetShouldNotify(2, !notify_val));
+  EXPECT_EQ(true, signals.SetShouldSuppress(2, !suppress_val));
+  EXPECT_EQ(true, signals.ResetSignal(2, false, true, false));
+  EXPECT_EQ(stop_val, signals.GetShouldStop(2));
+  EXPECT_EQ(notify_val, signals.GetShouldStop(2));
+  EXPECT_EQ(!suppress_val, signals.GetShouldNotify(2));
+  
+  // Make sure reset with no arguments resets them all:
+  EXPECT_EQ(true, signals.SetShouldSuppress(2, !suppress_val));
+  EXPECT_EQ(true, signals.SetShouldNotify(2, !notify_val));
+  EXPECT_EQ(true, signals.ResetSignal(2));
+  EXPECT_EQ(stop_val, signals.GetShouldStop(2));
+  EXPECT_EQ(notify_val, signals.GetShouldNotify(2));
+  EXPECT_EQ(suppress_val, signals.GetShouldSuppress(2));
+}
+
 TEST(UnixSignalsTest, GetInfo) {
   TestSignals signals;
 


        


More information about the lldb-commits mailing list