diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
| commit | b60736ec1405bb0a8dd40989f67ef4c93da068ab (patch) | |
| tree | 5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /lldb/source/Target/Target.cpp | |
| parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) | |
Diffstat (limited to 'lldb/source/Target/Target.cpp')
| -rw-r--r-- | lldb/source/Target/Target.cpp | 770 |
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); |
