aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Host/common
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Host/common')
-rw-r--r--lldb/source/Host/common/DiagnosticsRendering.cpp235
-rw-r--r--lldb/source/Host/common/Editline.cpp3
-rw-r--r--lldb/source/Host/common/File.cpp42
-rw-r--r--lldb/source/Host/common/FileAction.cpp4
-rw-r--r--lldb/source/Host/common/MainLoopBase.cpp6
-rw-r--r--lldb/source/Host/common/StreamFile.cpp3
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,