diff options
Diffstat (limited to 'lldb/source/Host/common')
| -rw-r--r-- | lldb/source/Host/common/DiagnosticsRendering.cpp | 235 | ||||
| -rw-r--r-- | lldb/source/Host/common/Editline.cpp | 3 | ||||
| -rw-r--r-- | lldb/source/Host/common/File.cpp | 42 | ||||
| -rw-r--r-- | lldb/source/Host/common/FileAction.cpp | 4 | ||||
| -rw-r--r-- | lldb/source/Host/common/MainLoopBase.cpp | 6 | ||||
| -rw-r--r-- | lldb/source/Host/common/StreamFile.cpp | 3 |
6 files changed, 280 insertions, 13 deletions
diff --git a/lldb/source/Host/common/DiagnosticsRendering.cpp b/lldb/source/Host/common/DiagnosticsRendering.cpp new file mode 100644 index 0000000..f2cd396 --- /dev/null +++ b/lldb/source/Host/common/DiagnosticsRendering.cpp @@ -0,0 +1,235 @@ +//===-- DiagnosticsRendering.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/common/DiagnosticsRendering.h" +#include <cstdint> + +using namespace lldb_private; +using namespace lldb; + +namespace lldb_private { + +char DiagnosticError::ID; + +lldb::ErrorType DiagnosticError::GetErrorType() const { + return lldb::eErrorTypeExpression; +} + +StructuredData::ObjectSP Serialize(llvm::ArrayRef<DiagnosticDetail> details) { + auto make_array = []() { return std::make_unique<StructuredData::Array>(); }; + auto make_dict = []() { + return std::make_unique<StructuredData::Dictionary>(); + }; + auto dict_up = make_dict(); + dict_up->AddIntegerItem("version", 1u); + auto array_up = make_array(); + for (const DiagnosticDetail &diag : details) { + auto detail_up = make_dict(); + if (auto &sloc = diag.source_location) { + auto sloc_up = make_dict(); + sloc_up->AddStringItem("file", sloc->file.GetPath()); + sloc_up->AddIntegerItem("line", sloc->line); + sloc_up->AddIntegerItem("length", sloc->length); + sloc_up->AddBooleanItem("hidden", sloc->hidden); + sloc_up->AddBooleanItem("in_user_input", sloc->in_user_input); + detail_up->AddItem("source_location", std::move(sloc_up)); + } + llvm::StringRef severity = "unknown"; + switch (diag.severity) { + case lldb::eSeverityError: + severity = "error"; + break; + case lldb::eSeverityWarning: + severity = "warning"; + break; + case lldb::eSeverityInfo: + severity = "note"; + break; + } + detail_up->AddStringItem("severity", severity); + detail_up->AddStringItem("message", diag.message); + detail_up->AddStringItem("rendered", diag.rendered); + array_up->AddItem(std::move(detail_up)); + } + dict_up->AddItem("details", std::move(array_up)); + return dict_up; +} + +static llvm::raw_ostream &PrintSeverity(Stream &stream, + lldb::Severity severity) { + llvm::HighlightColor color; + llvm::StringRef text; + switch (severity) { + case lldb::eSeverityError: + color = llvm::HighlightColor::Error; + text = "error: "; + break; + case lldb::eSeverityWarning: + color = llvm::HighlightColor::Warning; + text = "warning: "; + break; + case lldb::eSeverityInfo: + color = llvm::HighlightColor::Remark; + text = "note: "; + break; + } + return llvm::WithColor(stream.AsRawOstream(), color, llvm::ColorMode::Enable) + << text; +} + +void RenderDiagnosticDetails(Stream &stream, + std::optional<uint16_t> offset_in_command, + bool show_inline, + llvm::ArrayRef<DiagnosticDetail> details) { + if (details.empty()) + return; + + if (!offset_in_command) { + for (const DiagnosticDetail &detail : details) { + PrintSeverity(stream, detail.severity); + stream << detail.rendered << '\n'; + } + return; + } + + // Since there is no other way to find this out, use the color + // attribute as a proxy for whether the terminal supports Unicode + // characters. In the future it might make sense to move this into + // Host so it can be customized for a specific platform. + llvm::StringRef cursor, underline, vbar, joint, hbar, spacer; + if (stream.AsRawOstream().colors_enabled()) { + cursor = "˄"; + underline = "˜"; + vbar = "│"; + joint = "╰"; + hbar = "─"; + spacer = " "; + } else { + cursor = "^"; + underline = "~"; + vbar = "|"; + joint = ""; + hbar = ""; + spacer = ""; + } + + // Partition the diagnostics. + std::vector<DiagnosticDetail> remaining_details, other_details, + hidden_details; + for (const DiagnosticDetail &detail : details) { + if (!show_inline || !detail.source_location) { + other_details.push_back(detail); + continue; + } + if (detail.source_location->hidden) { + hidden_details.push_back(detail); + continue; + } + if (!detail.source_location->in_user_input) { + other_details.push_back(detail); + continue; + } + + remaining_details.push_back(detail); + } + + // Sort the diagnostics. + auto sort = [](std::vector<DiagnosticDetail> &ds) { + llvm::stable_sort(ds, [](auto &d1, auto &d2) { + auto l1 = d1.source_location.value_or(DiagnosticDetail::SourceLocation{}); + auto l2 = d2.source_location.value_or(DiagnosticDetail::SourceLocation{}); + return std::tie(l1.line, l1.column) < std::tie(l2.line, l2.column); + }); + }; + sort(remaining_details); + sort(other_details); + sort(hidden_details); + + // Print a line with caret indicator(s) below the lldb prompt + command. + const size_t padding = *offset_in_command; + stream << std::string(padding, ' '); + { + size_t x_pos = 1; + for (const DiagnosticDetail &detail : remaining_details) { + auto &loc = *detail.source_location; + + if (x_pos > loc.column) + continue; + + stream << std::string(loc.column - x_pos, ' ') << cursor; + x_pos = loc.column + 1; + for (unsigned i = 0; i + 1 < loc.length; ++i) { + stream << underline; + x_pos += 1; + } + } + } + stream << '\n'; + + // Reverse the order within groups of diagnostics that are on the same column. + auto group = [](std::vector<DiagnosticDetail> &details) { + for (auto it = details.begin(), end = details.end(); it != end;) { + auto eq_end = std::find_if(it, end, [&](const DiagnosticDetail &d) { + return d.source_location->column != it->source_location->column; + }); + std::reverse(it, eq_end); + it = eq_end; + } + }; + group(remaining_details); + + // Work through each detail in reverse order using the vector/stack. + bool did_print = false; + for (; !remaining_details.empty(); remaining_details.pop_back()) { + const auto &detail = remaining_details.back(); + // Get the information to print this detail and remove it from the stack. + // Print all the lines for all the other messages first. + stream << std::string(padding, ' '); + size_t x_pos = 1; + for (auto &remaining_detail : + llvm::ArrayRef(remaining_details).drop_back(1)) { + uint16_t column = remaining_detail.source_location->column; + // Is this a note with the same column as another diagnostic? + if (column == detail.source_location->column) + continue; + + if (column >= x_pos) { + stream << std::string(column - x_pos, ' ') << vbar; + x_pos = column + 1; + } + } + + uint16_t column = detail.source_location->column; + // Print the line connecting the ^ with the error message. + if (column >= x_pos) + stream << std::string(column - x_pos, ' ') << joint << hbar << spacer; + + // Print a colorized string based on the message's severity type. + PrintSeverity(stream, detail.severity); + + // Finally, print the message and start a new line. + stream << detail.message << '\n'; + did_print = true; + } + + // Print the non-located details. + for (const DiagnosticDetail &detail : other_details) { + PrintSeverity(stream, detail.severity); + stream << detail.rendered << '\n'; + did_print = true; + } + + // Print the hidden details as a last resort. + if (!did_print) + for (const DiagnosticDetail &detail : hidden_details) { + PrintSeverity(stream, detail.severity); + stream << detail.rendered << '\n'; + } +} + +} // namespace lldb_private diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index 1b1922e..e2995b3 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -1626,6 +1626,9 @@ bool Editline::GetLine(std::string &line, bool &interrupted) { m_editor_status = EditorStatus::Editing; m_revert_cursor_index = -1; + lldbassert(m_output_stream_sp); + fprintf(m_locked_output->GetFile().GetStream(), "\r" ANSI_CLEAR_RIGHT); + int count; auto input = el_wgets(m_editline, &count); diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp index 65b75bd..4fad93f 100644 --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -249,8 +249,8 @@ uint32_t File::GetPermissions(Status &error) const { NativeFile::NativeFile() = default; -NativeFile::NativeFile(FILE *fh, bool transfer_ownership) - : m_stream(fh), m_own_stream(transfer_ownership) { +NativeFile::NativeFile(FILE *fh, OpenOptions options, bool transfer_ownership) + : m_stream(fh), m_options(options), m_own_stream(transfer_ownership) { #ifdef _WIN32 // In order to properly display non ASCII characters in Windows, we need to // use Windows APIs to print to the console. This is only required if the @@ -258,6 +258,26 @@ NativeFile::NativeFile(FILE *fh, bool transfer_ownership) int fd = _fileno(fh); is_windows_console = ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR; +#else +#ifndef NDEBUG + int fd = fileno(fh); + if (fd != -1) { + int required_mode = ConvertOpenOptionsForPOSIXOpen(options) & O_ACCMODE; + int mode = fcntl(fd, F_GETFL); + if (mode != -1) { + mode &= O_ACCMODE; + // Check that the file is open with a valid subset of the requested file + // access mode, e.g. if we expected the file to be writable then ensure it + // was opened with O_WRONLY or O_RDWR. + assert( + (required_mode == O_RDWR && mode == O_RDWR) || + (required_mode == O_RDONLY && (mode == O_RDWR || mode == O_RDONLY) || + (required_mode == O_WRONLY && + (mode == O_RDWR || mode == O_WRONLY))) && + "invalid file access mode"); + } + } +#endif #endif } @@ -274,7 +294,8 @@ NativeFile::NativeFile(int fd, OpenOptions options, bool transfer_ownership) } bool NativeFile::IsValid() const { - std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex); + std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, + m_stream_mutex); return DescriptorIsValidUnlocked() || StreamIsValidUnlocked(); } @@ -343,7 +364,8 @@ FILE *NativeFile::GetStream() { } Status NativeFile::Close() { - std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex); + std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, + m_stream_mutex); Status error; @@ -548,6 +570,10 @@ Status NativeFile::Sync() { Status NativeFile::Read(void *buf, size_t &num_bytes) { Status error; + // Ensure the file is open for reading. + if ((m_options & File::OpenOptionsModeMask) == eOpenOptionWriteOnly) + return Status(std::make_error_code(std::errc::bad_file_descriptor)); + #if defined(MAX_READ_SIZE) if (num_bytes > MAX_READ_SIZE) { uint8_t *p = (uint8_t *)buf; @@ -612,6 +638,10 @@ Status NativeFile::Read(void *buf, size_t &num_bytes) { Status NativeFile::Write(const void *buf, size_t &num_bytes) { Status error; + // Ensure the file is open for writing. + if ((m_options & File::OpenOptionsModeMask) == File::eOpenOptionReadOnly) + return Status(std::make_error_code(std::errc::bad_file_descriptor)); + #if defined(MAX_WRITE_SIZE) if (num_bytes > MAX_WRITE_SIZE) { const uint8_t *p = (const uint8_t *)buf; @@ -776,8 +806,8 @@ Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) { int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { #ifndef _WIN32 - ssize_t bytes_written = - llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset); + ssize_t bytes_written = llvm::sys::RetryAfterSignal( + -1, ::pwrite, m_descriptor, buf, num_bytes, offset); if (bytes_written < 0) { num_bytes = 0; error = Status::FromErrno(); diff --git a/lldb/source/Host/common/FileAction.cpp b/lldb/source/Host/common/FileAction.cpp index e1c3e14..ec271f7 100644 --- a/lldb/source/Host/common/FileAction.cpp +++ b/lldb/source/Host/common/FileAction.cpp @@ -25,10 +25,6 @@ void FileAction::Clear() { m_file_spec.Clear(); } -llvm::StringRef FileAction::GetPath() const { - return m_file_spec.GetPathAsConstString().AsCString(); -} - const FileSpec &FileAction::GetFileSpec() const { return m_file_spec; } bool FileAction::Open(int fd, const FileSpec &file_spec, bool read, diff --git a/lldb/source/Host/common/MainLoopBase.cpp b/lldb/source/Host/common/MainLoopBase.cpp index 64a57e6..232b9bc 100644 --- a/lldb/source/Host/common/MainLoopBase.cpp +++ b/lldb/source/Host/common/MainLoopBase.cpp @@ -12,8 +12,9 @@ using namespace lldb; using namespace lldb_private; -void MainLoopBase::AddCallback(const Callback &callback, TimePoint point) { +bool MainLoopBase::AddCallback(const Callback &callback, TimePoint point) { bool interrupt_needed; + bool interrupt_succeeded = true; { std::lock_guard<std::mutex> lock{m_callback_mutex}; // We need to interrupt the main thread if this callback is scheduled to @@ -22,7 +23,8 @@ void MainLoopBase::AddCallback(const Callback &callback, TimePoint point) { m_callbacks.emplace(point, callback); } if (interrupt_needed) - Interrupt(); + interrupt_succeeded = Interrupt(); + return interrupt_succeeded; } void MainLoopBase::ProcessCallbacks() { diff --git a/lldb/source/Host/common/StreamFile.cpp b/lldb/source/Host/common/StreamFile.cpp index 099980a..131412d 100644 --- a/lldb/source/Host/common/StreamFile.cpp +++ b/lldb/source/Host/common/StreamFile.cpp @@ -27,7 +27,8 @@ StreamFile::StreamFile(int fd, bool transfer_ownership) : Stream() { } StreamFile::StreamFile(FILE *fh, bool transfer_ownership) : Stream() { - m_file_sp = std::make_shared<NativeFile>(fh, transfer_ownership); + m_file_sp = std::make_shared<NativeFile>(fh, File::eOpenOptionWriteOnly, + transfer_ownership); } StreamFile::StreamFile(const char *path, File::OpenOptions options, |
