jingham updated this revision to Diff 432145.
jingham added a comment.

Address Jonas' review comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D126259/new/

https://reviews.llvm.org/D126259

Files:
  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/commands/process/handle/Makefile
  lldb/test/API/commands/process/handle/TestProcessHandle.py
  lldb/test/API/commands/process/handle/main.cpp
  lldb/test/API/functionalities/signal/raise/TestRaise.py
  lldb/unittests/Signals/UnixSignalsTest.cpp

Index: lldb/unittests/Signals/UnixSignalsTest.cpp
===================================================================
--- lldb/unittests/Signals/UnixSignalsTest.cpp
+++ lldb/unittests/Signals/UnixSignalsTest.cpp
@@ -53,6 +53,29 @@
   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;
 
Index: lldb/test/API/functionalities/signal/raise/TestRaise.py
===================================================================
--- lldb/test/API/functionalities/signal/raise/TestRaise.py
+++ lldb/test/API/functionalities/signal/raise/TestRaise.py
@@ -36,6 +36,10 @@
 
     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 @@
         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 @@
         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 @@
 
         # 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 @@
 
         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 @@
 
         # 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 @@
 
         # 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 @@
         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)
Index: lldb/test/API/commands/process/handle/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/process/handle/main.cpp
@@ -0,0 +1,3 @@
+int main() {
+  return 0; // break here
+}
Index: lldb/test/API/commands/process/handle/TestProcessHandle.py
===================================================================
--- /dev/null
+++ 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")
Index: lldb/test/API/commands/process/handle/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/commands/process/handle/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
Index: lldb/source/Target/UnixSignals.cpp
===================================================================
--- lldb/source/Target/UnixSignals.cpp
+++ lldb/source/Target/UnixSignals.cpp
@@ -22,7 +22,9 @@
                             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 @@
   }
   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;
+}
+
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ 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 @@
   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 @@
 
   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 @@
   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 {
@@ -3349,6 +3354,129 @@
   }
 }
 
+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(),
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -2557,6 +2557,13 @@
 
   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)
@@ -2919,6 +2926,12 @@
       }
     }
   }
+  // 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
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -738,6 +738,8 @@
 }
 
 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.">;
@@ -746,6 +748,10 @@
     "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 {
Index: lldb/source/Commands/CommandObjectProcess.cpp
===================================================================
--- lldb/source/Commands/CommandObjectProcess.cpp
+++ lldb/source/Commands/CommandObjectProcess.cpp
@@ -1432,6 +1432,12 @@
       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 @@
       case 'p':
         pass = std::string(option_arg);
         break;
+      case 't':
+        only_target_values = true;
+        break;
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -1451,6 +1460,9 @@
       stop.clear();
       notify.clear();
       pass.clear();
+      only_target_values = false;
+      do_clear = false;
+      dummy = false;
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -1462,6 +1474,9 @@
     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 @@
                             "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 @@
 
 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 @@
                          "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 @@
       }
     }
 
-    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);
 
Index: lldb/packages/Python/lldbsuite/test/lldbutil.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lldbutil.py
+++ lldb/packages/Python/lldbsuite/test/lldbutil.py
@@ -1527,6 +1527,42 @@
     # 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):
 
Index: lldb/include/lldb/Target/UnixSignals.h
===================================================================
--- lldb/include/lldb/Target/UnixSignals.h
+++ lldb/include/lldb/Target/UnixSignals.h
@@ -55,6 +55,9 @@
   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 @@
     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();
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -1414,6 +1414,42 @@
     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 @@
     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 @@
   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);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to