forked from OSchip/llvm-project
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
This commit is contained in:
parent
e267df8ce8
commit
134d7f9a4b
|
@ -1414,6 +1414,42 @@ public:
|
|||
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 @@ protected:
|
|||
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 @@ protected:
|
|||
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);
|
||||
|
|
|
@ -55,6 +55,9 @@ public:
|
|||
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 @@ protected:
|
|||
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();
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -1432,6 +1432,12 @@ public:
|
|||
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 @@ public:
|
|||
case 'p':
|
||||
pass = std::string(option_arg);
|
||||
break;
|
||||
case 't':
|
||||
only_target_values = true;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unimplemented option");
|
||||
}
|
||||
|
@ -1451,6 +1460,9 @@ public:
|
|||
stop.clear();
|
||||
notify.clear();
|
||||
pass.clear();
|
||||
only_target_values = false;
|
||||
do_clear = false;
|
||||
dummy = false;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
||||
|
@ -1462,6 +1474,9 @@ public:
|
|||
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 @@ public:
|
|||
"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 @@ public:
|
|||
|
||||
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 @@ protected:
|
|||
"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 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
|
@ -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")
|
|
@ -0,0 +1,3 @@
|
|||
int main() {
|
||||
return 0; // break here
|
||||
}
|
|
@ -36,6 +36,10 @@ class RaiseTestCase(TestBase):
|
|||
|
||||
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 @@ class RaiseTestCase(TestBase):
|
|||
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 @@ class RaiseTestCase(TestBase):
|
|||
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 @@ class RaiseTestCase(TestBase):
|
|||
|
||||
# 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 @@ class RaiseTestCase(TestBase):
|
|||
|
||||
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 @@ class RaiseTestCase(TestBase):
|
|||
|
||||
# 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 @@ class RaiseTestCase(TestBase):
|
|||
|
||||
# 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 @@ class RaiseTestCase(TestBase):
|
|||
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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue