aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Target/Target.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
commitb60736ec1405bb0a8dd40989f67ef4c93da068ab (patch)
tree5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /lldb/source/Target/Target.cpp
parentcfca06d7963fa0909f90483b42a6d7d194d01e08 (diff)
Diffstat (limited to 'lldb/source/Target/Target.cpp')
-rw-r--r--lldb/source/Target/Target.cpp770
1 files changed, 536 insertions, 234 deletions
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index dad56376005c..736864e021bb 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -27,9 +27,11 @@
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/ExpressionVariable.h"
#include "lldb/Expression/REPL.h"
#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/PosixApi.h"
#include "lldb/Interpreter/CommandInterpreter.h"
@@ -45,6 +47,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackFrameRecognizer.h"
#include "lldb/Target/SystemRuntime.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadSpec.h"
@@ -94,6 +97,8 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch,
m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0),
m_valid(true), m_suppress_stop_hooks(false),
m_is_dummy_target(is_dummy_target),
+ m_frame_recognizer_manager_up(
+ std::make_unique<StackFrameRecognizerManager>()),
m_stats_storage(static_cast<int>(StatisticKind::StatisticMax))
{
@@ -123,13 +128,10 @@ Target::~Target() {
DeleteCurrentProcess();
}
-void Target::PrimeFromDummyTarget(Target *target) {
- if (!target)
- return;
-
- m_stop_hooks = target->m_stop_hooks;
+void Target::PrimeFromDummyTarget(Target &target) {
+ m_stop_hooks = target.m_stop_hooks;
- for (const auto &breakpoint_sp : target->m_breakpoint_list.Breakpoints()) {
+ for (const auto &breakpoint_sp : target.m_breakpoint_list.Breakpoints()) {
if (breakpoint_sp->IsInternal())
continue;
@@ -138,11 +140,14 @@ void Target::PrimeFromDummyTarget(Target *target) {
AddBreakpoint(std::move(new_bp), false);
}
- for (auto bp_name_entry : target->m_breakpoint_names) {
+ for (auto bp_name_entry : target.m_breakpoint_names) {
BreakpointName *new_bp_name = new BreakpointName(*bp_name_entry.second);
AddBreakpointName(new_bp_name);
}
+
+ m_frame_recognizer_manager_up = std::make_unique<StackFrameRecognizerManager>(
+ *target.m_frame_recognizer_manager_up);
}
void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) {
@@ -194,12 +199,13 @@ void Target::DeleteCurrentProcess() {
const lldb::ProcessSP &Target::CreateProcess(ListenerSP listener_sp,
llvm::StringRef plugin_name,
- const FileSpec *crash_file) {
+ const FileSpec *crash_file,
+ bool can_connect) {
if (!listener_sp)
listener_sp = GetDebugger().GetListener();
DeleteCurrentProcess();
m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name,
- listener_sp, crash_file);
+ listener_sp, crash_file, can_connect);
return m_process_sp;
}
@@ -1394,9 +1400,7 @@ void Target::SetExecutableModule(ModuleSP &executable_sp,
ClearModules(false);
if (executable_sp) {
- static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
- Timer scoped_timer(func_cat,
- "Target::SetExecutableModule (executable = '%s')",
+ LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')",
executable_sp->GetFileSpec().GetPath().c_str());
const bool notify = true;
@@ -1965,8 +1969,9 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
module_sp = m_images.FindFirstModule(module_spec);
if (!module_sp) {
- ModuleSP old_module_sp; // This will get filled in if we have a new version
- // of the library
+ llvm::SmallVector<ModuleSP, 1>
+ old_modules; // This will get filled in if we have a new version
+ // of the library
bool did_create_module = false;
FileSpecList search_paths = GetExecutableSearchPaths();
// If there are image search path entries, try to use them first to acquire
@@ -1979,7 +1984,7 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
transformed_spec.GetFileSpec().GetFilename() =
module_spec.GetFileSpec().GetFilename();
error = ModuleList::GetSharedModule(transformed_spec, module_sp,
- &search_paths, &old_module_sp,
+ &search_paths, &old_modules,
&did_create_module);
}
}
@@ -1997,7 +2002,7 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
// We have a UUID, it is OK to check the global module list...
error =
ModuleList::GetSharedModule(module_spec, module_sp, &search_paths,
- &old_module_sp, &did_create_module);
+ &old_modules, &did_create_module);
}
if (!module_sp) {
@@ -2006,7 +2011,7 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
if (m_platform_sp) {
error = m_platform_sp->GetSharedModule(
module_spec, m_process_sp.get(), module_sp, &search_paths,
- &old_module_sp, &did_create_module);
+ &old_modules, &did_create_module);
} else {
error.SetErrorString("no platform is currently set");
}
@@ -2057,18 +2062,18 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
// this target. So let's remove the UUID from the module list, and look
// in the target's module list. Only do this if there is SOMETHING else
// in the module spec...
- if (!old_module_sp) {
- if (module_spec.GetUUID().IsValid() &&
- !module_spec.GetFileSpec().GetFilename().IsEmpty() &&
- !module_spec.GetFileSpec().GetDirectory().IsEmpty()) {
- ModuleSpec module_spec_copy(module_spec.GetFileSpec());
- module_spec_copy.GetUUID().Clear();
+ if (module_spec.GetUUID().IsValid() &&
+ !module_spec.GetFileSpec().GetFilename().IsEmpty() &&
+ !module_spec.GetFileSpec().GetDirectory().IsEmpty()) {
+ ModuleSpec module_spec_copy(module_spec.GetFileSpec());
+ module_spec_copy.GetUUID().Clear();
- ModuleList found_modules;
- m_images.FindModules(module_spec_copy, found_modules);
- if (found_modules.GetSize() == 1)
- old_module_sp = found_modules.GetModuleAtIndex(0);
- }
+ ModuleList found_modules;
+ m_images.FindModules(module_spec_copy, found_modules);
+ found_modules.ForEach([&](const ModuleSP &found_module) -> bool {
+ old_modules.push_back(found_module);
+ return true;
+ });
}
// Preload symbols outside of any lock, so hopefully we can do this for
@@ -2076,14 +2081,67 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
if (GetPreloadSymbols())
module_sp->PreloadSymbols();
- if (old_module_sp && m_images.GetIndexForModule(old_module_sp.get()) !=
- LLDB_INVALID_INDEX32) {
- m_images.ReplaceModule(old_module_sp, module_sp);
+ llvm::SmallVector<ModuleSP, 1> replaced_modules;
+ for (ModuleSP &old_module_sp : old_modules) {
+ if (m_images.GetIndexForModule(old_module_sp.get()) !=
+ LLDB_INVALID_INDEX32) {
+ if (replaced_modules.empty())
+ m_images.ReplaceModule(old_module_sp, module_sp);
+ else
+ m_images.Remove(old_module_sp);
+
+ replaced_modules.push_back(std::move(old_module_sp));
+ }
+ }
+
+ if (replaced_modules.size() > 1) {
+ // The same new module replaced multiple old modules
+ // simultaneously. It's not clear this should ever
+ // happen (if we always replace old modules as we add
+ // new ones, presumably we should never have more than
+ // one old one). If there are legitimate cases where
+ // this happens, then the ModuleList::Notifier interface
+ // may need to be adjusted to allow reporting this.
+ // In the meantime, just log that this has happened; just
+ // above we called ReplaceModule on the first one, and Remove
+ // on the rest.
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET |
+ LIBLLDB_LOG_MODULES)) {
+ StreamString message;
+ auto dump = [&message](Module &dump_module) -> void {
+ UUID dump_uuid = dump_module.GetUUID();
+
+ message << '[';
+ dump_module.GetDescription(message.AsRawOstream());
+ message << " (uuid ";
+
+ if (dump_uuid.IsValid())
+ dump_uuid.Dump(&message);
+ else
+ message << "not specified";
+
+ message << ")]";
+ };
+
+ message << "New module ";
+ dump(*module_sp);
+ message.AsRawOstream()
+ << llvm::formatv(" simultaneously replaced {0} old modules: ",
+ replaced_modules.size());
+ for (ModuleSP &replaced_module_sp : replaced_modules)
+ dump(*replaced_module_sp);
+
+ log->PutString(message.GetString());
+ }
+ }
+
+ if (replaced_modules.empty())
+ m_images.Append(module_sp, notify);
+
+ for (ModuleSP &old_module_sp : replaced_modules) {
Module *old_module_ptr = old_module_sp.get();
old_module_sp.reset();
ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr);
- } else {
- m_images.Append(module_sp, notify);
}
} else
module_sp.reset();
@@ -2236,27 +2294,29 @@ FunctionCaller *Target::GetFunctionCallerForLanguage(
return persistent_fn;
}
-UtilityFunction *
-Target::GetUtilityFunctionForLanguage(const char *text,
- lldb::LanguageType language,
- const char *name, Status &error) {
+llvm::Expected<std::unique_ptr<UtilityFunction>>
+Target::CreateUtilityFunction(std::string expression, std::string name,
+ lldb::LanguageType language,
+ ExecutionContext &exe_ctx) {
auto type_system_or_err = GetScratchTypeSystemForLanguage(language);
+ if (!type_system_or_err)
+ return type_system_or_err.takeError();
- if (auto err = type_system_or_err.takeError()) {
- error.SetErrorStringWithFormat(
- "Could not find type system for language %s: %s",
- Language::GetNameForLanguageType(language),
- llvm::toString(std::move(err)).c_str());
- return nullptr;
- }
-
- auto *utility_fn = type_system_or_err->GetUtilityFunction(text, name);
+ std::unique_ptr<UtilityFunction> utility_fn =
+ type_system_or_err->CreateUtilityFunction(std::move(expression),
+ std::move(name));
if (!utility_fn)
- error.SetErrorStringWithFormat(
- "Could not create an expression for language %s",
- Language::GetNameForLanguageType(language));
+ return llvm::make_error<llvm::StringError>(
+ llvm::StringRef("Could not create an expression for language") +
+ Language::GetNameForLanguageType(language),
+ llvm::inconvertibleErrorCode());
- return utility_fn;
+ DiagnosticManager diagnostics;
+ if (!utility_fn->Install(diagnostics, exe_ctx))
+ return llvm::make_error<llvm::StringError>(diagnostics.GetString(),
+ llvm::inconvertibleErrorCode());
+
+ return std::move(utility_fn);
}
void Target::SettingsInitialize() { Process::SettingsInitialize(); }
@@ -2401,21 +2461,13 @@ lldb::addr_t Target::GetPersistentSymbol(ConstString name) {
llvm::Expected<lldb_private::Address> Target::GetEntryPointAddress() {
Module *exe_module = GetExecutableModulePointer();
- llvm::Error error = llvm::Error::success();
- assert(!error); // Check the success value when assertions are enabled.
- if (!exe_module || !exe_module->GetObjectFile()) {
- error = llvm::make_error<llvm::StringError>("No primary executable found",
- llvm::inconvertibleErrorCode());
- } else {
+ // Try to find the entry point address in the primary executable.
+ const bool has_primary_executable = exe_module && exe_module->GetObjectFile();
+ if (has_primary_executable) {
Address entry_addr = exe_module->GetObjectFile()->GetEntryPointAddress();
if (entry_addr.IsValid())
return entry_addr;
-
- error = llvm::make_error<llvm::StringError>(
- "Could not find entry point address for executable module \"" +
- exe_module->GetFileSpec().GetFilename().GetStringRef() + "\"",
- llvm::inconvertibleErrorCode());
}
const ModuleList &modules = GetImages();
@@ -2426,14 +2478,21 @@ llvm::Expected<lldb_private::Address> Target::GetEntryPointAddress() {
continue;
Address entry_addr = module_sp->GetObjectFile()->GetEntryPointAddress();
- if (entry_addr.IsValid()) {
- // Discard the error.
- llvm::consumeError(std::move(error));
+ if (entry_addr.IsValid())
return entry_addr;
- }
}
- return std::move(error);
+ // We haven't found the entry point address. Return an appropriate error.
+ if (!has_primary_executable)
+ return llvm::make_error<llvm::StringError>(
+ "No primary executable found and could not find entry point address in "
+ "any executable module",
+ llvm::inconvertibleErrorCode());
+
+ return llvm::make_error<llvm::StringError>(
+ "Could not find entry point address for primary executable module \"" +
+ exe_module->GetFileSpec().GetFilename().GetStringRef() + "\"",
+ llvm::inconvertibleErrorCode());
}
lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr,
@@ -2479,13 +2538,28 @@ ClangModulesDeclVendor *Target::GetClangModulesDeclVendor() {
return m_clang_modules_decl_vendor_up.get();
}
-Target::StopHookSP Target::CreateStopHook() {
+Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) {
lldb::user_id_t new_uid = ++m_stop_hook_next_id;
- Target::StopHookSP stop_hook_sp(new StopHook(shared_from_this(), new_uid));
+ Target::StopHookSP stop_hook_sp;
+ switch (kind) {
+ case StopHook::StopHookKind::CommandBased:
+ stop_hook_sp.reset(new StopHookCommandLine(shared_from_this(), new_uid));
+ break;
+ case StopHook::StopHookKind::ScriptBased:
+ stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid));
+ break;
+ }
m_stop_hooks[new_uid] = stop_hook_sp;
return stop_hook_sp;
}
+void Target::UndoCreateStopHook(lldb::user_id_t user_id) {
+ if (!RemoveStopHookByID(user_id))
+ return;
+ if (user_id == m_stop_hook_next_id)
+ m_stop_hook_next_id--;
+}
+
bool Target::RemoveStopHookByID(lldb::user_id_t user_id) {
size_t num_removed = m_stop_hooks.erase(user_id);
return (num_removed != 0);
@@ -2521,45 +2595,39 @@ void Target::SetAllStopHooksActiveState(bool active_state) {
}
}
-void Target::RunStopHooks() {
+bool Target::RunStopHooks() {
if (m_suppress_stop_hooks)
- return;
+ return false;
if (!m_process_sp)
- return;
+ return false;
// Somebody might have restarted the process:
+ // Still return false, the return value is about US restarting the target.
if (m_process_sp->GetState() != eStateStopped)
- return;
+ return false;
// <rdar://problem/12027563> make sure we check that we are not stopped
// because of us running a user expression since in that case we do not want
// to run the stop-hooks
if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression())
- return;
+ return false;
if (m_stop_hooks.empty())
- return;
-
- StopHookCollection::iterator pos, end = m_stop_hooks.end();
+ return false;
// If there aren't any active stop hooks, don't bother either.
- // Also see if any of the active hooks want to auto-continue.
bool any_active_hooks = false;
- bool auto_continue = false;
for (auto hook : m_stop_hooks) {
if (hook.second->IsActive()) {
any_active_hooks = true;
- auto_continue |= hook.second->GetAutoContinue();
+ break;
}
}
if (!any_active_hooks)
- return;
-
- CommandReturnObject result(m_debugger.GetUseColor());
+ return false;
std::vector<ExecutionContext> exc_ctx_with_reasons;
- std::vector<SymbolContext> sym_ctx_with_reasons;
ThreadList &cur_threadlist = m_process_sp->GetThreadList();
size_t num_threads = cur_threadlist.GetSize();
@@ -2567,103 +2635,127 @@ void Target::RunStopHooks() {
lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i);
if (cur_thread_sp->ThreadStoppedForAReason()) {
lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0);
- exc_ctx_with_reasons.push_back(ExecutionContext(
- m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get()));
- sym_ctx_with_reasons.push_back(
- cur_frame_sp->GetSymbolContext(eSymbolContextEverything));
+ exc_ctx_with_reasons.emplace_back(m_process_sp.get(), cur_thread_sp.get(),
+ cur_frame_sp.get());
}
}
// If no threads stopped for a reason, don't run the stop-hooks.
size_t num_exe_ctx = exc_ctx_with_reasons.size();
if (num_exe_ctx == 0)
- return;
+ return false;
- result.SetImmediateOutputStream(m_debugger.GetAsyncOutputStream());
- result.SetImmediateErrorStream(m_debugger.GetAsyncErrorStream());
+ StreamSP output_sp = m_debugger.GetAsyncOutputStream();
- bool keep_going = true;
+ bool auto_continue = false;
bool hooks_ran = false;
bool print_hook_header = (m_stop_hooks.size() != 1);
bool print_thread_header = (num_exe_ctx != 1);
- bool did_restart = false;
+ bool should_stop = false;
+ bool somebody_restarted = false;
- for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) {
- // result.Clear();
- StopHookSP cur_hook_sp = (*pos).second;
+ for (auto stop_entry : m_stop_hooks) {
+ StopHookSP cur_hook_sp = stop_entry.second;
if (!cur_hook_sp->IsActive())
continue;
bool any_thread_matched = false;
- for (size_t i = 0; keep_going && i < num_exe_ctx; i++) {
- if ((cur_hook_sp->GetSpecifier() == nullptr ||
- cur_hook_sp->GetSpecifier()->SymbolContextMatches(
- sym_ctx_with_reasons[i])) &&
- (cur_hook_sp->GetThreadSpecifier() == nullptr ||
- cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests(
- exc_ctx_with_reasons[i].GetThreadRef()))) {
- if (!hooks_ran) {
- hooks_ran = true;
- }
- if (print_hook_header && !any_thread_matched) {
- const char *cmd =
- (cur_hook_sp->GetCommands().GetSize() == 1
- ? cur_hook_sp->GetCommands().GetStringAtIndex(0)
- : nullptr);
- if (cmd)
- result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n",
- cur_hook_sp->GetID(), cmd);
- else
- result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n",
- cur_hook_sp->GetID());
- any_thread_matched = true;
- }
+ for (auto exc_ctx : exc_ctx_with_reasons) {
+ // We detect somebody restarted in the stop-hook loop, and broke out of
+ // that loop back to here. So break out of here too.
+ if (somebody_restarted)
+ break;
- if (print_thread_header)
- result.AppendMessageWithFormat(
- "-- Thread %d\n",
- exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID());
+ if (!cur_hook_sp->ExecutionContextPasses(exc_ctx))
+ continue;
- CommandInterpreterRunOptions options;
- options.SetStopOnContinue(true);
- options.SetStopOnError(true);
- options.SetEchoCommands(false);
- options.SetPrintResults(true);
- options.SetPrintErrors(true);
- options.SetAddToHistory(false);
+ // We only consult the auto-continue for a stop hook if it matched the
+ // specifier.
+ auto_continue |= cur_hook_sp->GetAutoContinue();
- // Force Async:
- bool old_async = GetDebugger().GetAsyncExecution();
- GetDebugger().SetAsyncExecution(true);
- GetDebugger().GetCommandInterpreter().HandleCommands(
- cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options,
- result);
- GetDebugger().SetAsyncExecution(old_async);
- // If the command started the target going again, we should bag out of
- // running the stop hooks.
- if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||
- (result.GetStatus() == eReturnStatusSuccessContinuingResult)) {
- // But only complain if there were more stop hooks to do:
- StopHookCollection::iterator tmp = pos;
- if (++tmp != end)
- result.AppendMessageWithFormat(
- "\nAborting stop hooks, hook %" PRIu64
- " set the program running.\n"
- " Consider using '-G true' to make "
- "stop hooks auto-continue.\n",
- cur_hook_sp->GetID());
- keep_going = false;
- did_restart = true;
- }
+ if (!hooks_ran)
+ hooks_ran = true;
+
+ if (print_hook_header && !any_thread_matched) {
+ StreamString s;
+ cur_hook_sp->GetDescription(&s, eDescriptionLevelBrief);
+ if (s.GetSize() != 0)
+ output_sp->Printf("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(),
+ s.GetData());
+ else
+ output_sp->Printf("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID());
+ any_thread_matched = true;
}
+
+ if (print_thread_header)
+ output_sp->Printf("-- Thread %d\n",
+ exc_ctx.GetThreadPtr()->GetIndexID());
+
+ StopHook::StopHookResult this_result =
+ cur_hook_sp->HandleStop(exc_ctx, output_sp);
+ bool this_should_stop = true;
+
+ switch (this_result) {
+ case StopHook::StopHookResult::KeepStopped:
+ // If this hook is set to auto-continue that should override the
+ // HandleStop result...
+ if (cur_hook_sp->GetAutoContinue())
+ this_should_stop = false;
+ else
+ this_should_stop = true;
+
+ break;
+ case StopHook::StopHookResult::RequestContinue:
+ this_should_stop = false;
+ break;
+ case StopHook::StopHookResult::AlreadyContinued:
+ // We don't have a good way to prohibit people from restarting the
+ // target willy nilly in a stop hook. If the hook did so, give a
+ // gentle suggestion here and bag out if the hook processing.
+ output_sp->Printf("\nAborting stop hooks, hook %" PRIu64
+ " set the program running.\n"
+ " Consider using '-G true' to make "
+ "stop hooks auto-continue.\n",
+ cur_hook_sp->GetID());
+ somebody_restarted = true;
+ break;
+ }
+ // If we're already restarted, stop processing stop hooks.
+ // FIXME: if we are doing non-stop mode for real, we would have to
+ // check that OUR thread was restarted, otherwise we should keep
+ // processing stop hooks.
+ if (somebody_restarted)
+ break;
+
+ // If anybody wanted to stop, we should all stop.
+ if (!should_stop)
+ should_stop = this_should_stop;
}
}
+
+ output_sp->Flush();
+
+ // If one of the commands in the stop hook already restarted the target,
+ // report that fact.
+ if (somebody_restarted)
+ return true;
+
// Finally, if auto-continue was requested, do it now:
- if (!did_restart && auto_continue)
- m_process_sp->PrivateResume();
+ // We only compute should_stop against the hook results if a hook got to run
+ // which is why we have to do this conjoint test.
+ if ((hooks_ran && !should_stop) || auto_continue) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ Status error = m_process_sp->PrivateResume();
+ if (error.Success()) {
+ LLDB_LOG(log, "Resuming from RunStopHooks");
+ return true;
+ } else {
+ LLDB_LOG(log, "Resuming from RunStopHooks failed: {0}", error);
+ return false;
+ }
+ }
- result.GetImmediateOutputStream()->Flush();
- result.GetImmediateErrorStream()->Flush();
+ return false;
}
const TargetPropertiesSP &Target::GetGlobalProperties() {
@@ -2882,7 +2974,7 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
} else {
// Use a Process plugin to construct the process.
const char *plugin_name = launch_info.GetProcessPluginName();
- CreateProcess(launch_info.GetListener(), plugin_name, nullptr);
+ CreateProcess(launch_info.GetListener(), plugin_name, nullptr, false);
}
// Since we didn't have a platform launch the process, launch it here.
@@ -2890,80 +2982,79 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
error = m_process_sp->Launch(launch_info);
}
- if (!m_process_sp) {
- if (error.Success())
- error.SetErrorString("failed to launch or debug process");
+ if (!m_process_sp && error.Success())
+ error.SetErrorString("failed to launch or debug process");
+
+ if (!error.Success())
return error;
- }
- if (error.Success()) {
- if (synchronous_execution ||
- !launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) {
- ListenerSP hijack_listener_sp(launch_info.GetHijackListener());
- if (!hijack_listener_sp) {
- hijack_listener_sp =
- Listener::MakeListener("lldb.Target.Launch.hijack");
- launch_info.SetHijackListener(hijack_listener_sp);
- m_process_sp->HijackProcessEvents(hijack_listener_sp);
- }
+ auto at_exit =
+ llvm::make_scope_exit([&]() { m_process_sp->RestoreProcessEvents(); });
- StateType state = m_process_sp->WaitForProcessToStop(
- llvm::None, nullptr, false, hijack_listener_sp, nullptr);
+ if (!synchronous_execution &&
+ launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
+ return error;
- if (state == eStateStopped) {
- if (!launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) {
- if (synchronous_execution) {
- // Now we have handled the stop-from-attach, and we are just
- // switching to a synchronous resume. So we should switch to the
- // SyncResume hijacker.
- m_process_sp->RestoreProcessEvents();
- m_process_sp->ResumeSynchronous(stream);
- } else {
- m_process_sp->RestoreProcessEvents();
- error = m_process_sp->PrivateResume();
- }
- if (!error.Success()) {
- Status error2;
- error2.SetErrorStringWithFormat(
- "process resume at entry point failed: %s", error.AsCString());
- error = error2;
- }
- }
- } else if (state == eStateExited) {
- bool with_shell = !!launch_info.GetShell();
- const int exit_status = m_process_sp->GetExitStatus();
- const char *exit_desc = m_process_sp->GetExitDescription();
-#define LAUNCH_SHELL_MESSAGE \
- "\n'r' and 'run' are aliases that default to launching through a " \
- "shell.\nTry launching without going through a shell by using 'process " \
- "launch'."
- if (exit_desc && exit_desc[0]) {
- if (with_shell)
- error.SetErrorStringWithFormat(
- "process exited with status %i (%s)" LAUNCH_SHELL_MESSAGE,
- exit_status, exit_desc);
- else
- error.SetErrorStringWithFormat("process exited with status %i (%s)",
- exit_status, exit_desc);
- } else {
- if (with_shell)
- error.SetErrorStringWithFormat(
- "process exited with status %i" LAUNCH_SHELL_MESSAGE,
- exit_status);
- else
- error.SetErrorStringWithFormat("process exited with status %i",
- exit_status);
- }
- } else {
- error.SetErrorStringWithFormat(
- "initial process state wasn't stopped: %s", StateAsCString(state));
- }
+ ListenerSP hijack_listener_sp(launch_info.GetHijackListener());
+ if (!hijack_listener_sp) {
+ hijack_listener_sp = Listener::MakeListener("lldb.Target.Launch.hijack");
+ launch_info.SetHijackListener(hijack_listener_sp);
+ m_process_sp->HijackProcessEvents(hijack_listener_sp);
+ }
+
+ switch (m_process_sp->WaitForProcessToStop(llvm::None, nullptr, false,
+ hijack_listener_sp, nullptr)) {
+ case eStateStopped: {
+ if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
+ break;
+ if (synchronous_execution) {
+ // Now we have handled the stop-from-attach, and we are just
+ // switching to a synchronous resume. So we should switch to the
+ // SyncResume hijacker.
+ m_process_sp->RestoreProcessEvents();
+ m_process_sp->ResumeSynchronous(stream);
+ } else {
+ m_process_sp->RestoreProcessEvents();
+ error = m_process_sp->PrivateResume();
+ }
+ if (!error.Success()) {
+ Status error2;
+ error2.SetErrorStringWithFormat(
+ "process resume at entry point failed: %s", error.AsCString());
+ error = error2;
}
- m_process_sp->RestoreProcessEvents();
+ } break;
+ case eStateExited: {
+ bool with_shell = !!launch_info.GetShell();
+ const int exit_status = m_process_sp->GetExitStatus();
+ const char *exit_desc = m_process_sp->GetExitDescription();
+ std::string desc;
+ if (exit_desc && exit_desc[0])
+ desc = " (" + std::string(exit_desc) + ')';
+ if (with_shell)
+ error.SetErrorStringWithFormat(
+ "process exited with status %i%s\n"
+ "'r' and 'run' are aliases that default to launching through a "
+ "shell.\n"
+ "Try launching without going through a shell by using "
+ "'process launch'.",
+ exit_status, desc.c_str());
+ else
+ error.SetErrorStringWithFormat("process exited with status %i%s",
+ exit_status, desc.c_str());
+ } break;
+ default:
+ error.SetErrorStringWithFormat("initial process state wasn't stopped: %s",
+ StateAsCString(state));
+ break;
}
return error;
}
+void Target::SetTrace(const TraceSP &trace_sp) { m_trace_sp = trace_sp; }
+
+const TraceSP &Target::GetTrace() { return m_trace_sp; }
+
Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
auto state = eStateInvalid;
auto process_sp = GetProcessSP();
@@ -3011,7 +3102,7 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
const char *plugin_name = attach_info.GetProcessPluginName();
process_sp =
CreateProcess(attach_info.GetListenerForProcess(GetDebugger()),
- plugin_name, nullptr);
+ plugin_name, nullptr, false);
if (process_sp == nullptr) {
error.SetErrorStringWithFormat(
"failed to create process using plugin %s",
@@ -3128,20 +3219,17 @@ void Target::FinalizeFileActions(ProcessLaunchInfo &info) {
// Target::StopHook
Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid)
- : UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(),
+ : UserID(uid), m_target_sp(target_sp), m_specifier_sp(),
m_thread_spec_up() {}
Target::StopHook::StopHook(const StopHook &rhs)
: UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp),
- m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp),
- m_thread_spec_up(), m_active(rhs.m_active),
- m_auto_continue(rhs.m_auto_continue) {
+ m_specifier_sp(rhs.m_specifier_sp), m_thread_spec_up(),
+ m_active(rhs.m_active), m_auto_continue(rhs.m_auto_continue) {
if (rhs.m_thread_spec_up)
m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
}
-Target::StopHook::~StopHook() = default;
-
void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) {
m_specifier_sp.reset(specifier);
}
@@ -3150,8 +3238,31 @@ void Target::StopHook::SetThreadSpecifier(ThreadSpec *specifier) {
m_thread_spec_up.reset(specifier);
}
+bool Target::StopHook::ExecutionContextPasses(const ExecutionContext &exc_ctx) {
+ SymbolContextSpecifier *specifier = GetSpecifier();
+ if (!specifier)
+ return true;
+
+ bool will_run = true;
+ if (exc_ctx.GetFramePtr())
+ will_run = GetSpecifier()->SymbolContextMatches(
+ exc_ctx.GetFramePtr()->GetSymbolContext(eSymbolContextEverything));
+ if (will_run && GetThreadSpecifier() != nullptr)
+ will_run =
+ GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx.GetThreadRef());
+
+ return will_run;
+}
+
void Target::StopHook::GetDescription(Stream *s,
lldb::DescriptionLevel level) const {
+
+ // For brief descriptions, only print the subclass description:
+ if (level == eDescriptionLevelBrief) {
+ GetSubclassDescription(s, level);
+ return;
+ }
+
unsigned indent_level = s->GetIndentLevel();
s->SetIndentLevel(indent_level + 2);
@@ -3182,15 +3293,155 @@ void Target::StopHook::GetDescription(Stream *s,
s->PutCString("\n");
s->SetIndentLevel(indent_level + 2);
}
+ GetSubclassDescription(s, level);
+}
+void Target::StopHookCommandLine::GetSubclassDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ // The brief description just prints the first command.
+ if (level == eDescriptionLevelBrief) {
+ if (m_commands.GetSize() == 1)
+ s->PutCString(m_commands.GetStringAtIndex(0));
+ return;
+ }
s->Indent("Commands: \n");
- s->SetIndentLevel(indent_level + 4);
+ s->SetIndentLevel(s->GetIndentLevel() + 4);
uint32_t num_commands = m_commands.GetSize();
for (uint32_t i = 0; i < num_commands; i++) {
s->Indent(m_commands.GetStringAtIndex(i));
s->PutCString("\n");
}
- s->SetIndentLevel(indent_level);
+ s->SetIndentLevel(s->GetIndentLevel() - 4);
+}
+
+// Target::StopHookCommandLine
+void Target::StopHookCommandLine::SetActionFromString(const std::string &string) {
+ GetCommands().SplitIntoLines(string);
+}
+
+void Target::StopHookCommandLine::SetActionFromStrings(
+ const std::vector<std::string> &strings) {
+ for (auto string : strings)
+ GetCommands().AppendString(string.c_str());
+}
+
+Target::StopHook::StopHookResult
+Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx,
+ StreamSP output_sp) {
+ assert(exc_ctx.GetTargetPtr() && "Can't call PerformAction on a context "
+ "with no target");
+
+ if (!m_commands.GetSize())
+ return StopHookResult::KeepStopped;
+
+ CommandReturnObject result(false);
+ result.SetImmediateOutputStream(output_sp);
+ result.SetInteractive(false);
+ Debugger &debugger = exc_ctx.GetTargetPtr()->GetDebugger();
+ CommandInterpreterRunOptions options;
+ options.SetStopOnContinue(true);
+ options.SetStopOnError(true);
+ options.SetEchoCommands(false);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+
+ // Force Async:
+ bool old_async = debugger.GetAsyncExecution();
+ debugger.SetAsyncExecution(true);
+ debugger.GetCommandInterpreter().HandleCommands(GetCommands(), &exc_ctx,
+ options, result);
+ debugger.SetAsyncExecution(old_async);
+ lldb::ReturnStatus status = result.GetStatus();
+ if (status == eReturnStatusSuccessContinuingNoResult ||
+ status == eReturnStatusSuccessContinuingResult)
+ return StopHookResult::AlreadyContinued;
+ return StopHookResult::KeepStopped;
+}
+
+// Target::StopHookScripted
+Status Target::StopHookScripted::SetScriptCallback(
+ std::string class_name, StructuredData::ObjectSP extra_args_sp) {
+ Status error;
+
+ ScriptInterpreter *script_interp =
+ GetTarget()->GetDebugger().GetScriptInterpreter();
+ if (!script_interp) {
+ error.SetErrorString("No script interpreter installed.");
+ return error;
+ }
+
+ m_class_name = class_name;
+
+ m_extra_args = new StructuredDataImpl();
+
+ if (extra_args_sp)
+ m_extra_args->SetObjectSP(extra_args_sp);
+
+ m_implementation_sp = script_interp->CreateScriptedStopHook(
+ GetTarget(), m_class_name.c_str(), m_extra_args, error);
+
+ return error;
+}
+
+Target::StopHook::StopHookResult
+Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx,
+ StreamSP output_sp) {
+ assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context "
+ "with no target");
+
+ ScriptInterpreter *script_interp =
+ GetTarget()->GetDebugger().GetScriptInterpreter();
+ if (!script_interp)
+ return StopHookResult::KeepStopped;
+
+ bool should_stop = script_interp->ScriptedStopHookHandleStop(
+ m_implementation_sp, exc_ctx, output_sp);
+
+ return should_stop ? StopHookResult::KeepStopped
+ : StopHookResult::RequestContinue;
+}
+
+void Target::StopHookScripted::GetSubclassDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ if (level == eDescriptionLevelBrief) {
+ s->PutCString(m_class_name);
+ return;
+ }
+ s->Indent("Class:");
+ s->Printf("%s\n", m_class_name.c_str());
+
+ // Now print the extra args:
+ // FIXME: We should use StructuredData.GetDescription on the m_extra_args
+ // but that seems to rely on some printing plugin that doesn't exist.
+ if (!m_extra_args->IsValid())
+ return;
+ StructuredData::ObjectSP object_sp = m_extra_args->GetObjectSP();
+ if (!object_sp || !object_sp->IsValid())
+ return;
+
+ StructuredData::Dictionary *as_dict = object_sp->GetAsDictionary();
+ if (!as_dict || !as_dict->IsValid())
+ return;
+
+ uint32_t num_keys = as_dict->GetSize();
+ if (num_keys == 0)
+ return;
+
+ s->Indent("Args:\n");
+ s->SetIndentLevel(s->GetIndentLevel() + 4);
+
+ auto print_one_element = [&s](ConstString key,
+ StructuredData::Object *object) {
+ s->Indent();
+ s->Printf("%s : %s\n", key.GetCString(),
+ object->GetStringValue().str().c_str());
+ return true;
+ };
+
+ as_dict->ForEach(print_one_element);
+
+ s->SetIndentLevel(s->GetIndentLevel() - 4);
}
static constexpr OptionEnumValueElement g_dynamic_value_types[] = {
@@ -3263,6 +3514,28 @@ static constexpr OptionEnumValueElement g_x86_dis_flavor_value_types[] = {
},
};
+static constexpr OptionEnumValueElement g_import_std_module_value_types[] = {
+ {
+ eImportStdModuleFalse,
+ "false",
+ "Never import the 'std' C++ module in the expression parser.",
+ },
+ {
+ eImportStdModuleFallback,
+ "fallback",
+ "Retry evaluating expressions with an imported 'std' C++ module if they"
+ " failed to parse without the module. This allows evaluating more "
+ "complex expressions involving C++ standard library types."
+ },
+ {
+ eImportStdModuleTrue,
+ "true",
+ "Always import the 'std' C++ module. This allows evaluating more "
+ "complex expressions involving C++ standard library types. This feature"
+ " is experimental."
+ },
+};
+
static constexpr OptionEnumValueElement g_hex_immediate_style_values[] = {
{
Disassembler::eHexStyleC,
@@ -3425,6 +3698,8 @@ TargetProperties::TargetProperties(Target *target)
m_collection_sp->SetValueChangedCallback(
ePropertyDisableASLR, [this] { DisableASLRValueChangedCallback(); });
m_collection_sp->SetValueChangedCallback(
+ ePropertyInheritTCC, [this] { InheritTCCValueChangedCallback(); });
+ m_collection_sp->SetValueChangedCallback(
ePropertyDisableSTDIO, [this] { DisableSTDIOValueChangedCallback(); });
m_experimental_properties_up =
@@ -3462,6 +3737,7 @@ void TargetProperties::UpdateLaunchInfoFromProperties() {
ErrorPathValueChangedCallback();
DetachOnErrorValueChangedCallback();
DisableASLRValueChangedCallback();
+ InheritTCCValueChangedCallback();
DisableSTDIOValueChangedCallback();
}
@@ -3544,6 +3820,17 @@ void TargetProperties::SetDisableASLR(bool b) {
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
}
+bool TargetProperties::GetInheritTCC() const {
+ const uint32_t idx = ePropertyInheritTCC;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_target_properties[idx].default_uint_value != 0);
+}
+
+void TargetProperties::SetInheritTCC(bool b) {
+ const uint32_t idx = ePropertyInheritTCC;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
bool TargetProperties::GetDetachOnError() const {
const uint32_t idx = ePropertyDetachOnError;
return m_collection_sp->GetPropertyAtIndexAsBoolean(
@@ -3702,10 +3989,10 @@ bool TargetProperties::GetEnableAutoImportClangModules() const {
nullptr, idx, g_target_properties[idx].default_uint_value != 0);
}
-bool TargetProperties::GetEnableImportStdModule() const {
+ImportStdModule TargetProperties::GetImportStdModule() const {
const uint32_t idx = ePropertyImportStdModule;
- return m_collection_sp->GetPropertyAtIndexAsBoolean(
- nullptr, idx, g_target_properties[idx].default_uint_value != 0);
+ return (ImportStdModule)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_target_properties[idx].default_uint_value);
}
bool TargetProperties::GetEnableAutoApplyFixIts() const {
@@ -3816,6 +4103,12 @@ llvm::StringRef TargetProperties::GetExpressionPrefixContents() {
return "";
}
+uint64_t TargetProperties::GetExprErrorLimit() const {
+ const uint32_t idx = ePropertyExprErrorLimit;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_target_properties[idx].default_uint_value);
+}
+
bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() {
const uint32_t idx = ePropertyBreakpointUseAvoidList;
return m_collection_sp->GetPropertyAtIndexAsBoolean(
@@ -3935,6 +4228,8 @@ void TargetProperties::SetProcessLaunchInfo(
}
SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError));
SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR));
+ SetInheritTCC(
+ launch_info.GetFlags().Test(lldb::eLaunchFlagInheritTCCFromParent));
SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO));
}
@@ -3998,6 +4293,13 @@ void TargetProperties::DisableASLRValueChangedCallback() {
m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableASLR);
}
+void TargetProperties::InheritTCCValueChangedCallback() {
+ if (GetInheritTCC())
+ m_launch_info.GetFlags().Set(lldb::eLaunchFlagInheritTCCFromParent);
+ else
+ m_launch_info.GetFlags().Clear(lldb::eLaunchFlagInheritTCCFromParent);
+}
+
void TargetProperties::DisableSTDIOValueChangedCallback() {
if (GetDisableSTDIO())
m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO);