diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h --- a/lldb/include/lldb/Target/Target.h +++ b/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; + 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 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 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 --- a/lldb/include/lldb/Target/UnixSignals.h +++ b/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(); diff --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py --- a/lldb/packages/Python/lldbsuite/test/lldbutil.py +++ b/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): diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/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 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); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -742,6 +742,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.">; @@ -750,6 +752,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 { diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2566,6 +2566,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) @@ -2928,6 +2935,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 diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp --- 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 @@ 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( *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 { @@ -3357,6 +3362,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(), diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp --- a/lldb/source/Target/UnixSignals.cpp +++ b/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; +} + diff --git a/lldb/test/API/commands/process/handle/Makefile b/lldb/test/API/commands/process/handle/Makefile new file mode 100644 --- /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 --- /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 --- /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 --- a/lldb/test/API/functionalities/signal/raise/TestRaise.py +++ b/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) diff --git a/lldb/unittests/Signals/UnixSignalsTest.cpp b/lldb/unittests/Signals/UnixSignalsTest.cpp --- a/lldb/unittests/Signals/UnixSignalsTest.cpp +++ b/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;