diff options
Diffstat (limited to 'lldb/source/Core/Debugger.cpp')
| -rw-r--r-- | lldb/source/Core/Debugger.cpp | 357 |
1 files changed, 276 insertions, 81 deletions
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 49cc21b65951..f17cd8856a6d 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -9,6 +9,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/DebuggerEvents.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/ModuleList.h" @@ -43,13 +44,13 @@ #include "lldb/Target/ThreadList.h" #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/Event.h" +#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Listener.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Reproducer.h" #include "lldb/Utility/ReproducerProvider.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" -#include "lldb/Utility/StreamCallback.h" #include "lldb/Utility/StreamString.h" #if defined(_WIN32) @@ -64,6 +65,7 @@ #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Process.h" +#include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" @@ -377,12 +379,44 @@ bool Debugger::SetUseColor(bool b) { return ret; } +bool Debugger::GetShowProgress() const { + const uint32_t idx = ePropertyShowProgress; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_debugger_properties[idx].default_uint_value != 0); +} + +bool Debugger::SetShowProgress(bool show_progress) { + const uint32_t idx = ePropertyShowProgress; + return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, + show_progress); +} + +llvm::StringRef Debugger::GetShowProgressAnsiPrefix() const { + const uint32_t idx = ePropertyShowProgressAnsiPrefix; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); +} + +llvm::StringRef Debugger::GetShowProgressAnsiSuffix() const { + const uint32_t idx = ePropertyShowProgressAnsiSuffix; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); +} + bool Debugger::GetUseAutosuggestion() const { const uint32_t idx = ePropertyShowAutosuggestion; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_debugger_properties[idx].default_uint_value != 0); } +llvm::StringRef Debugger::GetAutosuggestionAnsiPrefix() const { + const uint32_t idx = ePropertyShowAutosuggestionAnsiPrefix; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); +} + +llvm::StringRef Debugger::GetAutosuggestionAnsiSuffix() const { + const uint32_t idx = ePropertyShowAutosuggestionAnsiSuffix; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, ""); +} + bool Debugger::GetUseSourceCache() const { const uint32_t idx = ePropertyUseSourceCache; return m_collection_sp->GetPropertyAtIndexAsBoolean( @@ -635,9 +669,9 @@ void Debugger::Destroy(DebuggerSP &debugger_sp) { CommandReturnObject result(debugger_sp->GetUseColor()); cmd_interpreter.SaveTranscript(result); if (result.Succeeded()) - debugger_sp->GetOutputStream() << result.GetOutputData() << '\n'; + (*debugger_sp->GetAsyncOutputStream()) << result.GetOutputData() << '\n'; else - debugger_sp->GetErrorStream() << result.GetErrorData() << '\n'; + (*debugger_sp->GetAsyncErrorStream()) << result.GetErrorData() << '\n'; } debugger_sp->Clear(); @@ -723,8 +757,8 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) m_forward_listener_sp(), m_clear_once() { m_instance_name.SetString(llvm::formatv("debugger_{0}", GetID()).str()); if (log_callback) - m_log_callback_stream_sp = - std::make_shared<StreamCallback>(log_callback, baton); + m_callback_handler_sp = + std::make_shared<CallbackLogHandler>(log_callback, baton); m_command_interpreter_up->Initialize(); // Always add our default platform to the platform list PlatformSP default_platform_sp(Platform::GetHostPlatform()); @@ -865,7 +899,8 @@ Status Debugger::SetInputString(const char *data) { return result; } - write(fds[WRITE], data, size); + int r = write(fds[WRITE], data, size); + (void)r; // Close the write end of the pipe, so that the command interpreter will exit // when it consumes all the data. llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); @@ -1041,9 +1076,12 @@ bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type, } void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) { - lldb_private::StreamFile &stream = - is_stdout ? GetOutputStream() : GetErrorStream(); - m_io_handler_stack.PrintAsync(&stream, s, len); + bool printed = m_io_handler_stack.PrintAsync(s, len, is_stdout); + if (!printed) { + lldb::StreamFileSP stream = + is_stdout ? m_output_stream_sp : m_error_stream_sp; + stream->Write(s, len); + } } ConstString Debugger::GetTopIOHandlerControlSequence(char ch) { @@ -1163,11 +1201,11 @@ bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) { } StreamSP Debugger::GetAsyncOutputStream() { - return std::make_shared<StreamAsynchronousIO>(*this, true); + return std::make_shared<StreamAsynchronousIO>(*this, true, GetUseColor()); } StreamSP Debugger::GetAsyncErrorStream() { - return std::make_shared<StreamAsynchronousIO>(*this, false); + return std::make_shared<StreamAsynchronousIO>(*this, false, GetUseColor()); } size_t Debugger::GetNumDebuggers() { @@ -1253,38 +1291,8 @@ void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback, // For simplicity's sake, I am not going to deal with how to close down any // open logging streams, I just redirect everything from here on out to the // callback. - m_log_callback_stream_sp = - std::make_shared<StreamCallback>(log_callback, baton); -} - -ConstString Debugger::ProgressEventData::GetFlavorString() { - static ConstString g_flavor("Debugger::ProgressEventData"); - return g_flavor; -} - -ConstString Debugger::ProgressEventData::GetFlavor() const { - return Debugger::ProgressEventData::GetFlavorString(); -} - -void Debugger::ProgressEventData::Dump(Stream *s) const { - s->Printf(" id = %" PRIu64 ", message = \"%s\"", m_id, m_message.c_str()); - if (m_completed == 0 || m_completed == m_total) - s->Printf(", type = %s", m_completed == 0 ? "start" : "end"); - else - s->PutCString(", type = update"); - // If m_total is UINT64_MAX, there is no progress to report, just "start" - // and "end". If it isn't we will show the completed and total amounts. - if (m_total != UINT64_MAX) - s->Printf(", progress = %" PRIu64 " of %" PRIu64, m_completed, m_total); -} - -const Debugger::ProgressEventData * -Debugger::ProgressEventData::GetEventDataFromEvent(const Event *event_ptr) { - if (event_ptr) - if (const EventData *event_data = event_ptr->GetData()) - if (event_data->GetFlavor() == ProgressEventData::GetFlavorString()) - return static_cast<const ProgressEventData *>(event_ptr->GetData()); - return nullptr; + m_callback_handler_sp = + std::make_shared<CallbackLogHandler>(log_callback, baton); } static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id, @@ -1295,9 +1303,9 @@ static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id, const uint32_t event_type = Debugger::eBroadcastBitProgress; if (!debugger.GetBroadcaster().EventTypeHasListeners(event_type)) return; - EventSP event_sp(new Event(event_type, new Debugger::ProgressEventData( - progress_id, message, completed, - total, is_debugger_specific))); + EventSP event_sp(new Event( + event_type, new ProgressEventData(progress_id, message, completed, total, + is_debugger_specific))); debugger.GetBroadcaster().BroadcastEvent(event_sp); } @@ -1305,7 +1313,7 @@ void Debugger::ReportProgress(uint64_t progress_id, const std::string &message, uint64_t completed, uint64_t total, llvm::Optional<lldb::user_id_t> debugger_id) { // Check if this progress is for a specific debugger. - if (debugger_id.hasValue()) { + if (debugger_id) { // It is debugger specific, grab it and deliver the event if the debugger // still exists. DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id); @@ -1325,27 +1333,116 @@ void Debugger::ReportProgress(uint64_t progress_id, const std::string &message, } } +static void PrivateReportDiagnostic(Debugger &debugger, + DiagnosticEventData::Type type, + std::string message, + bool debugger_specific) { + uint32_t event_type = 0; + switch (type) { + case DiagnosticEventData::Type::Warning: + event_type = Debugger::eBroadcastBitWarning; + break; + case DiagnosticEventData::Type::Error: + event_type = Debugger::eBroadcastBitError; + break; + } + + Broadcaster &broadcaster = debugger.GetBroadcaster(); + if (!broadcaster.EventTypeHasListeners(event_type)) { + // Diagnostics are too important to drop. If nobody is listening, print the + // diagnostic directly to the debugger's error stream. + DiagnosticEventData event_data(type, std::move(message), debugger_specific); + StreamSP stream = debugger.GetAsyncErrorStream(); + event_data.Dump(stream.get()); + return; + } + EventSP event_sp = std::make_shared<Event>( + event_type, + new DiagnosticEventData(type, std::move(message), debugger_specific)); + broadcaster.BroadcastEvent(event_sp); +} + +void Debugger::ReportDiagnosticImpl(DiagnosticEventData::Type type, + std::string message, + llvm::Optional<lldb::user_id_t> debugger_id, + std::once_flag *once) { + auto ReportDiagnosticLambda = [&]() { + // Check if this progress is for a specific debugger. + if (debugger_id) { + // It is debugger specific, grab it and deliver the event if the debugger + // still exists. + DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id); + if (debugger_sp) + PrivateReportDiagnostic(*debugger_sp, type, std::move(message), true); + return; + } + // The progress event is not debugger specific, iterate over all debuggers + // and deliver a progress event to each one. + if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { + std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); + for (const auto &debugger : *g_debugger_list_ptr) + PrivateReportDiagnostic(*debugger, type, message, false); + } + }; + + if (once) + std::call_once(*once, ReportDiagnosticLambda); + else + ReportDiagnosticLambda(); +} + +void Debugger::ReportWarning(std::string message, + llvm::Optional<lldb::user_id_t> debugger_id, + std::once_flag *once) { + ReportDiagnosticImpl(DiagnosticEventData::Type::Warning, std::move(message), + debugger_id, once); +} + +void Debugger::ReportError(std::string message, + llvm::Optional<lldb::user_id_t> debugger_id, + std::once_flag *once) { + + ReportDiagnosticImpl(DiagnosticEventData::Type::Error, std::move(message), + debugger_id, once); +} + +static std::shared_ptr<LogHandler> +CreateLogHandler(LogHandlerKind log_handler_kind, int fd, bool should_close, + size_t buffer_size) { + switch (log_handler_kind) { + case eLogHandlerStream: + return std::make_shared<StreamLogHandler>(fd, should_close, buffer_size); + case eLogHandlerCircular: + return std::make_shared<RotatingLogHandler>(buffer_size); + case eLogHandlerSystem: + return std::make_shared<SystemLogHandler>(); + case eLogHandlerCallback: + return {}; + } + return {}; +} + bool Debugger::EnableLog(llvm::StringRef channel, llvm::ArrayRef<const char *> categories, llvm::StringRef log_file, uint32_t log_options, + size_t buffer_size, LogHandlerKind log_handler_kind, llvm::raw_ostream &error_stream) { - const bool should_close = true; - const bool unbuffered = true; - std::shared_ptr<llvm::raw_ostream> log_stream_sp; - if (m_log_callback_stream_sp) { - log_stream_sp = m_log_callback_stream_sp; + std::shared_ptr<LogHandler> log_handler_sp; + if (m_callback_handler_sp) { + log_handler_sp = m_callback_handler_sp; // For now when using the callback mode you always get thread & timestamp. log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; } else if (log_file.empty()) { - log_stream_sp = std::make_shared<llvm::raw_fd_ostream>( - GetOutputFile().GetDescriptor(), !should_close, unbuffered); + log_handler_sp = + CreateLogHandler(log_handler_kind, GetOutputFile().GetDescriptor(), + /*should_close=*/false, buffer_size); } else { - auto pos = m_log_streams.find(log_file); - if (pos != m_log_streams.end()) - log_stream_sp = pos->second.lock(); - if (!log_stream_sp) { + auto pos = m_stream_handlers.find(log_file); + if (pos != m_stream_handlers.end()) + log_handler_sp = pos->second.lock(); + if (!log_handler_sp) { File::OpenOptions flags = File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate; if (log_options & LLDB_LOG_OPTION_APPEND) @@ -1360,18 +1457,18 @@ bool Debugger::EnableLog(llvm::StringRef channel, return false; } - log_stream_sp = std::make_shared<llvm::raw_fd_ostream>( - (*file)->GetDescriptor(), should_close, unbuffered); - m_log_streams[log_file] = log_stream_sp; + log_handler_sp = + CreateLogHandler(log_handler_kind, (*file)->GetDescriptor(), + /*should_close=*/true, buffer_size); + m_stream_handlers[log_file] = log_handler_sp; } } - assert(log_stream_sp); + assert(log_handler_sp); if (log_options == 0) - log_options = - LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; + log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME; - return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories, + return Log::EnableLogChannel(log_handler_sp, log_options, channel, categories, error_stream); } @@ -1575,7 +1672,7 @@ void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) { m_forward_listener_sp.reset(); } -void Debugger::DefaultEventHandler() { +lldb::thread_result_t Debugger::DefaultEventHandler() { ListenerSP listener_sp(GetListener()); ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); @@ -1604,6 +1701,10 @@ void Debugger::DefaultEventHandler() { CommandInterpreter::eBroadcastBitAsynchronousOutputData | CommandInterpreter::eBroadcastBitAsynchronousErrorData); + listener_sp->StartListeningForEvents( + &m_broadcaster, + eBroadcastBitProgress | eBroadcastBitWarning | eBroadcastBitError); + // Let the thread that spawned us know that we have started up and that we // are now listening to all required events so no events get missed m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening); @@ -1653,6 +1754,13 @@ void Debugger::DefaultEventHandler() { } } } + } else if (broadcaster == &m_broadcaster) { + if (event_type & Debugger::eBroadcastBitProgress) + HandleProgressEvent(event_sp); + else if (event_type & Debugger::eBroadcastBitWarning) + HandleDiagnosticEvent(event_sp); + else if (event_type & Debugger::eBroadcastBitError) + HandleDiagnosticEvent(event_sp); } } @@ -1661,10 +1769,6 @@ void Debugger::DefaultEventHandler() { } } } -} - -lldb::thread_result_t Debugger::EventHandlerThread(lldb::thread_arg_t arg) { - ((Debugger *)arg)->DefaultEventHandler(); return {}; } @@ -1686,14 +1790,14 @@ bool Debugger::StartEventHandlerThread() { // Use larger 8MB stack for this thread llvm::Expected<HostThread> event_handler_thread = - ThreadLauncher::LaunchThread(thread_name, EventHandlerThread, this, - g_debugger_event_thread_stack_bytes); + ThreadLauncher::LaunchThread( + thread_name, [this] { return DefaultEventHandler(); }, + g_debugger_event_thread_stack_bytes); if (event_handler_thread) { m_event_handler_thread = *event_handler_thread; } else { - LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), - "failed to launch host thread: {}", + LLDB_LOG(GetLog(LLDBLog::Host), "failed to launch host thread: {}", llvm::toString(event_handler_thread.takeError())); } @@ -1716,25 +1820,106 @@ void Debugger::StopEventHandlerThread() { } } -lldb::thread_result_t Debugger::IOHandlerThread(lldb::thread_arg_t arg) { - Debugger *debugger = (Debugger *)arg; - debugger->RunIOHandlers(); - debugger->StopEventHandlerThread(); +lldb::thread_result_t Debugger::IOHandlerThread() { + RunIOHandlers(); + StopEventHandlerThread(); return {}; } +void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { + auto *data = ProgressEventData::GetEventDataFromEvent(event_sp.get()); + if (!data) + return; + + // Do some bookkeeping for the current event, regardless of whether we're + // going to show the progress. + const uint64_t id = data->GetID(); + if (m_current_event_id) { + if (id != *m_current_event_id) + return; + if (data->GetCompleted()) + m_current_event_id.reset(); + } else { + m_current_event_id = id; + } + + // Decide whether we actually are going to show the progress. This decision + // can change between iterations so check it inside the loop. + if (!GetShowProgress()) + return; + + // Determine whether the current output file is an interactive terminal with + // color support. We assume that if we support ANSI escape codes we support + // vt100 escape codes. + File &file = GetOutputFile(); + if (!file.GetIsInteractive() || !file.GetIsTerminalWithColors()) + return; + + StreamSP output = GetAsyncOutputStream(); + + // Print over previous line, if any. + output->Printf("\r"); + + if (data->GetCompleted()) { + // Clear the current line. + output->Printf("\x1B[2K"); + output->Flush(); + return; + } + + // Trim the progress message if it exceeds the window's width and print it. + std::string message = data->GetMessage(); + if (data->IsFinite()) + message = llvm::formatv("[{0}/{1}] {2}", data->GetCompleted(), + data->GetTotal(), message) + .str(); + + // Trim the progress message if it exceeds the window's width and print it. + const uint32_t term_width = GetTerminalWidth(); + const uint32_t ellipsis = 3; + if (message.size() + ellipsis >= term_width) + message = message.substr(0, term_width - ellipsis); + + const bool use_color = GetUseColor(); + llvm::StringRef ansi_prefix = GetShowProgressAnsiPrefix(); + if (!ansi_prefix.empty()) + output->Printf( + "%s", ansi::FormatAnsiTerminalCodes(ansi_prefix, use_color).c_str()); + + output->Printf("%s...", message.c_str()); + + llvm::StringRef ansi_suffix = GetShowProgressAnsiSuffix(); + if (!ansi_suffix.empty()) + output->Printf( + "%s", ansi::FormatAnsiTerminalCodes(ansi_suffix, use_color).c_str()); + + // Clear until the end of the line. + output->Printf("\x1B[K\r"); + + // Flush the output. + output->Flush(); +} + +void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) { + auto *data = DiagnosticEventData::GetEventDataFromEvent(event_sp.get()); + if (!data) + return; + + StreamSP stream = GetAsyncErrorStream(); + data->Dump(stream.get()); +} + bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); } bool Debugger::StartIOHandlerThread() { if (!m_io_handler_thread.IsJoinable()) { llvm::Expected<HostThread> io_handler_thread = ThreadLauncher::LaunchThread( - "lldb.debugger.io-handler", IOHandlerThread, this, + "lldb.debugger.io-handler", [this] { return IOHandlerThread(); }, 8 * 1024 * 1024); // Use larger 8MB stack for this thread if (io_handler_thread) { m_io_handler_thread = *io_handler_thread; } else { - LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), - "failed to launch host thread: {}", + LLDB_LOG(GetLog(LLDBLog::Host), "failed to launch host thread: {}", llvm::toString(io_handler_thread.takeError())); } } @@ -1807,3 +1992,13 @@ Status Debugger::RunREPL(LanguageType language, const char *repl_options) { return err; } + +llvm::ThreadPool &Debugger::GetThreadPool() { + // NOTE: intentional leak to avoid issues with C++ destructor chain + static llvm::ThreadPool *g_thread_pool = nullptr; + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + g_thread_pool = new llvm::ThreadPool(llvm::optimal_concurrency()); + }); + return *g_thread_pool; +} |
