diff options
Diffstat (limited to 'lldb/source/Core/Debugger.cpp')
-rw-r--r-- | lldb/source/Core/Debugger.cpp | 265 |
1 files changed, 125 insertions, 140 deletions
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 33f72a0896cbd..5f4f1e266d81c 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1,4 +1,4 @@ -//===-- Debugger.cpp --------------------------------------------*- C++ -*-===// +//===-- Debugger.cpp ------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -135,84 +135,6 @@ static constexpr OptionEnumValueElement g_language_enumerators[] = { }, }; -#define MODULE_WITH_FUNC \ - "{ " \ - "${module.file.basename}{`${function.name-with-args}" \ - "{${frame.no-debug}${function.pc-offset}}}}" - -#define MODULE_WITH_FUNC_NO_ARGS \ - "{ " \ - "${module.file.basename}{`${function.name-without-args}" \ - "{${frame.no-debug}${function.pc-offset}}}}" - -#define FILE_AND_LINE \ - "{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}" \ - ":${ansi.fg.yellow}${line.number}${ansi.normal}" \ - "{:${ansi.fg.yellow}${line.column}${ansi.normal}}}" - -#define IS_OPTIMIZED "{${function.is-optimized} [opt]}" - -#define IS_ARTIFICIAL "{${frame.is-artificial} [artificial]}" - -#define DEFAULT_THREAD_FORMAT \ - "thread #${thread.index}: tid = ${thread.id%tid}" \ - "{, ${frame.pc}}" MODULE_WITH_FUNC FILE_AND_LINE \ - "{, name = ${ansi.fg.green}'${thread.name}'${ansi.normal}}" \ - "{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}" \ - "{, activity = " \ - "${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}" \ - "{, ${thread.info.trace_messages} messages}" \ - "{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}" \ - "{\\nReturn value: ${thread.return-value}}" \ - "{\\nCompleted expression: ${thread.completed-expression}}" \ - "\\n" - -#define DEFAULT_THREAD_STOP_FORMAT \ - "thread #${thread.index}{, name = '${thread.name}'}" \ - "{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}" \ - "{, activity = " \ - "${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}" \ - "{, ${thread.info.trace_messages} messages}" \ - "{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}" \ - "{\\nReturn value: ${thread.return-value}}" \ - "{\\nCompleted expression: ${thread.completed-expression}}" \ - "\\n" - -#define DEFAULT_FRAME_FORMAT \ - "frame #${frame.index}: " \ - "${ansi.fg.yellow}${frame.pc}${ansi.normal}" MODULE_WITH_FUNC FILE_AND_LINE \ - IS_OPTIMIZED IS_ARTIFICIAL "\\n" - -#define DEFAULT_FRAME_FORMAT_NO_ARGS \ - "frame #${frame.index}: " \ - "${ansi.fg.yellow}${frame.pc}${ansi.normal}" MODULE_WITH_FUNC_NO_ARGS \ - FILE_AND_LINE IS_OPTIMIZED IS_ARTIFICIAL "\\n" - -// Three parts to this disassembly format specification: -// 1. If this is a new function/symbol (no previous symbol/function), print -// dylib`funcname:\n -// 2. If this is a symbol context change (different from previous -// symbol/function), print -// dylib`funcname:\n -// 3. print -// address <+offset>: -#define DEFAULT_DISASSEMBLY_FORMAT \ - "{${function.initial-function}{${module.file.basename}`}{${function.name-" \ - "without-args}}:\\n}{${function.changed}\\n{${module.file.basename}`}{${" \ - "function.name-without-args}}:\\n}{${current-pc-arrow} " \ - "}${addr-file-or-load}{ " \ - "<${function.concrete-only-addr-offset-no-padding}>}: " - -// gdb's disassembly format can be emulated with ${current-pc-arrow}${addr- -// file-or-load}{ <${function.name-without-args}${function.concrete-only-addr- -// offset-no-padding}>}: - -// lldb's original format for disassembly would look like this format string - -// {${function.initial-function}{${module.file.basename}`}{${function.name- -// without- -// args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name- -// without-args}}:\n}{${current-pc-arrow} }{${addr-file-or-load}}: - static constexpr OptionEnumValueElement s_stop_show_column_values[] = { { eStopShowColumnAnsiOrCaret, @@ -290,6 +212,11 @@ Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx, // use-color changed. Ping the prompt so it can reset the ansi terminal // codes. SetPrompt(GetPrompt()); + } else if (property_path == g_debugger_properties[ePropertyUseSourceCache].name) { + // use-source-cache changed. Wipe out the cache contents if it was disabled. + if (!GetUseSourceCache()) { + m_source_file_cache.Clear(); + } } else if (is_load_script && target_sp && load_script_old_value == eLoadScriptFromSymFileWarn) { if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == @@ -388,6 +315,9 @@ uint32_t Debugger::GetTerminalWidth() const { } bool Debugger::SetTerminalWidth(uint32_t term_width) { + if (auto handler_sp = m_io_handler_stack.Top()) + handler_sp->TerminalSizeChanged(); + const uint32_t idx = ePropertyTerminalWidth; return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width); } @@ -416,6 +346,20 @@ bool Debugger::SetUseColor(bool b) { return ret; } +bool Debugger::GetUseSourceCache() const { + const uint32_t idx = ePropertyUseSourceCache; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_debugger_properties[idx].default_uint_value != 0); +} + +bool Debugger::SetUseSourceCache(bool b) { + const uint32_t idx = ePropertyUseSourceCache; + bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + if (!ret) { + m_source_file_cache.Clear(); + } + return ret; +} bool Debugger::GetHighlightSource() const { const uint32_t idx = ePropertyHighlightSource; return m_collection_sp->GetPropertyAtIndexAsBoolean( @@ -438,6 +382,16 @@ llvm::StringRef Debugger::GetStopShowColumnAnsiSuffix() const { return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); } +llvm::StringRef Debugger::GetStopShowLineMarkerAnsiPrefix() const { + const uint32_t idx = ePropertyStopShowLineMarkerAnsiPrefix; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); +} + +llvm::StringRef Debugger::GetStopShowLineMarkerAnsiSuffix() const { + const uint32_t idx = ePropertyStopShowLineMarkerAnsiSuffix; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); +} + uint32_t Debugger::GetStopSourceLineCount(bool before) const { const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter; @@ -708,7 +662,7 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) m_source_manager_up(), m_source_file_cache(), m_command_interpreter_up( std::make_unique<CommandInterpreter>(*this, false)), - m_input_reader_stack(), m_instance_name(), m_loaded_plugins(), + m_io_handler_stack(), m_instance_name(), m_loaded_plugins(), m_event_handler_thread(), m_io_handler_thread(), m_sync_broadcaster(nullptr, "lldb.debugger.sync"), m_forward_listener_sp(), m_clear_once() { @@ -870,15 +824,15 @@ ExecutionContext Debugger::GetSelectedExecutionContext() { } void Debugger::DispatchInputInterrupt() { - std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex()); - IOHandlerSP reader_sp(m_input_reader_stack.Top()); + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); + IOHandlerSP reader_sp(m_io_handler_stack.Top()); if (reader_sp) reader_sp->Interrupt(); } void Debugger::DispatchInputEndOfFile() { - std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex()); - IOHandlerSP reader_sp(m_input_reader_stack.Top()); + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); + IOHandlerSP reader_sp(m_io_handler_stack.Top()); if (reader_sp) reader_sp->GotEOF(); } @@ -886,81 +840,107 @@ void Debugger::DispatchInputEndOfFile() { void Debugger::ClearIOHandlers() { // The bottom input reader should be the main debugger input reader. We do // not want to close that one here. - std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex()); - while (m_input_reader_stack.GetSize() > 1) { - IOHandlerSP reader_sp(m_input_reader_stack.Top()); + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); + while (m_io_handler_stack.GetSize() > 1) { + IOHandlerSP reader_sp(m_io_handler_stack.Top()); if (reader_sp) PopIOHandler(reader_sp); } } -void Debugger::ExecuteIOHandlers() { +void Debugger::RunIOHandlers() { + IOHandlerSP reader_sp = m_io_handler_stack.Top(); while (true) { - IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (!reader_sp) break; reader_sp->Run(); + { + std::lock_guard<std::recursive_mutex> guard( + m_io_handler_synchronous_mutex); + + // Remove all input readers that are done from the top of the stack + while (true) { + IOHandlerSP top_reader_sp = m_io_handler_stack.Top(); + if (top_reader_sp && top_reader_sp->GetIsDone()) + PopIOHandler(top_reader_sp); + else + break; + } + reader_sp = m_io_handler_stack.Top(); + } + } + ClearIOHandlers(); +} + +void Debugger::RunIOHandlerSync(const IOHandlerSP &reader_sp) { + std::lock_guard<std::recursive_mutex> guard(m_io_handler_synchronous_mutex); + + PushIOHandler(reader_sp); + IOHandlerSP top_reader_sp = reader_sp; + + while (top_reader_sp) { + if (!top_reader_sp) + break; - // Remove all input readers that are done from the top of the stack + top_reader_sp->Run(); + + // Don't unwind past the starting point. + if (top_reader_sp.get() == reader_sp.get()) { + if (PopIOHandler(reader_sp)) + break; + } + + // If we pushed new IO handlers, pop them if they're done or restart the + // loop to run them if they're not. while (true) { - IOHandlerSP top_reader_sp = m_input_reader_stack.Top(); - if (top_reader_sp && top_reader_sp->GetIsDone()) + top_reader_sp = m_io_handler_stack.Top(); + if (top_reader_sp && top_reader_sp->GetIsDone()) { PopIOHandler(top_reader_sp); - else + // Don't unwind past the starting point. + if (top_reader_sp.get() == reader_sp.get()) + return; + } else { break; + } } } - ClearIOHandlers(); } bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) { - return m_input_reader_stack.IsTop(reader_sp); + return m_io_handler_stack.IsTop(reader_sp); } bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type, IOHandler::Type second_top_type) { - return m_input_reader_stack.CheckTopIOHandlerTypes(top_type, second_top_type); + return m_io_handler_stack.CheckTopIOHandlerTypes(top_type, second_top_type); } void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) { lldb_private::StreamFile &stream = is_stdout ? GetOutputStream() : GetErrorStream(); - m_input_reader_stack.PrintAsync(&stream, s, len); + m_io_handler_stack.PrintAsync(&stream, s, len); } ConstString Debugger::GetTopIOHandlerControlSequence(char ch) { - return m_input_reader_stack.GetTopIOHandlerControlSequence(ch); + return m_io_handler_stack.GetTopIOHandlerControlSequence(ch); } const char *Debugger::GetIOHandlerCommandPrefix() { - return m_input_reader_stack.GetTopIOHandlerCommandPrefix(); + return m_io_handler_stack.GetTopIOHandlerCommandPrefix(); } const char *Debugger::GetIOHandlerHelpPrologue() { - return m_input_reader_stack.GetTopIOHandlerHelpPrologue(); + return m_io_handler_stack.GetTopIOHandlerHelpPrologue(); } -void Debugger::RunIOHandler(const IOHandlerSP &reader_sp) { - PushIOHandler(reader_sp); - - IOHandlerSP top_reader_sp = reader_sp; - while (top_reader_sp) { - top_reader_sp->Run(); - - if (top_reader_sp.get() == reader_sp.get()) { - if (PopIOHandler(reader_sp)) - break; - } +bool Debugger::RemoveIOHandler(const IOHandlerSP &reader_sp) { + return PopIOHandler(reader_sp); +} - while (true) { - top_reader_sp = m_input_reader_stack.Top(); - if (top_reader_sp && top_reader_sp->GetIsDone()) - PopIOHandler(top_reader_sp); - else - break; - } - } +void Debugger::RunIOHandlerAsync(const IOHandlerSP &reader_sp, + bool cancel_top_handler) { + PushIOHandler(reader_sp, cancel_top_handler); } void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out, @@ -970,8 +950,8 @@ void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out, // input reader's in/out/err streams, or fall back to the debugger file // handles, or we fall back onto stdin/stdout/stderr as a last resort. - std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex()); - IOHandlerSP top_reader_sp(m_input_reader_stack.Top()); + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); + IOHandlerSP top_reader_sp(m_io_handler_stack.Top()); // If no STDIN has been set, then set it appropriately if (!in || !in->IsValid()) { if (top_reader_sp) @@ -1009,17 +989,17 @@ void Debugger::PushIOHandler(const IOHandlerSP &reader_sp, if (!reader_sp) return; - std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); // Get the current top input reader... - IOHandlerSP top_reader_sp(m_input_reader_stack.Top()); + IOHandlerSP top_reader_sp(m_io_handler_stack.Top()); // Don't push the same IO handler twice... if (reader_sp == top_reader_sp) return; // Push our new input reader - m_input_reader_stack.Push(reader_sp); + m_io_handler_stack.Push(reader_sp); reader_sp->Activate(); // Interrupt the top input reader to it will exit its Run() function and let @@ -1035,23 +1015,23 @@ bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) { if (!pop_reader_sp) return false; - std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); // The reader on the stop of the stack is done, so let the next read on the // stack refresh its prompt and if there is one... - if (m_input_reader_stack.IsEmpty()) + if (m_io_handler_stack.IsEmpty()) return false; - IOHandlerSP reader_sp(m_input_reader_stack.Top()); + IOHandlerSP reader_sp(m_io_handler_stack.Top()); if (pop_reader_sp != reader_sp) return false; reader_sp->Deactivate(); reader_sp->Cancel(); - m_input_reader_stack.Pop(); + m_io_handler_stack.Pop(); - reader_sp = m_input_reader_stack.Top(); + reader_sp = m_io_handler_stack.Top(); if (reader_sp) reader_sp->Activate(); @@ -1174,17 +1154,22 @@ bool Debugger::EnableLog(llvm::StringRef channel, if (pos != m_log_streams.end()) log_stream_sp = pos->second.lock(); if (!log_stream_sp) { - llvm::sys::fs::OpenFlags flags = llvm::sys::fs::OF_Text; + File::OpenOptions flags = + File::eOpenOptionWrite | File::eOpenOptionCanCreate; if (log_options & LLDB_LOG_OPTION_APPEND) - flags |= llvm::sys::fs::OF_Append; - int FD; - if (std::error_code ec = llvm::sys::fs::openFileForWrite( - log_file, FD, llvm::sys::fs::CD_CreateAlways, flags)) { - error_stream << "Unable to open log file: " << ec.message(); + flags |= File::eOpenOptionAppend; + else + flags |= File::eOpenOptionTruncate; + auto file = FileSystem::Instance().Open( + FileSpec(log_file), flags, lldb::eFilePermissionsFileDefault, false); + if (!file) { + // FIXME: This gets garbled when called from the log command. + error_stream << "Unable to open log file: " << log_file; return false; } - log_stream_sp = - std::make_shared<llvm::raw_fd_ostream>(FD, should_close, unbuffered); + + log_stream_sp = std::make_shared<llvm::raw_fd_ostream>( + (*file)->GetDescriptor(), should_close, unbuffered); m_log_streams[log_file] = log_stream_sp; } } @@ -1503,9 +1488,9 @@ bool Debugger::StartEventHandlerThread() { listener_sp->StartListeningForEvents(&m_sync_broadcaster, eBroadcastBitEventThreadIsListening); - auto thread_name = + llvm::StringRef thread_name = full_name.GetLength() < llvm::get_max_thread_name_length() - ? full_name.AsCString() + ? full_name.GetStringRef() : "dbg.evt-handler"; // Use larger 8MB stack for this thread @@ -1542,7 +1527,7 @@ void Debugger::StopEventHandlerThread() { lldb::thread_result_t Debugger::IOHandlerThread(lldb::thread_arg_t arg) { Debugger *debugger = (Debugger *)arg; - debugger->ExecuteIOHandlers(); + debugger->RunIOHandlers(); debugger->StopEventHandlerThread(); return {}; } |