diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-04 19:20:19 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-08 19:02:26 +0000 |
commit | 81ad626541db97eb356e2c1d4a20eb2a26a766ab (patch) | |
tree | 311b6a8987c32b1e1dcbab65c54cfac3fdb56175 /contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp | |
parent | 5fff09660e06a66bed6482da9c70df328e16bbb6 (diff) | |
parent | 145449b1e420787bb99721a429341fa6be3adfb6 (diff) |
Diffstat (limited to 'contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp | 407 |
1 files changed, 324 insertions, 83 deletions
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp index c73f0df0aaf2..c76ae99057f2 100644 --- a/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp +++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp @@ -8,9 +8,12 @@ #include "CommandObjectProcess.h" #include "CommandObjectTrace.h" +#include "CommandObjectBreakpoint.h" #include "CommandOptionsProcessLaunch.h" #include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointName.h" #include "lldb/Breakpoint/BreakpointSite.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" @@ -29,6 +32,8 @@ #include "lldb/Utility/Args.h" #include "lldb/Utility/State.h" +#include "llvm/ADT/ScopeExit.h" + #include <bitset> using namespace lldb; @@ -145,10 +150,10 @@ public: Options *GetOptions() override { return &m_all_options; } - const char *GetRepeatCommand(Args ¤t_command_args, - uint32_t index) override { + llvm::Optional<std::string> GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { // No repeat for "process launch"... - return ""; + return std::string(""); } protected: @@ -409,12 +414,6 @@ protected: ModuleSP old_exec_module_sp = target->GetExecutableModule(); ArchSpec old_arch_spec = target->GetArchitecture(); - if (command.GetArgumentCount()) { - result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: %s\n", - m_cmd_name.c_str(), m_cmd_syntax.c_str()); - return false; - } - StreamString stream; ProcessSP process_sp; const auto error = target->Attach(m_options.attach_info, &stream); @@ -516,7 +515,7 @@ protected: ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, - ExecutionContext *execution_context) override { + ExecutionContext *exe_ctx) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { @@ -526,7 +525,10 @@ protected: "invalid value for ignore option: \"%s\", should be a number.", option_arg.str().c_str()); break; - + case 'b': + m_run_to_bkpt_args.AppendArgument(option_arg); + m_any_bkpts_specified = true; + break; default: llvm_unreachable("Unimplemented option"); } @@ -535,27 +537,25 @@ protected: void OptionParsingStarting(ExecutionContext *execution_context) override { m_ignore = 0; + m_run_to_bkpt_args.Clear(); + m_any_bkpts_specified = false; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { return llvm::makeArrayRef(g_process_continue_options); } - uint32_t m_ignore; + uint32_t m_ignore = 0; + Args m_run_to_bkpt_args; + bool m_any_bkpts_specified = false; }; + bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); bool synchronous_execution = m_interpreter.GetSynchronous(); StateType state = process->GetState(); if (state == eStateStopped) { - if (command.GetArgumentCount() != 0) { - result.AppendErrorWithFormat( - "The '%s' command does not take any arguments.\n", - m_cmd_name.c_str()); - return false; - } - if (m_options.m_ignore > 0) { ThreadSP sel_thread_sp(GetDefaultThread()->shared_from_this()); if (sel_thread_sp) { @@ -579,6 +579,130 @@ protected: } } } + + Target *target = m_exe_ctx.GetTargetPtr(); + BreakpointIDList run_to_bkpt_ids; + // Don't pass an empty run_to_breakpoint list, as Verify will look for the + // default breakpoint. + if (m_options.m_run_to_bkpt_args.GetArgumentCount() > 0) + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( + m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids, + BreakpointName::Permissions::disablePerm); + if (!result.Succeeded()) { + return false; + } + result.Clear(); + if (m_options.m_any_bkpts_specified && run_to_bkpt_ids.GetSize() == 0) { + result.AppendError("continue-to breakpoints did not specify any actual " + "breakpoints or locations"); + return false; + } + + // First figure out which breakpoints & locations were specified by the + // user: + size_t num_run_to_bkpt_ids = run_to_bkpt_ids.GetSize(); + std::vector<break_id_t> bkpts_disabled; + std::vector<BreakpointID> locs_disabled; + if (num_run_to_bkpt_ids != 0) { + // Go through the ID's specified, and separate the breakpoints from are + // the breakpoint.location specifications since the latter require + // special handling. We also figure out whether there's at least one + // specifier in the set that is enabled. + BreakpointList &bkpt_list = target->GetBreakpointList(); + std::unordered_set<break_id_t> bkpts_seen; + std::unordered_set<break_id_t> bkpts_with_locs_seen; + BreakpointIDList with_locs; + bool any_enabled = false; + + for (size_t idx = 0; idx < num_run_to_bkpt_ids; idx++) { + BreakpointID bkpt_id = run_to_bkpt_ids.GetBreakpointIDAtIndex(idx); + break_id_t bp_id = bkpt_id.GetBreakpointID(); + break_id_t loc_id = bkpt_id.GetLocationID(); + BreakpointSP bp_sp + = bkpt_list.FindBreakpointByID(bp_id); + // Note, VerifyBreakpointOrLocationIDs checks for existence, so we + // don't need to do it again here. + if (bp_sp->IsEnabled()) { + if (loc_id == LLDB_INVALID_BREAK_ID) { + // A breakpoint (without location) was specified. Make sure that + // at least one of the locations is enabled. + size_t num_locations = bp_sp->GetNumLocations(); + for (size_t loc_idx = 0; loc_idx < num_locations; loc_idx++) { + BreakpointLocationSP loc_sp + = bp_sp->GetLocationAtIndex(loc_idx); + if (loc_sp->IsEnabled()) { + any_enabled = true; + break; + } + } + } else { + // A location was specified, check if it was enabled: + BreakpointLocationSP loc_sp = bp_sp->FindLocationByID(loc_id); + if (loc_sp->IsEnabled()) + any_enabled = true; + } + + // Then sort the bp & bp.loc entries for later use: + if (bkpt_id.GetLocationID() == LLDB_INVALID_BREAK_ID) + bkpts_seen.insert(bkpt_id.GetBreakpointID()); + else { + bkpts_with_locs_seen.insert(bkpt_id.GetBreakpointID()); + with_locs.AddBreakpointID(bkpt_id); + } + } + } + // Do all the error checking here so once we start disabling we don't + // have to back out half-way through. + + // Make sure at least one of the specified breakpoints is enabled. + if (!any_enabled) { + result.AppendError("at least one of the continue-to breakpoints must " + "be enabled."); + return false; + } + + // Also, if you specify BOTH a breakpoint and one of it's locations, + // we flag that as an error, since it won't do what you expect, the + // breakpoint directive will mean "run to all locations", which is not + // what the location directive means... + for (break_id_t bp_id : bkpts_with_locs_seen) { + if (bkpts_seen.count(bp_id)) { + result.AppendErrorWithFormatv("can't specify both a breakpoint and " + "one of its locations: {0}", bp_id); + } + } + + // Now go through the breakpoints in the target, disabling all the ones + // that the user didn't mention: + for (BreakpointSP bp_sp : bkpt_list.Breakpoints()) { + break_id_t bp_id = bp_sp->GetID(); + // Handle the case where no locations were specified. Note we don't + // have to worry about the case where a breakpoint and one of its + // locations are both in the lists, we've already disallowed that. + if (!bkpts_with_locs_seen.count(bp_id)) { + if (!bkpts_seen.count(bp_id) && bp_sp->IsEnabled()) { + bkpts_disabled.push_back(bp_id); + bp_sp->SetEnabled(false); + } + continue; + } + // Next, handle the case where a location was specified: + // Run through all the locations of this breakpoint and disable + // the ones that aren't on our "with locations" BreakpointID list: + size_t num_locations = bp_sp->GetNumLocations(); + BreakpointID tmp_id(bp_id, LLDB_INVALID_BREAK_ID); + for (size_t loc_idx = 0; loc_idx < num_locations; loc_idx++) { + BreakpointLocationSP loc_sp = bp_sp->GetLocationAtIndex(loc_idx); + tmp_id.SetBreakpointLocationID(loc_idx); + size_t position = 0; + if (!with_locs.FindBreakpointID(tmp_id, &position) + && loc_sp->IsEnabled()) { + locs_disabled.push_back(tmp_id); + loc_sp->SetEnabled(false); + } + } + } + } { // Scope for thread list mutex: std::lock_guard<std::recursive_mutex> guard( @@ -597,10 +721,39 @@ protected: StreamString stream; Status error; + // For now we can only do -b with synchronous: + bool old_sync = GetDebugger().GetAsyncExecution(); + + if (run_to_bkpt_ids.GetSize() != 0) { + GetDebugger().SetAsyncExecution(false); + synchronous_execution = true; + } if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); + + if (run_to_bkpt_ids.GetSize() != 0) { + GetDebugger().SetAsyncExecution(old_sync); + } + + // Now re-enable the breakpoints we disabled: + BreakpointList &bkpt_list = target->GetBreakpointList(); + for (break_id_t bp_id : bkpts_disabled) { + BreakpointSP bp_sp = bkpt_list.FindBreakpointByID(bp_id); + if (bp_sp) + bp_sp->SetEnabled(true); + } + for (const BreakpointID &bkpt_id : locs_disabled) { + BreakpointSP bp_sp + = bkpt_list.FindBreakpointByID(bkpt_id.GetBreakpointID()); + if (bp_sp) { + BreakpointLocationSP loc_sp + = bp_sp->FindLocationByID(bkpt_id.GetLocationID()); + if (loc_sp) + loc_sp->SetEnabled(true); + } + } if (error.Success()) { // There is a race condition where this thread will return up the call @@ -777,7 +930,10 @@ public: CommandObjectProcessConnect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process connect", "Connect to a remote debug service.", - "process connect <remote-url>", 0) {} + "process connect <remote-url>", 0) { + CommandArgumentData connect_arg{eArgTypeConnectURL, eArgRepeatPlain}; + m_arguments.push_back({connect_arg}); + } ~CommandObjectProcessConnect() override = default; @@ -902,7 +1058,10 @@ public: "process load <filename> [<filename> ...]", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | - eCommandProcessMustBePaused) {} + eCommandProcessMustBePaused) { + CommandArgumentData file_arg{eArgTypePath, eArgRepeatPlus}; + m_arguments.push_back({file_arg}); + } ~CommandObjectProcessLoad() override = default; @@ -977,7 +1136,10 @@ public: "returned by a previous call to \"process load\".", "process unload <index>", eCommandRequiresProcess | eCommandTryTargetAPILock | - eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {} + eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { + CommandArgumentData load_idx_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; + m_arguments.push_back({load_idx_arg}); + } ~CommandObjectProcessUnload() override = default; @@ -1125,18 +1287,13 @@ protected: return false; } - if (command.GetArgumentCount() == 0) { - bool clear_thread_plans = true; - Status error(process->Halt(clear_thread_plans)); - if (error.Success()) { - result.SetStatus(eReturnStatusSuccessFinishResult); - } else { - result.AppendErrorWithFormat("Failed to halt process: %s\n", - error.AsCString()); - } + bool clear_thread_plans = true; + Status error(process->Halt(clear_thread_plans)); + if (error.Success()) { + result.SetStatus(eReturnStatusSuccessFinishResult); } else { - result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n", - m_cmd_name.c_str(), m_cmd_syntax.c_str()); + result.AppendErrorWithFormat("Failed to halt process: %s\n", + error.AsCString()); } return result.Succeeded(); } @@ -1164,17 +1321,12 @@ protected: return false; } - if (command.GetArgumentCount() == 0) { - Status error(process->Destroy(true)); - if (error.Success()) { - result.SetStatus(eReturnStatusSuccessFinishResult); - } else { - result.AppendErrorWithFormat("Failed to kill process: %s\n", - error.AsCString()); - } + Status error(process->Destroy(true)); + if (error.Success()) { + result.SetStatus(eReturnStatusSuccessFinishResult); } else { - result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n", - m_cmd_name.c_str(), m_cmd_syntax.c_str()); + result.AppendErrorWithFormat("Failed to kill process: %s\n", + error.AsCString()); } return result.Succeeded(); } @@ -1206,7 +1358,10 @@ public: "appropriate file type.", "process save-core [-s corefile-style -p plugin-name] FILE", eCommandRequiresProcess | eCommandTryTargetAPILock | - eCommandProcessMustBeLaunched) {} + eCommandProcessMustBeLaunched) { + CommandArgumentData file_arg{eArgTypePath, eArgRepeatPlain}; + m_arguments.push_back({file_arg}); + } ~CommandObjectProcessSaveCore() override = default; @@ -1214,7 +1369,7 @@ public: class CommandOptions : public Options { public: - CommandOptions() : m_requested_save_core_style(eSaveCoreUnspecified) {} + CommandOptions() = default; ~CommandOptions() override = default; @@ -1250,7 +1405,7 @@ public: } // Instance variables to hold the values for command options. - SaveCoreStyle m_requested_save_core_style; + SaveCoreStyle m_requested_save_core_style = eSaveCoreUnspecified; std::string m_requested_plugin_name; }; @@ -1317,7 +1472,7 @@ public: class CommandOptions : public Options { public: - CommandOptions() {} + CommandOptions() = default; ~CommandOptions() override = default; @@ -1353,11 +1508,6 @@ protected: Stream &strm = result.GetOutputStream(); result.SetStatus(eReturnStatusSuccessFinishNoResult); - if (command.GetArgumentCount()) { - result.AppendError("'process status' takes no arguments"); - return result.Succeeded(); - } - // No need to check "process" for validity as eCommandRequiresProcess // ensures it is valid Process *process = m_exe_ctx.GetProcessPtr(); @@ -1432,6 +1582,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 +1597,9 @@ public: case 'p': pass = std::string(option_arg); break; + case 't': + only_target_values = true; + break; default: llvm_unreachable("Unimplemented option"); } @@ -1451,6 +1610,9 @@ public: stop.clear(); notify.clear(); pass.clear(); + only_target_values = false; + do_clear = false; + dummy = false; } llvm::ArrayRef<OptionDefinition> GetDefinitions() override { @@ -1462,6 +1624,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 +1634,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 +1729,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 +1761,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 +1872,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); |