diff options
Diffstat (limited to 'source/Host')
25 files changed, 2269 insertions, 600 deletions
diff --git a/source/Host/common/Editline.cpp b/source/Host/common/Editline.cpp index a127d58b2767..ed67d0c2c2b3 100644 --- a/source/Host/common/Editline.cpp +++ b/source/Host/common/Editline.cpp @@ -20,6 +20,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/Mutex.h" +#include "lldb/Utility/LLDBAssert.h" using namespace lldb_private; using namespace lldb_private::line_editor; @@ -192,9 +193,9 @@ namespace lldb_private { if (m_path.empty() && m_history && !m_prefix.empty()) { - std::string parent_path = FileSpec ("~/.lldb", true).GetPath(); + FileSpec parent_path{"~/.lldb", true}; char history_path[PATH_MAX]; - if (FileSystem::MakeDirectory(parent_path.c_str(), lldb::eFilePermissionsDirectoryDefault).Success()) + if (FileSystem::MakeDirectory(parent_path, lldb::eFilePermissionsDirectoryDefault).Success()) { snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str()); } @@ -581,9 +582,22 @@ Editline::GetCharacter (EditLineCharType * c) { lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; char ch = 0; - m_editor_getting_char = true; + + // This mutex is locked by our caller (GetLine). Unlock it while we read a character + // (blocking operation), so we do not hold the mutex indefinitely. This gives a chance + // for someone to interrupt us. After Read returns, immediately lock the mutex again and + // check if we were interrupted. + m_output_mutex.Unlock(); int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL); - m_editor_getting_char = false; + m_output_mutex.Lock(); + if (m_editor_status == EditorStatus::Interrupted) + { + while (read_count > 0 && status == lldb::eConnectionStatusSuccess) + read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL); + lldbassert(status == lldb::eConnectionStatusInterrupted); + return 0; + } + if (read_count) { #if LLDB_EDITLINE_USE_WCHAR @@ -594,7 +608,7 @@ Editline::GetCharacter (EditLineCharType * c) return 1; #else *c = ch; - if(*c != EOF) + if(ch != (char)EOF) return 1; #endif } @@ -602,14 +616,12 @@ Editline::GetCharacter (EditLineCharType * c) { switch (status) { - case lldb::eConnectionStatusInterrupted: - m_editor_status = EditorStatus::Interrupted; - printf ("^C\n"); - return 0; - case lldb::eConnectionStatusSuccess: // Success break; + case lldb::eConnectionStatusInterrupted: + lldbassert(0 && "Interrupts should have been handled above."); + case lldb::eConnectionStatusError: // Check GetError() for details case lldb::eConnectionStatusTimedOut: // Request timed out case lldb::eConnectionStatusEndOfFile: // End-of-file encountered @@ -711,7 +723,7 @@ Editline::BreakLineCommand (int ch) unsigned char Editline::DeleteNextCharCommand (int ch) { - LineInfoW * info = (LineInfoW *)el_wline (m_editline); + LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); // Just delete the next character normally if possible if (info->cursor < info->lastchar) @@ -755,7 +767,7 @@ Editline::DeleteNextCharCommand (int ch) unsigned char Editline::DeletePreviousCharCommand (int ch) { - LineInfoW * info = (LineInfoW *)el_wline (m_editline); + LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); // Just delete the previous character normally when not at the start of a line if (info->cursor > info->buffer) @@ -861,7 +873,7 @@ Editline::FixIndentationCommand (int ch) StringList lines = GetInputAsStringList (m_current_line_index + 1); // Determine the cursor position - LineInfoW * info = (LineInfoW *)el_wline (m_editline); + LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); int cursor_position = info->cursor - info->buffer; int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton); @@ -887,7 +899,7 @@ Editline::RevertLineCommand (int ch) el_winsertstr (m_editline, m_input_lines[m_current_line_index].c_str()); if (m_revert_cursor_index >= 0) { - LineInfoW * info = (LineInfoW *)el_wline (m_editline); + LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); info->cursor = info->buffer + m_revert_cursor_index; if (info->cursor > info->lastchar) { @@ -1252,41 +1264,31 @@ Editline::GetCurrentLine() return m_current_line_index; } -void -Editline::Hide() -{ - // Make sure we're at a stable location waiting for input - while (m_editor_status == EditorStatus::Editing && !m_editor_getting_char) - { - usleep(100000); - } - - // Clear the existing input - if (m_editor_status == EditorStatus::Editing) - { - MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); - fprintf(m_output_file, ANSI_CLEAR_BELOW); - } -} - -void -Editline::Refresh() +bool +Editline::Interrupt() { - if (m_editor_status == EditorStatus::Editing) - { - DisplayInput(); - MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + bool result = true; + Mutex::Locker locker(m_output_mutex); + if (m_editor_status == EditorStatus::Editing) { + fprintf(m_output_file, "^C\n"); + result = m_input_connection.InterruptRead(); } + m_editor_status = EditorStatus::Interrupted; + return result; } bool -Editline::Interrupt() +Editline::Cancel() { - if (m_editor_status == EditorStatus::Editing) - { - return m_input_connection.InterruptRead(); + bool result = true; + Mutex::Locker locker(m_output_mutex); + if (m_editor_status == EditorStatus::Editing) { + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + fprintf(m_output_file, ANSI_CLEAR_BELOW); + result = m_input_connection.InterruptRead(); } - return false; // Interrupt not handled as we weren't getting a line or lines + m_editor_status = EditorStatus::Interrupted; + return result; } void @@ -1321,14 +1323,23 @@ Editline::GetLine (std::string &line, bool &interrupted) m_input_lines = std::vector<EditLineStringType>(); m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); + Mutex::Locker locker(m_output_mutex); + + lldbassert(m_editor_status != EditorStatus::Editing); + if (m_editor_status == EditorStatus::Interrupted) + { + m_editor_status = EditorStatus::Complete; + interrupted = true; + return true; + } + SetCurrentLine (0); m_in_history = false; m_editor_status = EditorStatus::Editing; - m_editor_getting_char = false; m_revert_cursor_index = -1; #ifdef USE_SETUPTERM_WORKAROUND - setupterm((char *)0, fileno(m_output_file), (int *)0); + setupterm((char *)0, fileno(m_output_file), (int *)0); #endif int count; @@ -1366,12 +1377,12 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted) m_input_lines = std::vector<EditLineStringType>(); m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); + Mutex::Locker locker(m_output_mutex); // Begin the line editing loop DisplayInput(); SetCurrentLine (0); MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart); m_editor_status = EditorStatus::Editing; - m_editor_getting_char = false; m_in_history = false; m_revert_cursor_index = -1; @@ -1396,3 +1407,21 @@ Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted) } return m_editor_status != EditorStatus::EndOfInput; } + +void +Editline::PrintAsync (Stream *stream, const char *s, size_t len) +{ + Mutex::Locker locker(m_output_mutex); + if (m_editor_status == EditorStatus::Editing) + { + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + fprintf(m_output_file, ANSI_CLEAR_BELOW); + } + stream->Write (s, len); + stream->Flush(); + if (m_editor_status == EditorStatus::Editing) + { + DisplayInput(); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + } +} diff --git a/source/Host/common/File.cpp b/source/Host/common/File.cpp index 946f3dd6fef5..a3420bff65f5 100644 --- a/source/Host/common/File.cpp +++ b/source/Host/common/File.cpp @@ -325,12 +325,12 @@ File::Open (const char *path, uint32_t options, uint32_t permissions) } uint32_t -File::GetPermissions (const char *path, Error &error) +File::GetPermissions(const FileSpec &file_spec, Error &error) { - if (path && path[0]) + if (file_spec) { struct stat file_stats; - if (::stat (path, &file_stats) == -1) + if (::stat(file_spec.GetCString(), &file_stats) == -1) error.SetErrorToErrno(); else { @@ -339,12 +339,7 @@ File::GetPermissions (const char *path, Error &error) } } else - { - if (path) - error.SetErrorString ("invalid path"); - else - error.SetErrorString ("empty path"); - } + error.SetErrorString ("empty file spec"); return 0; } @@ -595,10 +590,49 @@ File::Sync () return error; } +#if defined (__APPLE__) +// Darwin kernels only can read/write <= INT_MAX bytes +#define MAX_READ_SIZE INT_MAX +#define MAX_WRITE_SIZE INT_MAX +#endif + Error File::Read (void *buf, size_t &num_bytes) { Error error; + +#if defined (MAX_READ_SIZE) + if (num_bytes > MAX_READ_SIZE) + { + uint8_t *p = (uint8_t *)buf; + size_t bytes_left = num_bytes; + // Init the num_bytes read to zero + num_bytes = 0; + + while (bytes_left > 0) + { + size_t curr_num_bytes; + if (bytes_left > MAX_READ_SIZE) + curr_num_bytes = MAX_READ_SIZE; + else + curr_num_bytes = bytes_left; + + error = Read (p + num_bytes, curr_num_bytes); + + // Update how many bytes were read + num_bytes += curr_num_bytes; + if (bytes_left < curr_num_bytes) + bytes_left = 0; + else + bytes_left -= curr_num_bytes; + + if (error.Fail()) + break; + } + return error; + } +#endif + ssize_t bytes_read = -1; if (DescriptorIsValid()) { @@ -642,6 +676,39 @@ Error File::Write (const void *buf, size_t &num_bytes) { Error error; + +#if defined (MAX_WRITE_SIZE) + if (num_bytes > MAX_WRITE_SIZE) + { + const uint8_t *p = (const uint8_t *)buf; + size_t bytes_left = num_bytes; + // Init the num_bytes written to zero + num_bytes = 0; + + while (bytes_left > 0) + { + size_t curr_num_bytes; + if (bytes_left > MAX_WRITE_SIZE) + curr_num_bytes = MAX_WRITE_SIZE; + else + curr_num_bytes = bytes_left; + + error = Write (p + num_bytes, curr_num_bytes); + + // Update how many bytes were read + num_bytes += curr_num_bytes; + if (bytes_left < curr_num_bytes) + bytes_left = 0; + else + bytes_left -= curr_num_bytes; + + if (error.Fail()) + break; + } + return error; + } +#endif + ssize_t bytes_written = -1; if (DescriptorIsValid()) { @@ -687,8 +754,41 @@ File::Write (const void *buf, size_t &num_bytes) Error File::Read (void *buf, size_t &num_bytes, off_t &offset) { -#ifndef _WIN32 Error error; + +#if defined (MAX_READ_SIZE) + if (num_bytes > MAX_READ_SIZE) + { + uint8_t *p = (uint8_t *)buf; + size_t bytes_left = num_bytes; + // Init the num_bytes read to zero + num_bytes = 0; + + while (bytes_left > 0) + { + size_t curr_num_bytes; + if (bytes_left > MAX_READ_SIZE) + curr_num_bytes = MAX_READ_SIZE; + else + curr_num_bytes = bytes_left; + + error = Read (p + num_bytes, curr_num_bytes, offset); + + // Update how many bytes were read + num_bytes += curr_num_bytes; + if (bytes_left < curr_num_bytes) + bytes_left = 0; + else + bytes_left -= curr_num_bytes; + + if (error.Fail()) + break; + } + return error; + } +#endif + +#ifndef _WIN32 int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { @@ -714,15 +814,14 @@ File::Read (void *buf, size_t &num_bytes, off_t &offset) num_bytes = 0; error.SetErrorString("invalid file handle"); } - return error; #else long cur = ::lseek(m_descriptor, 0, SEEK_CUR); SeekFromStart(offset); - Error error = Read(buf, num_bytes); + error = Read(buf, num_bytes); if (!error.Fail()) SeekFromStart(cur); - return error; #endif + return error; } Error @@ -746,7 +845,8 @@ File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP size_t num_bytes_plus_nul_char = num_bytes + (null_terminate ? 1 : 0); std::unique_ptr<DataBufferHeap> data_heap_ap; - data_heap_ap.reset(new DataBufferHeap(num_bytes_plus_nul_char, '\0')); + data_heap_ap.reset(new DataBufferHeap()); + data_heap_ap->SetByteSize(num_bytes_plus_nul_char); if (data_heap_ap.get()) { @@ -783,6 +883,39 @@ Error File::Write (const void *buf, size_t &num_bytes, off_t &offset) { Error error; + +#if defined (MAX_WRITE_SIZE) + if (num_bytes > MAX_WRITE_SIZE) + { + const uint8_t *p = (const uint8_t *)buf; + size_t bytes_left = num_bytes; + // Init the num_bytes written to zero + num_bytes = 0; + + while (bytes_left > 0) + { + size_t curr_num_bytes; + if (bytes_left > MAX_WRITE_SIZE) + curr_num_bytes = MAX_WRITE_SIZE; + else + curr_num_bytes = bytes_left; + + error = Write (p + num_bytes, curr_num_bytes, offset); + + // Update how many bytes were read + num_bytes += curr_num_bytes; + if (bytes_left < curr_num_bytes) + bytes_left = 0; + else + bytes_left -= curr_num_bytes; + + if (error.Fail()) + break; + } + return error; + } +#endif + int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { @@ -811,7 +944,6 @@ File::Write (const void *buf, size_t &num_bytes, off_t &offset) if (!error.Fail()) SeekFromStart(cur); - ssize_t bytes_written = after - cur; offset = after; #endif } diff --git a/source/Host/common/FileSpec.cpp b/source/Host/common/FileSpec.cpp index 6a6de53cd311..ceb094b9ede7 100644 --- a/source/Host/common/FileSpec.cpp +++ b/source/Host/common/FileSpec.cpp @@ -27,6 +27,7 @@ #include <pwd.h> #endif +#include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataBufferMemoryMap.h" #include "lldb/Core/RegularExpression.h" @@ -46,7 +47,45 @@ using namespace lldb; using namespace lldb_private; -static bool +namespace { + +bool +PathSyntaxIsPosix(FileSpec::PathSyntax syntax) +{ + return (syntax == FileSpec::ePathSyntaxPosix || + (syntax == FileSpec::ePathSyntaxHostNative && + FileSystem::GetNativePathSyntax() == FileSpec::ePathSyntaxPosix)); +} + +char +GetPathSeparator(FileSpec::PathSyntax syntax) +{ + return PathSyntaxIsPosix(syntax) ? '/' : '\\'; +} + +void +Normalize(llvm::SmallVectorImpl<char> &path, FileSpec::PathSyntax syntax) +{ + if (PathSyntaxIsPosix(syntax)) return; + + std::replace(path.begin(), path.end(), '\\', '/'); + // Windows path can have \\ slashes which can be changed by replace + // call above to //. Here we remove the duplicate. + auto iter = std::unique ( path.begin(), path.end(), + []( char &c1, char &c2 ){ + return (c1 == '/' && c2 == '/');}); + path.erase(iter, path.end()); +} + +void +Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::PathSyntax syntax) +{ + if (PathSyntaxIsPosix(syntax)) return; + + std::replace(path.begin(), path.end(), '/', '\\'); +} + +bool GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) { char resolved_path[PATH_MAX]; @@ -55,6 +94,8 @@ GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) return false; } +} + // Resolves the username part of a path of the form ~user/other/directories, and // writes the result into dst_path. This will also resolve "~" to the current user. // If you want to complete "~" to the list of users, pass it to ResolvePartialUsername. @@ -164,7 +205,20 @@ FileSpec::Resolve (llvm::SmallVectorImpl<char> &path) ResolveUsername(path); #endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + // Save a copy of the original path that's passed in + llvm::SmallString<PATH_MAX> original_path(path.begin(), path.end()); + llvm::sys::fs::make_absolute(path); + + + path.push_back(0); // Be sure we have a nul terminated string + path.pop_back(); + struct stat file_stats; + if (::stat (path.data(), &file_stats) != 0) + { + path.clear(); + path.append(original_path.begin(), original_path.end()); + } } FileSpec::FileSpec() : @@ -188,6 +242,21 @@ FileSpec::FileSpec(const char *pathname, bool resolve_path, PathSyntax syntax) : SetFile(pathname, resolve_path, syntax); } +FileSpec::FileSpec(const char *pathname, bool resolve_path, ArchSpec arch) : + FileSpec{pathname, resolve_path, arch.GetTriple().isOSWindows() ? ePathSyntaxWindows : ePathSyntaxPosix} +{ +} + +FileSpec::FileSpec(const std::string &path, bool resolve_path, PathSyntax syntax) : + FileSpec{path.c_str(), resolve_path, syntax} +{ +} + +FileSpec::FileSpec(const std::string &path, bool resolve_path, ArchSpec arch) : + FileSpec{path.c_str(), resolve_path, arch} +{ +} + //------------------------------------------------------------------ // Copy constructor //------------------------------------------------------------------ @@ -233,30 +302,6 @@ FileSpec::operator= (const FileSpec& rhs) return *this; } -void FileSpec::Normalize(llvm::SmallVectorImpl<char> &path, PathSyntax syntax) -{ - if (syntax == ePathSyntaxPosix || - (syntax == ePathSyntaxHostNative && FileSystem::GetNativePathSyntax() == ePathSyntaxPosix)) - return; - - std::replace(path.begin(), path.end(), '\\', '/'); - // Windows path can have \\ slashes which can be changed by replace - // call above to //. Here we remove the duplicate. - auto iter = std::unique ( path.begin(), path.end(), - []( char &c1, char &c2 ){ - return (c1 == '/' && c2 == '/');}); - path.erase(iter, path.end()); -} - -void FileSpec::DeNormalize(llvm::SmallVectorImpl<char> &path, PathSyntax syntax) -{ - if (syntax == ePathSyntaxPosix || - (syntax == ePathSyntaxHostNative && FileSystem::GetNativePathSyntax() == ePathSyntaxPosix)) - return; - - std::replace(path.begin(), path.end(), '/', '\\'); -} - //------------------------------------------------------------------ // Update the contents of this object with a new path. The path will // be split up into a directory and filename and stored as uniqued @@ -299,6 +344,27 @@ FileSpec::SetFile (const char *pathname, bool resolve, PathSyntax syntax) m_directory.SetCString(normalized.c_str()); } +void +FileSpec::SetFile(const char *pathname, bool resolve, ArchSpec arch) +{ + return SetFile(pathname, resolve, + arch.GetTriple().isOSWindows() + ? ePathSyntaxWindows + : ePathSyntaxPosix); +} + +void +FileSpec::SetFile(const std::string &pathname, bool resolve, PathSyntax syntax) +{ + return SetFile(pathname.c_str(), resolve, syntax); +} + +void +FileSpec::SetFile(const std::string &pathname, bool resolve, ArchSpec arch) +{ + return SetFile(pathname.c_str(), resolve, arch); +} + //---------------------------------------------------------------------- // Convert to pointer operator. This allows code to check any FileSpec // objects to see if they contain anything valid using code such as: @@ -480,6 +546,14 @@ FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full, bool remove_ba } void +FileSpec::NormalizePath () +{ + ConstString normalized_directory; + FileSpec::RemoveBackupDots(m_directory, normalized_directory); + m_directory = normalized_directory; +} + +void FileSpec::RemoveBackupDots (const ConstString &input_const_str, ConstString &result_const_str) { const char *input = input_const_str.GetCString(); @@ -545,6 +619,8 @@ FileSpec::RemoveBackupDots (const ConstString &input_const_str, ConstString &res { if (had_dots) { + while (before_sep.startswith("//")) + before_sep = before_sep.substr(1); if (!before_sep.empty()) { result.append(before_sep.data(), before_sep.size()); @@ -590,13 +666,13 @@ FileSpec::RemoveBackupDots (const ConstString &input_const_str, ConstString &res void FileSpec::Dump(Stream *s) const { - static ConstString g_slash_only ("/"); if (s) { - m_directory.Dump(s); - if (m_directory && m_directory != g_slash_only) - s->PutChar('/'); - m_filename.Dump(s); + std::string path{GetPath(true)}; + s->PutCString(path.c_str()); + char path_separator = GetPathSeparator(m_syntax); + if (!m_filename && !path.empty() && path.back() != path_separator) + s->PutChar(path_separator); } } @@ -718,7 +794,7 @@ FileSpec::GetPermissions () const { uint32_t file_permissions = 0; if (*this) - FileSystem::GetFilePermissions(GetPath().c_str(), file_permissions); + FileSystem::GetFilePermissions(*this, file_permissions); return file_permissions; } @@ -780,26 +856,37 @@ FileSpec::GetPath(char *path, size_t path_max_len, bool denormalize) const return 0; std::string result = GetPath(denormalize); - - size_t result_length = std::min(path_max_len-1, result.length()); - ::strncpy(path, result.c_str(), result_length + 1); - return result_length; + ::snprintf(path, path_max_len, "%s", result.c_str()); + return std::min(path_max_len-1, result.length()); } std::string -FileSpec::GetPath (bool denormalize) const +FileSpec::GetPath(bool denormalize) const { llvm::SmallString<64> result; - if (m_directory) - result.append(m_directory.GetCString()); - if (m_filename) - llvm::sys::path::append(result, m_filename.GetCString()); - if (denormalize && !result.empty()) - DeNormalize(result, m_syntax); - + GetPath(result, denormalize); return std::string(result.begin(), result.end()); } +const char * +FileSpec::GetCString(bool denormalize) const +{ + return ConstString{GetPath(denormalize)}.AsCString(NULL); +} + +void +FileSpec::GetPath(llvm::SmallVectorImpl<char> &path, bool denormalize) const +{ + path.append(m_directory.GetStringRef().begin(), m_directory.GetStringRef().end()); + if (m_directory) + path.insert(path.end(), '/'); + path.append(m_filename.GetStringRef().begin(), m_filename.GetStringRef().end()); + Normalize(path, m_syntax); + if (path.size() > 1 && path.back() == '/') path.pop_back(); + if (denormalize && !path.empty()) + Denormalize(path, m_syntax); +} + ConstString FileSpec::GetFileNameExtension () const { @@ -852,6 +939,15 @@ FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const return data_sp; } +DataBufferSP +FileSpec::MemoryMapFileContentsIfLocal(off_t file_offset, size_t file_size) const +{ + if (FileSystem::IsLocal(*this)) + return MemoryMapFileContents(file_offset, file_size); + else + return ReadFileContents(file_offset, file_size, NULL); +} + //------------------------------------------------------------------ // Return the size in bytes that this object takes in memory. This @@ -974,15 +1070,7 @@ FileSpec::ReadFileLines (STLStringArray &lines) } FileSpec::EnumerateDirectoryResult -FileSpec::EnumerateDirectory -( - const char *dir_path, - bool find_directories, - bool find_files, - bool find_other, - EnumerateDirectoryCallbackType callback, - void *callback_baton -) +FileSpec::ForEachItemInDirectory (const char *dir_path, DirectoryCallback const &callback) { if (dir_path && dir_path[0]) { @@ -1000,7 +1088,6 @@ FileSpec::EnumerateDirectory do { - bool call_callback = false; FileSpec::FileType file_type = eFileTypeUnknown; if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { @@ -1013,31 +1100,27 @@ FileSpec::EnumerateDirectory continue; file_type = eFileTypeDirectory; - call_callback = find_directories; } else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) { file_type = eFileTypeOther; - call_callback = find_other; } else { file_type = eFileTypeRegular; - call_callback = find_files; } - if (call_callback) + + char child_path[MAX_PATH]; + const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s\\%s", dir_path, ffd.cFileName); + if (child_path_len < (int)(sizeof(child_path) - 1)) { - char child_path[MAX_PATH]; - const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s\\%s", dir_path, ffd.cFileName); - if (child_path_len < (int)(sizeof(child_path) - 1)) - { - // Don't resolve the file type or path - FileSpec child_path_spec (child_path, false); + // Don't resolve the file type or path + FileSpec child_path_spec (child_path, false); - EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); + EnumerateDirectoryResult result = callback (file_type, child_path_spec); - switch (result) - { + switch (result) + { case eEnumerateDirectoryResultNext: // Enumerate next entry in the current directory. We just // exit this switch and will continue enumerating the @@ -1045,27 +1128,21 @@ FileSpec::EnumerateDirectory break; case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not - if (FileSpec::EnumerateDirectory(child_path, - find_directories, - find_files, - find_other, - callback, - callback_baton) == eEnumerateDirectoryResultQuit) + if (FileSpec::ForEachItemInDirectory(child_path, callback) == eEnumerateDirectoryResultQuit) { - // The subdirectory returned Quit, which means to + // The subdirectory returned Quit, which means to // stop all directory enumerations at all levels. return eEnumerateDirectoryResultQuit; } break; case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. - // Exit from this directory level and tell parent to + // Exit from this directory level and tell parent to // keep enumerating. return eEnumerateDirectoryResultNext; case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level return eEnumerateDirectoryResultQuit; - } } } } while (FindNextFile(hFind, &ffd) != 0); @@ -1098,80 +1175,71 @@ FileSpec::EnumerateDirectory if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') continue; } - - bool call_callback = false; + FileSpec::FileType file_type = eFileTypeUnknown; switch (dp->d_type) { - default: - case DT_UNKNOWN: file_type = eFileTypeUnknown; call_callback = true; break; - case DT_FIFO: file_type = eFileTypePipe; call_callback = find_other; break; - case DT_CHR: file_type = eFileTypeOther; call_callback = find_other; break; - case DT_DIR: file_type = eFileTypeDirectory; call_callback = find_directories; break; - case DT_BLK: file_type = eFileTypeOther; call_callback = find_other; break; - case DT_REG: file_type = eFileTypeRegular; call_callback = find_files; break; - case DT_LNK: file_type = eFileTypeSymbolicLink; call_callback = find_other; break; - case DT_SOCK: file_type = eFileTypeSocket; call_callback = find_other; break; + default: + case DT_UNKNOWN: file_type = eFileTypeUnknown; break; + case DT_FIFO: file_type = eFileTypePipe; break; + case DT_CHR: file_type = eFileTypeOther; break; + case DT_DIR: file_type = eFileTypeDirectory; break; + case DT_BLK: file_type = eFileTypeOther; break; + case DT_REG: file_type = eFileTypeRegular; break; + case DT_LNK: file_type = eFileTypeSymbolicLink; break; + case DT_SOCK: file_type = eFileTypeSocket; break; #if !defined(__OpenBSD__) - case DT_WHT: file_type = eFileTypeOther; call_callback = find_other; break; + case DT_WHT: file_type = eFileTypeOther; break; #endif } - if (call_callback) + char child_path[PATH_MAX]; + + // Don't make paths with "/foo//bar", that just confuses everybody. + int child_path_len; + if (dir_path_last_char == '/') + child_path_len = ::snprintf (child_path, sizeof(child_path), "%s%s", dir_path, dp->d_name); + else + child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); + + if (child_path_len < (int)(sizeof(child_path) - 1)) { - char child_path[PATH_MAX]; + // Don't resolve the file type or path + FileSpec child_path_spec (child_path, false); - // Don't make paths with "/foo//bar", that just confuses everybody. - int child_path_len; - if (dir_path_last_char == '/') - child_path_len = ::snprintf (child_path, sizeof(child_path), "%s%s", dir_path, dp->d_name); - else - child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); + EnumerateDirectoryResult result = callback (file_type, child_path_spec); - if (child_path_len < (int)(sizeof(child_path) - 1)) + switch (result) { - // Don't resolve the file type or path - FileSpec child_path_spec (child_path, false); - - EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); - - switch (result) - { - case eEnumerateDirectoryResultNext: + case eEnumerateDirectoryResultNext: // Enumerate next entry in the current directory. We just // exit this switch and will continue enumerating the // current directory as we currently are... break; case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not - if (FileSpec::EnumerateDirectory (child_path, - find_directories, - find_files, - find_other, - callback, - callback_baton) == eEnumerateDirectoryResultQuit) + if (FileSpec::ForEachItemInDirectory (child_path, callback) == eEnumerateDirectoryResultQuit) { - // The subdirectory returned Quit, which means to + // The subdirectory returned Quit, which means to // stop all directory enumerations at all levels. if (buf) free (buf); return eEnumerateDirectoryResultQuit; } break; - + case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. - // Exit from this directory level and tell parent to + // Exit from this directory level and tell parent to // keep enumerating. if (buf) free (buf); return eEnumerateDirectoryResultNext; - + case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level if (buf) free (buf); return eEnumerateDirectoryResultQuit; - } } } } @@ -1187,6 +1255,39 @@ FileSpec::EnumerateDirectory return eEnumerateDirectoryResultNext; } +FileSpec::EnumerateDirectoryResult +FileSpec::EnumerateDirectory +( + const char *dir_path, + bool find_directories, + bool find_files, + bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton +) +{ + return ForEachItemInDirectory(dir_path, + [&find_directories, &find_files, &find_other, &callback, &callback_baton] + (FileType file_type, const FileSpec &file_spec) { + switch (file_type) + { + case FileType::eFileTypeDirectory: + if (find_directories) + return callback(callback_baton, file_type, file_spec); + break; + case FileType::eFileTypeRegular: + if (find_files) + return callback(callback_baton, file_type, file_spec); + break; + default: + if (find_other) + return callback(callback_baton, file_type, file_spec); + break; + } + return eEnumerateDirectoryResultNext; + }); +} + FileSpec FileSpec::CopyByAppendingPathComponent (const char *new_path) const { @@ -1266,25 +1367,70 @@ FileSpec::GetLastPathComponent () const } void -FileSpec::AppendPathComponent (const char *new_path) +FileSpec::PrependPathComponent(const char *new_path) { + if (!new_path) return; const bool resolve = false; if (m_filename.IsEmpty() && m_directory.IsEmpty()) { - SetFile(new_path,resolve); + SetFile(new_path, resolve); return; } StreamString stream; if (m_filename.IsEmpty()) - stream.Printf("%s/%s",m_directory.GetCString(),new_path); + stream.Printf("%s/%s", new_path, m_directory.GetCString()); else if (m_directory.IsEmpty()) - stream.Printf("%s/%s",m_filename.GetCString(),new_path); + stream.Printf("%s/%s", new_path, m_filename.GetCString()); else - stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path); + stream.Printf("%s/%s/%s", new_path, m_directory.GetCString(), m_filename.GetCString()); SetFile(stream.GetData(), resolve); } void +FileSpec::PrependPathComponent(const std::string &new_path) +{ + return PrependPathComponent(new_path.c_str()); +} + +void +FileSpec::PrependPathComponent(const FileSpec &new_path) +{ + return PrependPathComponent(new_path.GetPath(false)); +} + +void +FileSpec::AppendPathComponent(const char *new_path) +{ + if (!new_path) return; + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + { + SetFile(new_path, resolve); + return; + } + StreamString stream; + if (m_filename.IsEmpty()) + stream.Printf("%s/%s", m_directory.GetCString(), new_path); + else if (m_directory.IsEmpty()) + stream.Printf("%s/%s", m_filename.GetCString(), new_path); + else + stream.Printf("%s/%s/%s", m_directory.GetCString(), m_filename.GetCString(), new_path); + SetFile(stream.GetData(), resolve); +} + +void +FileSpec::AppendPathComponent(const std::string &new_path) +{ + return AppendPathComponent(new_path.c_str()); +} + +void +FileSpec::AppendPathComponent(const FileSpec &new_path) +{ + return AppendPathComponent(new_path.GetPath(false)); +} + +void FileSpec::RemoveLastPathComponent () { const bool resolve = false; @@ -1343,22 +1489,14 @@ FileSpec::IsSourceImplementationFile () const } bool -FileSpec::IsRelativeToCurrentWorkingDirectory () const +FileSpec::IsRelative() const { const char *dir = m_directory.GetCString(); llvm::StringRef directory(dir ? dir : ""); if (directory.size() > 0) { - if (m_syntax == ePathSyntaxWindows) - { - if (directory.size() >= 2 && directory[1] == ':') - return false; - if (directory[0] == '/') - return false; - return true; - } - else + if (PathSyntaxIsPosix(m_syntax)) { // If the path doesn't start with '/' or '~', return true switch (directory[0]) @@ -1370,6 +1508,14 @@ FileSpec::IsRelativeToCurrentWorkingDirectory () const return true; } } + else + { + if (directory.size() >= 2 && directory[1] == ':') + return false; + if (directory[0] == '/') + return false; + return true; + } } else if (m_filename) { @@ -1378,3 +1524,9 @@ FileSpec::IsRelativeToCurrentWorkingDirectory () const } return false; } + +bool +FileSpec::IsAbsolute() const +{ + return !FileSpec::IsRelative(); +} diff --git a/source/Host/common/FileSystem.cpp b/source/Host/common/FileSystem.cpp new file mode 100644 index 000000000000..5a5dbc79fe11 --- /dev/null +++ b/source/Host/common/FileSystem.cpp @@ -0,0 +1,103 @@ +//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/FileSystem.h" + +#include "llvm/Support/MD5.h" + +#include <algorithm> +#include <fstream> +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +bool +CalcMD5(const FileSpec &file_spec, uint64_t offset, uint64_t length, llvm::MD5::MD5Result &md5_result) +{ + llvm::MD5 md5_hash; + std::ifstream file(file_spec.GetPath(), std::ios::binary); + if (!file.is_open()) + return false; + + if (offset > 0) + file.seekg(offset, file.beg); + + std::vector<char> read_buf(4096); + uint64_t total_read_bytes = 0; + while (!file.eof()) + { + const uint64_t to_read = (length > 0) ? + std::min(static_cast<uint64_t>(read_buf.size()), length - total_read_bytes) : + read_buf.size(); + if (to_read == 0) + break; + + file.read(&read_buf[0], to_read); + const auto read_bytes = file.gcount(); + if (read_bytes == 0) + break; + + md5_hash.update(llvm::StringRef(&read_buf[0], read_bytes)); + total_read_bytes += read_bytes; + } + + md5_hash.final(md5_result); + return true; +} + +} // namespace + +bool +FileSystem::CalculateMD5(const FileSpec &file_spec, uint64_t &low, uint64_t &high) +{ + return CalculateMD5(file_spec, 0, 0, low, high); +} + +bool +FileSystem::CalculateMD5(const FileSpec &file_spec, + uint64_t offset, + uint64_t length, + uint64_t &low, + uint64_t &high) +{ + llvm::MD5::MD5Result md5_result; + if (!CalcMD5(file_spec, offset, length, md5_result)) + return false; + + const auto uint64_res = reinterpret_cast<const uint64_t*>(md5_result); + high = uint64_res[0]; + low = uint64_res[1]; + + return true; +} + +bool +FileSystem::CalculateMD5AsString(const FileSpec &file_spec, std::string& digest_str) +{ + return CalculateMD5AsString(file_spec, 0, 0, digest_str); +} + +bool +FileSystem::CalculateMD5AsString(const FileSpec &file_spec, + uint64_t offset, + uint64_t length, + std::string& digest_str) +{ + llvm::MD5::MD5Result md5_result; + if (!CalcMD5(file_spec, offset, length, md5_result)) + return false; + + llvm::SmallString<32> result_str; + llvm::MD5::stringifyResult(md5_result, result_str); + digest_str = result_str.c_str(); + return true; +} diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp index 30f5c8683060..20d6355e6195 100644 --- a/source/Host/common/Host.cpp +++ b/source/Host/common/Host.cpp @@ -64,6 +64,8 @@ #if defined(_WIN32) #include "lldb/Host/windows/ProcessLauncherWindows.h" +#elif defined(__ANDROID__) || defined(__ANDROID_NDK__) +#include "lldb/Host/android/ProcessLauncherAndroid.h" #else #include "lldb/Host/posix/ProcessLauncherPosix.h" #endif @@ -111,7 +113,7 @@ Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, vo return ThreadLauncher::LaunchThread(thread_name, MonitorChildProcessThreadFunction, info_ptr, NULL); } -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) +#ifndef __linux__ //------------------------------------------------------------------ // Scoped class that will disable thread canceling when it is // constructed, and exception safely restore the previous value it @@ -138,7 +140,32 @@ public: private: int m_old_state; // Save the old cancelability state. }; -#endif // __ANDROID_NDK__ +#endif // __linux__ + +#ifdef __linux__ +static thread_local volatile sig_atomic_t g_usr1_called; + +static void +SigUsr1Handler (int) +{ + g_usr1_called = 1; +} +#endif // __linux__ + +static bool +CheckForMonitorCancellation() +{ +#ifdef __linux__ + if (g_usr1_called) + { + g_usr1_called = 0; + return true; + } +#else + ::pthread_testcancel (); +#endif + return false; +} static thread_result_t MonitorChildProcessThreadFunction (void *arg) @@ -165,21 +192,29 @@ MonitorChildProcessThreadFunction (void *arg) #endif const int options = __WALL; +#ifdef __linux__ + // This signal is only used to interrupt the thread from waitpid + struct sigaction sigUsr1Action; + memset(&sigUsr1Action, 0, sizeof(sigUsr1Action)); + sigUsr1Action.sa_handler = SigUsr1Handler; + ::sigaction(SIGUSR1, &sigUsr1Action, nullptr); +#endif // __linux__ + while (1) { log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i)...", function, pid, options); - // Wait for all child processes -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) - ::pthread_testcancel (); -#endif + if (CheckForMonitorCancellation ()) + break; + // Get signals from all children with same process group of pid const ::pid_t wait_pid = ::waitpid (pid, &status, options); -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) - ::pthread_testcancel (); -#endif + + if (CheckForMonitorCancellation ()) + break; + if (wait_pid == -1) { if (errno == EINTR) @@ -224,7 +259,7 @@ MonitorChildProcessThreadFunction (void *arg) // Scope for pthread_cancel_disabler { -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) +#ifndef __linux__ ScopedPThreadCancelDisabler pthread_cancel_disabler; #endif @@ -388,28 +423,6 @@ Host::GetSignalAsCString (int signo) #endif -void -Host::WillTerminate () -{ -} - -#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) // see macosx/Host.mm - -void -Host::Backtrace (Stream &strm, uint32_t max_frames) -{ - // TODO: Is there a way to backtrace the current process on other systems? -} - -size_t -Host::GetEnvironment (StringList &env) -{ - // TODO: Is there a way to the host environment for this process on other systems? - return 0; -} - -#endif // #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) - #ifndef _WIN32 lldb::thread_key_t @@ -463,8 +476,6 @@ Host::GetModuleFileSpecForHostAddress (const void *host_addr) if (info.dli_fname) module_filespec.SetFile(info.dli_fname, true); } -#else - assert(false && "dladdr() not supported on Android"); #endif return module_filespec; } @@ -522,13 +533,25 @@ MonitorShellCommand (void *callback_baton, } Error -Host::RunShellCommand (const char *command, - const char *working_dir, - int *status_ptr, - int *signo_ptr, - std::string *command_output_ptr, - uint32_t timeout_sec, - bool run_in_default_shell) +Host::RunShellCommand(const char *command, + const FileSpec &working_dir, + int *status_ptr, + int *signo_ptr, + std::string *command_output_ptr, + uint32_t timeout_sec, + bool run_in_default_shell) +{ + return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr, command_output_ptr, timeout_sec, run_in_default_shell); +} + +Error +Host::RunShellCommand(const Args &args, + const FileSpec &working_dir, + int *status_ptr, + int *signo_ptr, + std::string *command_output_ptr, + uint32_t timeout_sec, + bool run_in_default_shell) { Error error; ProcessLaunchInfo launch_info; @@ -537,10 +560,10 @@ Host::RunShellCommand (const char *command, { // Run the command in a shell launch_info.SetShell(HostInfo::GetDefaultShell()); - launch_info.GetArguments().AppendArgument(command); + launch_info.GetArguments().AppendArguments(args); const bool localhost = true; const bool will_debug = false; - const bool first_arg_is_full_shell_command = true; + const bool first_arg_is_full_shell_command = false; launch_info.ConvertArgumentsForLaunchingInShell (error, localhost, will_debug, @@ -550,7 +573,6 @@ Host::RunShellCommand (const char *command, else { // No shell, just run it - Args args (command); const bool first_arg_is_executable = true; launch_info.SetArguments(args, first_arg_is_executable); } @@ -558,7 +580,7 @@ Host::RunShellCommand (const char *command, if (working_dir) launch_info.SetWorkingDirectory(working_dir); llvm::SmallString<PATH_MAX> output_file_path; - + if (command_output_ptr) { // Create a temporary file to get the stdout/stderr and redirect the @@ -575,11 +597,13 @@ Host::RunShellCommand (const char *command, llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "", output_file_path); } } - + + FileSpec output_file_spec{output_file_path.c_str(), false}; + launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); - if (!output_file_path.empty()) + if (output_file_spec) { - launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path.c_str(), false, true); + launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false, true); launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); } else @@ -596,10 +620,10 @@ Host::RunShellCommand (const char *command, error = LaunchProcess (launch_info); const lldb::pid_t pid = launch_info.GetProcessID(); - + if (error.Success() && pid == LLDB_INVALID_PROCESS_ID) error.SetErrorString("failed to get process ID"); - + if (error.Success()) { // The process successfully launched, so we can defer ownership of @@ -618,7 +642,7 @@ Host::RunShellCommand (const char *command, if (timed_out) { error.SetErrorString("timed out waiting for shell command to complete"); - + // Kill the process since it didn't complete within the timeout specified Kill (pid, SIGKILL); // Wait for the monitor callback to get the message @@ -631,15 +655,14 @@ Host::RunShellCommand (const char *command, { if (status_ptr) *status_ptr = shell_info->status; - + if (signo_ptr) *signo_ptr = shell_info->signo; - + if (command_output_ptr) { command_output_ptr->clear(); - FileSpec file_spec(output_file_path.c_str(), File::eOpenOptionRead); - uint64_t file_size = file_spec.GetByteSize(); + uint64_t file_size = output_file_spec.GetByteSize(); if (file_size > 0) { if (file_size > command_output_ptr->max_size()) @@ -648,8 +671,10 @@ Host::RunShellCommand (const char *command, } else { - command_output_ptr->resize(file_size); - file_spec.ReadFileContents(0, &((*command_output_ptr)[0]), command_output_ptr->size(), &error); + std::vector<char> command_output(file_size); + output_file_spec.ReadFileContents(0, command_output.data(), file_size, &error); + if (error.Success()) + command_output_ptr->assign(command_output.data(), file_size); } } } @@ -657,27 +682,25 @@ Host::RunShellCommand (const char *command, shell_info->can_delete.SetValue(true, eBroadcastAlways); } - FileSpec output_file_spec(output_file_path.c_str(), false); if (FileSystem::GetFileExists(output_file_spec)) - FileSystem::Unlink(output_file_path.c_str()); + FileSystem::Unlink(output_file_spec); // Handshake with the monitor thread, or just let it know in advance that // it can delete "shell_info" in case we timed out and were not able to kill // the process... return error; } - // LaunchProcessPosixSpawn for Apple, Linux, FreeBSD and other GLIBC // systems #if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) || defined(__NetBSD__) +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) // this method needs to be visible to macosx/Host.cpp and // common/Host.cpp. short Host::GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) { -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; #if defined (__APPLE__) @@ -720,17 +743,12 @@ Host::GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) #endif #endif // #if defined (__APPLE__) return flags; -#else - assert(false && "Host::GetPosixspawnFlags() not supported on Android"); - return 0; -#endif } Error Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid) { Error error; -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); posix_spawnattr_t attr; @@ -816,16 +834,18 @@ Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &lau current_dir[0] = '\0'; #endif - const char *working_dir = launch_info.GetWorkingDirectory(); + FileSpec working_dir{launch_info.GetWorkingDirectory()}; if (working_dir) { #if defined (__APPLE__) // Set the working directory on this thread only - if (__pthread_chdir (working_dir) < 0) { + if (__pthread_chdir(working_dir.GetCString()) < 0) { if (errno == ENOENT) { - error.SetErrorStringWithFormat("No such file or directory: %s", working_dir); + error.SetErrorStringWithFormat("No such file or directory: %s", + working_dir.GetCString()); } else if (errno == ENOTDIR) { - error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir); + error.SetErrorStringWithFormat("Path doesn't name a directory: %s", + working_dir.GetCString()); } else { error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution."); } @@ -839,10 +859,11 @@ Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &lau return error; } - if (::chdir(working_dir) == -1) + if (::chdir(working_dir.GetCString()) == -1) { error.SetError(errno, eErrorTypePOSIX); - error.LogIfError(log, "unable to change working directory to %s", working_dir); + error.LogIfError(log, "unable to change working directory to %s", + working_dir.GetCString()); return error; } #endif @@ -920,9 +941,6 @@ Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &lau } #endif } -#else - error.SetErrorString("Host::LaunchProcessPosixSpawn() not supported on Android"); -#endif return error; } @@ -930,7 +948,6 @@ Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &lau bool Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Error &error) { -#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) if (info == NULL) return false; @@ -993,12 +1010,9 @@ Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log * break; } return error.Success(); -#else - error.SetErrorString("Host::AddPosixSpawnFileAction() not supported on Android"); - return false; -#endif } -#endif // LaunchProcedssPosixSpawn: Apple, Linux, FreeBSD and other GLIBC systems +#endif // !defined(__ANDROID__) && !defined(__ANDROID_NDK__) +#endif // defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) || defined(__NetBSD__) #if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) || defined(__NetBSD__) || defined(_WIN32) // The functions below implement process launching via posix_spawn() for Linux, @@ -1010,6 +1024,8 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info) std::unique_ptr<ProcessLauncher> delegate_launcher; #if defined(_WIN32) delegate_launcher.reset(new ProcessLauncherWindows()); +#elif defined(__ANDROID__) || defined(__ANDROID_NDK__) + delegate_launcher.reset(new ProcessLauncherAndroid()); #else delegate_launcher.reset(new ProcessLauncherPosix()); #endif @@ -1054,7 +1070,7 @@ Host::SetCrashDescription (const char *description) #endif -#if !defined (__linux__) && !defined (__FreeBSD__) && !defined (__NetBSD__) +#if !defined (__linux__) && !defined (__FreeBSD__) && !defined(__FreeBSD_kernel__) && !defined (__NetBSD__) const lldb_private::UnixSignalsSP& Host::GetUnixSignals () diff --git a/source/Host/common/HostInfoBase.cpp b/source/Host/common/HostInfoBase.cpp index 9816c1ebf080..e969e33190eb 100644 --- a/source/Host/common/HostInfoBase.cpp +++ b/source/Host/common/HostInfoBase.cpp @@ -40,7 +40,7 @@ namespace // Remove the LLDB temporary directory if we have one. Set "recurse" to // true to all files that were created for the LLDB process can be cleaned up. - FileSystem::DeleteDirectory(tmpdir_file_spec.GetDirectory().GetCString(), true); + FileSystem::DeleteDirectory(tmpdir_file_spec, true); } //---------------------------------------------------------------------- @@ -68,7 +68,8 @@ namespace FileSpec m_lldb_clang_resource_dir; FileSpec m_lldb_system_plugin_dir; FileSpec m_lldb_user_plugin_dir; - FileSpec m_lldb_tmp_dir; + FileSpec m_lldb_process_tmp_dir; + FileSpec m_lldb_global_tmp_dir; }; HostInfoBaseFields *g_fields = nullptr; @@ -263,13 +264,27 @@ HostInfoBase::GetLLDBPath(lldb::PathType type, FileSpec &file_spec) static std::once_flag g_once_flag; static bool success = false; std::call_once(g_once_flag, []() { - success = HostInfo::ComputeTempFileDirectory (g_fields->m_lldb_tmp_dir); + success = HostInfo::ComputeProcessTempFileDirectory (g_fields->m_lldb_process_tmp_dir); Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); if (log) - log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBTempSystemDir) => '%s'", g_fields->m_lldb_tmp_dir.GetPath().c_str()); + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBTempSystemDir) => '%s'", g_fields->m_lldb_process_tmp_dir.GetPath().c_str()); }); if (success) - result = &g_fields->m_lldb_tmp_dir; + result = &g_fields->m_lldb_process_tmp_dir; + } + break; + case lldb::ePathTypeGlobalLLDBTempSystemDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeGlobalTempFileDirectory (g_fields->m_lldb_global_tmp_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeGlobalLLDBTempSystemDir) => '%s'", g_fields->m_lldb_global_tmp_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_global_tmp_dir; } break; } @@ -305,35 +320,57 @@ HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) } bool -HostInfoBase::ComputeTempFileDirectory(FileSpec &file_spec) +HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) +{ + FileSpec temp_file_spec; + if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec)) + return false; + + std::string pid_str{std::to_string(Host::GetCurrentProcessID())}; + temp_file_spec.AppendPathComponent(pid_str); + if (!FileSystem::MakeDirectory(temp_file_spec, eFilePermissionsDirectoryDefault).Success()) + return false; + + // Make an atexit handler to clean up the process specify LLDB temp dir + // and all of its contents. + ::atexit(CleanupProcessSpecificLLDBTempDir); + file_spec.GetDirectory().SetCString(temp_file_spec.GetCString()); + return true; +} + +bool +HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) { + file_spec.Clear(); + const char *tmpdir_cstr = getenv("TMPDIR"); - if (tmpdir_cstr == NULL) + if (tmpdir_cstr == nullptr) { tmpdir_cstr = getenv("TMP"); - if (tmpdir_cstr == NULL) + if (tmpdir_cstr == nullptr) tmpdir_cstr = getenv("TEMP"); } if (!tmpdir_cstr) return false; - FileSpec temp_file_spec(tmpdir_cstr, false); - temp_file_spec.AppendPathComponent("lldb"); - if (!FileSystem::MakeDirectory(temp_file_spec.GetPath().c_str(), eFilePermissionsDirectoryDefault).Success()) + file_spec = FileSpec(tmpdir_cstr, false); + return true; +} + +bool +HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) +{ + file_spec.Clear(); + + FileSpec temp_file_spec; + if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec)) return false; - std::string pid_str; - llvm::raw_string_ostream pid_stream(pid_str); - pid_stream << Host::GetCurrentProcessID(); - temp_file_spec.AppendPathComponent(pid_stream.str().c_str()); - std::string final_path = temp_file_spec.GetPath(); - if (!FileSystem::MakeDirectory(final_path.c_str(), eFilePermissionsDirectoryDefault).Success()) + temp_file_spec.AppendPathComponent("lldb"); + if (!FileSystem::MakeDirectory(temp_file_spec, eFilePermissionsDirectoryDefault).Success()) return false; - // Make an atexit handler to clean up the process specify LLDB temp dir - // and all of its contents. - ::atexit(CleanupProcessSpecificLLDBTempDir); - file_spec.GetDirectory().SetCStringWithLength(final_path.c_str(), final_path.size()); + file_spec.GetDirectory().SetCString(temp_file_spec.GetCString()); return true; } @@ -367,7 +404,7 @@ HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) { - llvm::Triple triple(llvm::sys::getDefaultTargetTriple()); + llvm::Triple triple(llvm::sys::getProcessTriple()); arch_32.Clear(); arch_64.Clear(); @@ -386,6 +423,7 @@ HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_6 case llvm::Triple::aarch64: case llvm::Triple::mips64: + case llvm::Triple::mips64el: case llvm::Triple::sparcv9: arch_64.SetTriple(triple); break; diff --git a/source/Host/common/LockFileBase.cpp b/source/Host/common/LockFileBase.cpp new file mode 100644 index 000000000000..f74694561184 --- /dev/null +++ b/source/Host/common/LockFileBase.cpp @@ -0,0 +1,124 @@ +//===-- LockFileBase.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/LockFileBase.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + +Error +AlreadyLocked () +{ + return Error ("Already locked"); +} + +Error +NotLocked () +{ + return Error ("Not locked"); +} + +} + +LockFileBase::LockFileBase (int fd) : + m_fd (fd), + m_locked (false), + m_start (0), + m_len (0) +{ + +} + +bool +LockFileBase::IsLocked () const +{ + return m_locked; +} + +Error +LockFileBase::WriteLock (const uint64_t start, const uint64_t len) +{ + return DoLock ([&] (const uint64_t start, const uint64_t len) + { + return DoWriteLock (start, len); + }, start, len); +} + +Error +LockFileBase::TryWriteLock (const uint64_t start, const uint64_t len) +{ + return DoLock ([&] (const uint64_t start, const uint64_t len) + { + return DoTryWriteLock (start, len); + }, start, len); +} + +Error +LockFileBase::ReadLock (const uint64_t start, const uint64_t len) +{ + return DoLock ([&] (const uint64_t start, const uint64_t len) + { + return DoReadLock (start, len); + }, start, len); +} + +Error +LockFileBase::TryReadLock (const uint64_t start, const uint64_t len) +{ + return DoLock ([&] (const uint64_t start, const uint64_t len) + { + return DoTryReadLock (start, len); + }, start, len); + +} + +Error +LockFileBase::Unlock () +{ + if (!IsLocked ()) + return NotLocked (); + + const auto error = DoUnlock (); + if (error.Success ()) + { + m_locked = false; + m_start = 0; + m_len = 0; + } + return error; +} + +bool +LockFileBase::IsValidFile () const +{ + return m_fd != -1; +} + +Error +LockFileBase::DoLock (const Locker &locker, const uint64_t start, const uint64_t len) +{ + if (!IsValidFile ()) + return Error("File is invalid"); + + if (IsLocked ()) + return AlreadyLocked (); + + const auto error = locker (start, len); + if (error.Success ()) + { + m_locked = true; + m_start = start; + m_len = len; + } + + return error; +} diff --git a/source/Host/common/NativeBreakpointList.cpp b/source/Host/common/NativeBreakpointList.cpp index 94d0b3756da4..52b9baf5f537 100644 --- a/source/Host/common/NativeBreakpointList.cpp +++ b/source/Host/common/NativeBreakpointList.cpp @@ -12,6 +12,7 @@ #include "lldb/Core/Log.h" #include "lldb/Host/common/NativeBreakpoint.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" using namespace lldb; using namespace lldb_private; @@ -197,3 +198,24 @@ NativeBreakpointList::GetBreakpoint (lldb::addr_t addr, NativeBreakpointSP &brea return Error (); } +Error +NativeBreakpointList::RemoveTrapsFromBuffer(lldb::addr_t addr, void *buf, size_t size) const +{ + for (const auto &map : m_breakpoints) + { + lldb::addr_t bp_addr = map.first; + // Breapoint not in range, ignore + if (bp_addr < addr || addr + size <= bp_addr) + continue; + const auto &bp_sp = map.second; + // Not software breakpoint, ignore + if (!bp_sp->IsSoftwareBreakpoint()) + continue; + auto software_bp_sp = std::static_pointer_cast<SoftwareBreakpoint>(bp_sp); + auto opcode_addr = static_cast<char *>(buf) + bp_addr - addr; + auto saved_opcodes = software_bp_sp->m_saved_opcodes; + auto opcode_size = software_bp_sp->m_opcode_size; + ::memcpy(opcode_addr, saved_opcodes, opcode_size); + } + return Error(); +} diff --git a/source/Host/common/NativeProcessProtocol.cpp b/source/Host/common/NativeProcessProtocol.cpp index ff7310d2d45a..35003f5d0207 100644 --- a/source/Host/common/NativeProcessProtocol.cpp +++ b/source/Host/common/NativeProcessProtocol.cpp @@ -435,3 +435,9 @@ NativeProcessProtocol::DoStopIDBumped (uint32_t /* newBumpId */) { // Default implementation does nothing. } + +void +NativeProcessProtocol::Terminate () +{ + // Default implementation does nothing. +} diff --git a/source/Host/common/NativeRegisterContext.cpp b/source/Host/common/NativeRegisterContext.cpp index 42a9c91a63a0..37040efc4ece 100644 --- a/source/Host/common/NativeRegisterContext.cpp +++ b/source/Host/common/NativeRegisterContext.cpp @@ -12,8 +12,6 @@ #include "lldb/Core/Log.h" #include "lldb/Core/RegisterValue.h" -#include "lldb/lldb-private-log.h" - #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" @@ -145,6 +143,12 @@ NativeRegisterContext::GetPC (lldb::addr_t fail_value) return retval; } +lldb::addr_t +NativeRegisterContext::GetPCfromBreakpointLocation (lldb::addr_t fail_value) +{ + return GetPC (fail_value); +} + Error NativeRegisterContext::SetPC (lldb::addr_t pc) { @@ -303,6 +307,33 @@ NativeRegisterContext::ClearAllHardwareWatchpoints () return Error ("not implemented"); } +Error +NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit) +{ + is_hit = false; + return Error ("not implemented"); +} + +Error +NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + wp_index = LLDB_INVALID_INDEX32; + return Error ("not implemented"); +} + +Error +NativeRegisterContext::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) +{ + is_vacant = false; + return Error ("not implemented"); +} + +lldb::addr_t +NativeRegisterContext::GetWatchpointAddress (uint32_t wp_index) +{ + return LLDB_INVALID_ADDRESS; +} + bool NativeRegisterContext::HardwareSingleStep (bool enable) { @@ -313,7 +344,7 @@ Error NativeRegisterContext::ReadRegisterValueFromMemory ( const RegisterInfo *reg_info, lldb::addr_t src_addr, - lldb::addr_t src_len, + size_t src_len, RegisterValue ®_value) { Error error; @@ -346,11 +377,12 @@ NativeRegisterContext::ReadRegisterValueFromMemory ( return error; } - const lldb::addr_t dst_len = reg_info->byte_size; + const size_t dst_len = reg_info->byte_size; if (src_len > dst_len) { - error.SetErrorStringWithFormat("%" PRIu64 " bytes is too big to store in register %s (%" PRIu64 " bytes)", src_len, reg_info->name, dst_len); + error.SetErrorStringWithFormat("%" PRIu64 " bytes is too big to store in register %s (%" PRIu64 " bytes)", + static_cast<uint64_t>(src_len), reg_info->name, static_cast<uint64_t>(dst_len)); return error; } @@ -364,7 +396,7 @@ NativeRegisterContext::ReadRegisterValueFromMemory ( uint8_t src[RegisterValue::kMaxRegisterByteSize]; // Read the memory - lldb::addr_t bytes_read; + size_t bytes_read; error = process_sp->ReadMemory (src_addr, src, src_len, bytes_read); if (error.Fail ()) return error; @@ -373,7 +405,8 @@ NativeRegisterContext::ReadRegisterValueFromMemory ( if (bytes_read != src_len) { // This might happen if we read _some_ bytes but not all - error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes", bytes_read, src_len); + error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes", + static_cast<uint64_t>(bytes_read), static_cast<uint64_t>(src_len)); return error; } @@ -403,7 +436,7 @@ Error NativeRegisterContext::WriteRegisterValueToMemory ( const RegisterInfo *reg_info, lldb::addr_t dst_addr, - lldb::addr_t dst_len, + size_t dst_len, const RegisterValue ®_value) { @@ -422,7 +455,7 @@ NativeRegisterContext::WriteRegisterValueToMemory ( if (!process_sp->GetByteOrder (byte_order)) return Error ("NativeProcessProtocol::GetByteOrder () failed"); - const lldb::addr_t bytes_copied = reg_value.GetAsMemoryData ( + const size_t bytes_copied = reg_value.GetAsMemoryData ( reg_info, dst, dst_len, @@ -437,15 +470,16 @@ NativeRegisterContext::WriteRegisterValueToMemory ( } else { - lldb::addr_t bytes_written; - error = process_sp->WriteMemory (dst_addr, dst, bytes_copied, bytes_written); + size_t bytes_written; + error = process_sp->WriteMemory(dst_addr, dst, bytes_copied, bytes_written); if (error.Fail ()) return error; if (bytes_written != bytes_copied) { // This might happen if we read _some_ bytes but not all - error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64 " bytes", bytes_written, bytes_copied); + error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64 " bytes", + static_cast<uint64_t>(bytes_written), static_cast<uint64_t>(bytes_copied)); } } } diff --git a/source/Host/common/Socket.cpp b/source/Host/common/Socket.cpp index b5559fffb45d..f7e93c634a12 100644 --- a/source/Host/common/Socket.cpp +++ b/source/Host/common/Socket.cpp @@ -80,6 +80,25 @@ NativeSocket Accept(NativeSocket sockfd, struct sockaddr *addr, socklen_t *addrl return ::accept (sockfd, addr, addrlen); #endif } + +void SetLastError(Error &error) +{ +#if defined(_WIN32) + error.SetError(::WSAGetLastError(), lldb::eErrorTypeWin32); +#else + error.SetErrorToErrno(); +#endif +} + +bool IsInterrupted() +{ +#if defined(_WIN32) + return ::WSAGetLastError() == WSAEINTR; +#else + return errno == EINTR; +#endif +} + } Socket::Socket(NativeSocket socket, SocketProtocol protocol, bool should_close) @@ -116,8 +135,7 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inh sock = CreateSocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, child_processes_inherit); if (sock == kInvalidSocketValue) { - // TODO: On Windows, use WSAGetLastError(). - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -143,9 +161,8 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inh inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); if (inet_pton_result <= 0) { - // TODO: On Windows, use WSAGetLastError() if (inet_pton_result == -1) - error.SetErrorToErrno(); + SetLastError(error); else error.SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str()); @@ -155,8 +172,7 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inh if (-1 == ::connect (sock, (const struct sockaddr *)&sa, sizeof(sa))) { - // TODO: On Windows, use WSAGetLastError() - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -167,7 +183,12 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inh return error; } -Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket, Predicate<uint16_t>* predicate) +Error Socket::TcpListen( + llvm::StringRef host_and_port, + bool child_processes_inherit, + Socket *&socket, + Predicate<uint16_t>* predicate, + int backlog) { std::unique_ptr<Socket> listen_socket; NativeSocket listen_sock = kInvalidSocketValue; @@ -179,7 +200,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inhe listen_sock = ::CreateSocket (family, socktype, protocol, child_processes_inherit); if (listen_sock == kInvalidSocketValue) { - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -204,16 +225,14 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inhe int err = ::bind (listen_sock, anyaddr, anyaddr.GetLength()); if (err == -1) { - // TODO: On Windows, use WSAGetLastError() - error.SetErrorToErrno(); + SetLastError (error); return error; } - err = ::listen (listen_sock, 1); + err = ::listen (listen_sock, backlog); if (err == -1) { - // TODO: On Windows, use WSAGetLastError() - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -284,8 +303,7 @@ Error Socket::BlockingAccept(llvm::StringRef host_and_port, bool child_processes if (sock == kInvalidSocketValue) { - // TODO: On Windows, use WSAGetLastError() - error.SetErrorToErrno(); + SetLastError (error); break; } @@ -349,8 +367,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inh if (final_recv_fd == kInvalidSocketValue) { // Socket creation failed... - // TODO: On Windows, use WSAGetLastError(). - error.SetErrorToErrno(); + SetLastError (error); } else { @@ -363,8 +380,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inh if (::bind (final_recv_fd, addr, addr.GetLength()) == -1) { // Bind failed... - // TODO: On Windows use WSAGetLastError() - error.SetErrorToErrno(); + SetLastError (error); } } @@ -415,8 +431,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inh if (final_send_fd == kInvalidSocketValue) { - // TODO: On Windows, use WSAGetLastError(). - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -437,7 +452,7 @@ Error Socket::UnixDomainConnect(llvm::StringRef name, bool child_processes_inher int fd = ::CreateSocket (AF_UNIX, SOCK_STREAM, 0, child_processes_inherit); if (fd == kInvalidSocketValue) { - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -452,7 +467,7 @@ Error Socket::UnixDomainConnect(llvm::StringRef name, bool child_processes_inher if (::connect (fd, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0) { - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -476,7 +491,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inheri listen_fd = ::CreateSocket (AF_UNIX, SOCK_STREAM, 0, child_processes_inherit); if (listen_fd == kInvalidSocketValue) { - error.SetErrorToErrno(); + SetLastError (error); return error; } @@ -489,7 +504,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inheri saddr_un.sun_len = SUN_LEN (&saddr_un); #endif - FileSystem::Unlink(name.data()); + FileSystem::Unlink(FileSpec{name, true}); bool success = false; if (::bind (listen_fd, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0) { @@ -506,7 +521,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inheri if (!success) { - error.SetErrorToErrno(); + SetLastError (error); return error; } // We are done with the listen port @@ -580,12 +595,11 @@ Error Socket::Read (void *buf, size_t &num_bytes) do { bytes_received = ::recv (m_socket, static_cast<char *>(buf), num_bytes, 0); - // TODO: Use WSAGetLastError on windows. - } while (bytes_received < 0 && errno == EINTR); + } while (bytes_received < 0 && IsInterrupted ()); if (bytes_received < 0) { - error.SetErrorToErrno(); + SetLastError (error); num_bytes = 0; } else @@ -623,13 +637,11 @@ Error Socket::Write (const void *buf, size_t &num_bytes) } else bytes_sent = ::send (m_socket, static_cast<const char *>(buf), num_bytes, 0); - // TODO: Use WSAGetLastError on windows. - } while (bytes_sent < 0 && errno == EINTR); + } while (bytes_sent < 0 && IsInterrupted ()); if (bytes_sent < 0) { - // TODO: On Windows, use WSAGEtLastError. - error.SetErrorToErrno(); + SetLastError (error); num_bytes = 0; } else @@ -675,8 +687,7 @@ Error Socket::Close() m_socket = kInvalidSocketValue; if (!success) { - // TODO: On Windows, use WSAGetLastError(). - error.SetErrorToErrno(); + SetLastError (error); } return error; @@ -699,7 +710,7 @@ int Socket::SetOption(int level, int option_name, int option_value) uint16_t Socket::GetLocalPortNumber(const NativeSocket& socket) { // We bound to port zero, so we need to figure out which port we actually bound to - if (socket >= 0) + if (socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength (); @@ -718,7 +729,7 @@ uint16_t Socket::GetLocalPortNumber() const std::string Socket::GetLocalIPAddress () const { // We bound to port zero, so we need to figure out which port we actually bound to - if (m_socket >= 0) + if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength (); @@ -730,7 +741,7 @@ std::string Socket::GetLocalIPAddress () const uint16_t Socket::GetRemotePortNumber () const { - if (m_socket >= 0) + if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength (); @@ -743,7 +754,7 @@ uint16_t Socket::GetRemotePortNumber () const std::string Socket::GetRemoteIPAddress () const { // We bound to port zero, so we need to figure out which port we actually bound to - if (m_socket >= 0) + if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength (); diff --git a/source/Host/common/SocketAddress.cpp b/source/Host/common/SocketAddress.cpp index fd7fbac952e3..3ab6cfeec4a0 100644 --- a/source/Host/common/SocketAddress.cpp +++ b/source/Host/common/SocketAddress.cpp @@ -9,10 +9,13 @@ #include "lldb/Host/SocketAddress.h" #include <stddef.h> +#include <stdio.h> // C Includes #if !defined(_WIN32) #include <arpa/inet.h> +#else +#include "lldb/Host/windows/win32.h" #endif #include <assert.h> #include <string.h> @@ -45,8 +48,7 @@ const char* inet_ntop(int af, const void * src, const char* formatted = inet_ntoa(*static_cast<const in_addr*>(src)); if (formatted && strlen(formatted) < size) { - strncpy(dst, formatted, size); - return dst; + return ::strcpy(dst, formatted); } } return nullptr; @@ -54,15 +56,14 @@ const char* inet_ntop(int af, const void * src, { char tmp[INET6_ADDRSTRLEN] = {0}; const uint16_t* src16 = static_cast<const uint16_t*>(src); - int full_size = _snprintf(tmp, sizeof(tmp), + int full_size = ::snprintf(tmp, sizeof(tmp), "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(src16[0]), ntohs(src16[1]), ntohs(src16[2]), ntohs(src16[3]), ntohs(src16[4]), ntohs(src16[5]), ntohs(src16[6]), ntohs(src16[7]) ); if (full_size < static_cast<int>(size)) { - strncpy(dst,tmp,size); - return dst; + return ::strcpy(dst, tmp); } return nullptr; } diff --git a/source/Host/common/SoftwareBreakpoint.cpp b/source/Host/common/SoftwareBreakpoint.cpp index 67f472b88f5c..5a6f78372b3f 100644 --- a/source/Host/common/SoftwareBreakpoint.cpp +++ b/source/Host/common/SoftwareBreakpoint.cpp @@ -101,9 +101,9 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr); // Save the original opcodes by reading them so we can restore later. - lldb::addr_t bytes_read = 0; + size_t bytes_read = 0; - Error error = process.ReadMemory(addr, saved_opcode_bytes, static_cast<lldb::addr_t> (bp_opcode_size), bytes_read); + Error error = process.ReadMemory(addr, saved_opcode_bytes, bp_opcode_size, bytes_read); if (error.Fail ()) { if (log) @@ -112,11 +112,11 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll } // Ensure we read as many bytes as we expected. - if (bytes_read != static_cast<lldb::addr_t> (bp_opcode_size)) + if (bytes_read != bp_opcode_size) { if (log) - log->Printf ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, bytes_read); - return Error ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, bytes_read); + log->Printf ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, (uint64_t)bytes_read); + return Error ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, (uint64_t)bytes_read); } // Log what we read. @@ -125,13 +125,15 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll int i = 0; for (const uint8_t *read_byte = saved_opcode_bytes; read_byte < saved_opcode_bytes + bp_opcode_size; ++read_byte) { - log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " ovewriting byte index %d (was 0x%x)", __FUNCTION__, addr, i++, static_cast<int> (*read_byte)); + log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64 + " ovewriting byte index %d (was 0x%hhx)", + __FUNCTION__, addr, i++, *read_byte); } } // Write a software breakpoint in place of the original opcode. - lldb::addr_t bytes_written = 0; - error = process.WriteMemory (addr, bp_opcode_bytes, static_cast<lldb::addr_t> (bp_opcode_size), bytes_written); + size_t bytes_written = 0; + error = process.WriteMemory(addr, bp_opcode_bytes, bp_opcode_size, bytes_written); if (error.Fail ()) { if (log) @@ -140,17 +142,17 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll } // Ensure we wrote as many bytes as we expected. - if (bytes_written != static_cast<lldb::addr_t> (bp_opcode_size)) + if (bytes_written != bp_opcode_size) { - error.SetErrorStringWithFormat("SoftwareBreakpoint::%s failed write memory while attempting to set breakpoint: attempted to write %lu bytes but only wrote %" PRIu64, __FUNCTION__, bp_opcode_size, bytes_written); + error.SetErrorStringWithFormat("SoftwareBreakpoint::%s failed write memory while attempting to set breakpoint: attempted to write %lu bytes but only wrote %" PRIu64, __FUNCTION__, bp_opcode_size, (uint64_t)bytes_written); if (log) log->PutCString (error.AsCString ()); return error; } uint8_t verify_bp_opcode_bytes [MAX_TRAP_OPCODE_SIZE]; - lldb::addr_t verify_bytes_read = 0; - error = process.ReadMemory(addr, verify_bp_opcode_bytes, static_cast<lldb::addr_t> (bp_opcode_size), verify_bytes_read); + size_t verify_bytes_read = 0; + error = process.ReadMemory(addr, verify_bp_opcode_bytes, bp_opcode_size, verify_bytes_read); if (error.Fail ()) { if (log) @@ -159,11 +161,11 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll } // Ensure we read as many verification bytes as we expected. - if (verify_bytes_read != static_cast<lldb::addr_t> (bp_opcode_size)) + if (verify_bytes_read != bp_opcode_size) { if (log) - log->Printf ("SoftwareBreakpoint::%s failed to read memory while attempting to verify breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, verify_bytes_read); - return Error ("SoftwareBreakpoint::%s failed to read memory while attempting to verify breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, verify_bytes_read); + log->Printf ("SoftwareBreakpoint::%s failed to read memory while attempting to verify breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, (uint64_t)verify_bytes_read); + return Error ("SoftwareBreakpoint::%s failed to read memory while attempting to verify breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, (uint64_t)verify_bytes_read); } if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, bp_opcode_size) != 0) @@ -223,26 +225,26 @@ SoftwareBreakpoint::DoDisable () assert (m_opcode_size <= sizeof (curr_break_op)); // Read the breakpoint opcode - lldb::addr_t bytes_read = 0; + size_t bytes_read = 0; error = m_process.ReadMemory (m_addr, curr_break_op, m_opcode_size, bytes_read); - if (error.Success () && (bytes_read < static_cast<lldb::addr_t> (m_opcode_size))) + if (error.Success() && bytes_read < m_opcode_size) { - error.SetErrorStringWithFormat ("SoftwareBreakpointr::%s addr=0x%" PRIx64 ": tried to read %lu bytes but only read %" PRIu64, __FUNCTION__, m_addr, m_opcode_size, bytes_read); + error.SetErrorStringWithFormat ("SoftwareBreakpointr::%s addr=0x%" PRIx64 ": tried to read %lu bytes but only read %" PRIu64, __FUNCTION__, m_addr, m_opcode_size, (uint64_t)bytes_read); } if (error.Success ()) { bool verify = false; - // Make sure we have the a breakpoint opcode exists at this address + // Make sure the breakpoint opcode exists at this address if (::memcmp (curr_break_op, m_trap_opcodes, m_opcode_size) == 0) { break_op_found = true; // We found a valid breakpoint opcode at this address, now restore // the saved opcode. - lldb::addr_t bytes_written = 0; + size_t bytes_written = 0; error = m_process.WriteMemory (m_addr, m_saved_opcodes, m_opcode_size, bytes_written); - if (error.Success () && (bytes_written < static_cast<lldb::addr_t> (m_opcode_size))) + if (error.Success() && bytes_written < m_opcode_size) { - error.SetErrorStringWithFormat ("SoftwareBreakpoint::%s addr=0x%" PRIx64 ": tried to write %lu bytes but only wrote %" PRIu64, __FUNCTION__, m_addr, m_opcode_size, bytes_written); + error.SetErrorStringWithFormat ("SoftwareBreakpoint::%s addr=0x%" PRIx64 ": tried to write %lu bytes but only wrote %" PRIu64, __FUNCTION__, m_addr, m_opcode_size, (uint64_t)bytes_written); } if (error.Success ()) { @@ -262,11 +264,11 @@ SoftwareBreakpoint::DoDisable () assert (m_opcode_size <= sizeof (verify_opcode)); // Verify that our original opcode made it back to the inferior - lldb::addr_t verify_bytes_read = 0; + size_t verify_bytes_read = 0; error = m_process.ReadMemory (m_addr, verify_opcode, m_opcode_size, verify_bytes_read); - if (error.Success () && (verify_bytes_read < static_cast<lldb::addr_t> (m_opcode_size))) + if (error.Success() && verify_bytes_read < m_opcode_size) { - error.SetErrorStringWithFormat ("SoftwareBreakpoint::%s addr=0x%" PRIx64 ": tried to read %lu verification bytes but only read %" PRIu64, __FUNCTION__, m_addr, m_opcode_size, verify_bytes_read); + error.SetErrorStringWithFormat ("SoftwareBreakpoint::%s addr=0x%" PRIx64 ": tried to read %lu verification bytes but only read %" PRIu64, __FUNCTION__, m_addr, m_opcode_size, (uint64_t)verify_bytes_read); } if (error.Success ()) { @@ -279,7 +281,9 @@ SoftwareBreakpoint::DoDisable () int i = 0; for (const uint8_t *verify_byte = verify_opcode; verify_byte < verify_opcode + m_opcode_size; ++verify_byte) { - log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " replaced byte index %d with 0x%x", __FUNCTION__, m_addr, i++, static_cast<int> (*verify_byte)); + log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64 + " replaced byte index %d with 0x%hhx", + __FUNCTION__, m_addr, i++, *verify_byte); } log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- SUCCESS", __FUNCTION__, m_addr); } diff --git a/source/Host/common/Symbols.cpp b/source/Host/common/Symbols.cpp index 41f465abc836..2b63f46c02e6 100644 --- a/source/Host/common/Symbols.cpp +++ b/source/Host/common/Symbols.cpp @@ -18,137 +18,271 @@ #include "lldb/Core/UUID.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/SafeMachO.h" + +#include "llvm/Support/FileSystem.h" + +// From MacOSX system header "mach/machine.h" +typedef int cpu_type_t; +typedef int cpu_subtype_t; using namespace lldb; using namespace lldb_private; +using namespace llvm::MachO; -#if defined (__linux__) || defined (__FreeBSD__) +#if defined(__APPLE__) -FileSpec -Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) -{ - // FIXME - return FileSpec(); -} +// Forward declaration of method defined in source/Host/macosx/Symbols.cpp +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + FileSpec *out_exec_fspec, // If non-NULL, try and find the executable + FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file +); -FileSpec -Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) -{ - const char *symbol_filename = module_spec.GetSymbolFileSpec().GetFilename().AsCString(); - if (!symbol_filename || !symbol_filename[0]) - return FileSpec(); +#else - FileSpecList debug_file_search_paths (Target::GetDefaultDebugFileSearchPaths()); - - // Add module directory. - const ConstString &file_dir = module_spec.GetFileSpec().GetDirectory(); - debug_file_search_paths.AppendIfUnique (FileSpec(file_dir.AsCString("."), true)); - - // Add current working directory. - debug_file_search_paths.AppendIfUnique (FileSpec(".", true)); +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + FileSpec *out_exec_fspec, // If non-NULL, try and find the executable + FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file +) { + // Cannot find MacOSX files using debug symbols on non MacOSX. + return 0; +} - // Add /usr/lib/debug directory. - debug_file_search_paths.AppendIfUnique (FileSpec("/usr/lib/debug", true)); +#endif - std::string uuid_str; - const UUID &module_uuid = module_spec.GetUUID(); - if (module_uuid.IsValid()) +static bool +FileAtPathContainsArchAndUUID (const FileSpec &file_fspec, const ArchSpec *arch, const lldb_private::UUID *uuid) +{ + ModuleSpecList module_specs; + if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) { - // Some debug files are stored in the .build-id directory like this: - // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug - uuid_str = module_uuid.GetAsString(""); - uuid_str.insert (2, 1, '/'); - uuid_str = uuid_str + ".debug"; + ModuleSpec spec; + for (size_t i = 0; i < module_specs.GetSize(); ++i) + { + assert(module_specs.GetModuleSpecAtIndex(i, spec)); + if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && + (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) + { + return true; + } + } } + return false; +} - // Get directory of our module. Needed to check debug files like this: - // /usr/lib/debug/usr/lib/library.so.debug - std::string module_directory = module_spec.GetFileSpec().GetDirectory().AsCString(); - - size_t num_directories = debug_file_search_paths.GetSize(); - for (size_t idx = 0; idx < num_directories; ++idx) +static bool +LocateDSYMInVincinityOfExecutable (const ModuleSpec &module_spec, FileSpec &dsym_fspec) +{ + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + if (exec_fspec) { - FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex (idx); - dirspec.ResolvePath(); - if (!dirspec.Exists() || !dirspec.IsDirectory()) - continue; - - std::vector<std::string> files; - std::string dirname = dirspec.GetPath(); - - files.push_back (dirname + "/" + symbol_filename); - files.push_back (dirname + "/.debug/" + symbol_filename); - files.push_back (dirname + "/.build-id/" + uuid_str); - files.push_back (dirname + module_directory + "/" + symbol_filename); - - const uint32_t num_files = files.size(); - for (size_t idx_file = 0; idx_file < num_files; ++idx_file) + char path[PATH_MAX]; + if (exec_fspec->GetPath(path, sizeof(path))) { - const std::string &filename = files[idx_file]; - FileSpec file_spec (filename.c_str(), true); + // Make sure the module isn't already just a dSYM file... + if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) + { + size_t obj_file_path_length = strlen(path); + ::strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path) - strlen(path) - 1); + ::strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path) - strlen(path) - 1); - if (file_spec == module_spec.GetFileSpec()) - continue; + dsym_fspec.SetFile(path, false); - if (file_spec.Exists()) - { - lldb_private::ModuleSpecList specs; - const size_t num_specs = ObjectFile::GetModuleSpecifications (file_spec, 0, 0, specs); - assert (num_specs <= 1 && "Symbol Vendor supports only a single architecture"); - if (num_specs == 1) + ModuleSpecList module_specs; + ModuleSpec matched_module_spec; + if (dsym_fspec.Exists() && + FileAtPathContainsArchAndUUID(dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) { - ModuleSpec mspec; - if (specs.GetModuleSpecAtIndex (0, mspec)) + return true; + } + else + { + path[obj_file_path_length] = '\0'; + + char *last_dot = strrchr(path, '.'); + while (last_dot != NULL && last_dot[0]) { - if (mspec.GetUUID() == module_uuid) - return file_spec; + char *next_slash = strchr(last_dot, '/'); + if (next_slash != NULL) + { + *next_slash = '\0'; + ::strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path) - strlen(path) - 1); + ::strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path) - strlen(path) - 1); + dsym_fspec.SetFile(path, false); + if (dsym_fspec.Exists() && + FileAtPathContainsArchAndUUID(dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) + { + return true; + } + else + { + *last_dot = '\0'; + char *prev_slash = strrchr(path, '/'); + if (prev_slash != NULL) + *prev_slash = '\0'; + else + break; + } + } + else + { + break; + } } } } } } - - return FileSpec(); + dsym_fspec.Clear(); + return false; } FileSpec -Symbols::FindSymbolFileInBundle (const FileSpec& symfile_bundle, - const lldb_private::UUID *uuid, - const ArchSpec *arch) +LocateExecutableSymbolFileDsym (const ModuleSpec &module_spec) { - // FIXME - return FileSpec(); -} + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + const UUID *uuid = module_spec.GetUUIDPtr(); -bool -Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) -{ - // Fill in the module_spec.GetFileSpec() for the object file and/or the - // module_spec.GetSymbolFileSpec() for the debug symbols file. - return false; -} + Timer scoped_timer (__PRETTY_FUNCTION__, + "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)", + exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", + arch ? arch->GetArchitectureName() : "<NULL>", + (void*)uuid); -#elif !defined (__APPLE__) + FileSpec symbol_fspec; + // First try and find the dSYM in the same directory as the executable or in + // an appropriate parent directory + if (LocateDSYMInVincinityOfExecutable (module_spec, symbol_fspec) == false) + { + // We failed to easily find the dSYM above, so use DebugSymbols + LocateMacOSXFilesUsingDebugSymbols (module_spec, NULL, &symbol_fspec); + } + return symbol_fspec; +} FileSpec Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) { - // FIXME - return FileSpec(); + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + const UUID *uuid = module_spec.GetUUIDPtr(); + Timer scoped_timer (__PRETTY_FUNCTION__, + "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", + exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", + arch ? arch->GetArchitectureName() : "<NULL>", + (void*)uuid); + + FileSpec objfile_fspec; + ModuleSpecList module_specs; + ModuleSpec matched_module_spec; + if (exec_fspec && + ObjectFile::GetModuleSpecifications(*exec_fspec, 0, 0, module_specs) && + module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + { + objfile_fspec = exec_fspec; + } + else + { + LocateMacOSXFilesUsingDebugSymbols (module_spec, &objfile_fspec, NULL); + } + return objfile_fspec; } FileSpec Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) { - // FIXME - return FileSpec(); + const char *symbol_filename = module_spec.GetSymbolFileSpec().GetFilename().AsCString(); + if (symbol_filename && symbol_filename[0]) + { + FileSpecList debug_file_search_paths (Target::GetDefaultDebugFileSearchPaths()); + + // Add module directory. + const ConstString &file_dir = module_spec.GetFileSpec().GetDirectory(); + debug_file_search_paths.AppendIfUnique (FileSpec(file_dir.AsCString("."), true)); + + // Add current working directory. + debug_file_search_paths.AppendIfUnique (FileSpec(".", true)); + + // Add /usr/lib/debug directory. + debug_file_search_paths.AppendIfUnique (FileSpec("/usr/lib/debug", true)); + + std::string uuid_str; + const UUID &module_uuid = module_spec.GetUUID(); + if (module_uuid.IsValid()) + { + // Some debug files are stored in the .build-id directory like this: + // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug + uuid_str = module_uuid.GetAsString(""); + uuid_str.insert (2, 1, '/'); + uuid_str = uuid_str + ".debug"; + } + + // Get directory of our module. Needed to check debug files like this: + // /usr/lib/debug/usr/lib/library.so.debug + std::string module_directory = module_spec.GetFileSpec().GetDirectory().AsCString(); + + size_t num_directories = debug_file_search_paths.GetSize(); + for (size_t idx = 0; idx < num_directories; ++idx) + { + FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex (idx); + dirspec.ResolvePath(); + if (!dirspec.Exists() || !dirspec.IsDirectory()) + continue; + + std::vector<std::string> files; + std::string dirname = dirspec.GetPath(); + + files.push_back (dirname + "/" + symbol_filename); + files.push_back (dirname + "/.debug/" + symbol_filename); + files.push_back (dirname + "/.build-id/" + uuid_str); + files.push_back (dirname + module_directory + "/" + symbol_filename); + + const uint32_t num_files = files.size(); + for (size_t idx_file = 0; idx_file < num_files; ++idx_file) + { + const std::string &filename = files[idx_file]; + FileSpec file_spec (filename.c_str(), true); + + if (llvm::sys::fs::equivalent (file_spec.GetPath(), module_spec.GetFileSpec().GetPath())) + continue; + + if (file_spec.Exists()) + { + lldb_private::ModuleSpecList specs; + const size_t num_specs = ObjectFile::GetModuleSpecifications (file_spec, 0, 0, specs); + assert (num_specs <= 1 && "Symbol Vendor supports only a single architecture"); + if (num_specs == 1) + { + ModuleSpec mspec; + if (specs.GetModuleSpecAtIndex (0, mspec)) + { + if (mspec.GetUUID() == module_uuid) + return file_spec; + } + } + } + } + } + } + + return LocateExecutableSymbolFileDsym(module_spec); } +#if !defined (__APPLE__) + FileSpec Symbols::FindSymbolFileInBundle (const FileSpec& symfile_bundle, const lldb_private::UUID *uuid, const ArchSpec *arch) { + // FIXME return FileSpec(); } diff --git a/source/Host/common/Terminal.cpp b/source/Host/common/Terminal.cpp index ca46eb0f744b..9f3abb75e919 100644 --- a/source/Host/common/Terminal.cpp +++ b/source/Host/common/Terminal.cpp @@ -180,20 +180,18 @@ TerminalState::Save (int fd, bool save_process_group) bool TerminalState::Restore () const { +#ifndef LLDB_DISABLE_POSIX if (IsValid()) { const int fd = m_tty.GetFileDescriptor(); -#ifndef LLDB_DISABLE_POSIX if (TFlagsIsValid()) fcntl (fd, F_SETFL, m_tflags); -#endif #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED if (TTYStateIsValid()) tcsetattr (fd, TCSANOW, m_termios_ap.get()); #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED -#ifndef LLDB_DISABLE_POSIX if (ProcessGroupIsValid()) { // Save the original signal handler. @@ -204,9 +202,9 @@ TerminalState::Restore () const // Restore the original signal handler. signal (SIGTTOU, saved_sigttou_callback); } -#endif return true; } +#endif return false; } diff --git a/source/Host/common/XML.cpp b/source/Host/common/XML.cpp new file mode 100644 index 000000000000..14e786ab8b16 --- /dev/null +++ b/source/Host/common/XML.cpp @@ -0,0 +1,693 @@ +//===-- XML.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> /* atof */ + +#include "lldb/Host/XML.h" +#include "lldb/Host/StringConvert.h" + +using namespace lldb; +using namespace lldb_private; + + +#pragma mark -- XMLDocument + +XMLDocument::XMLDocument () : + m_document (nullptr) +{ +} + +XMLDocument::~XMLDocument () +{ + Clear(); +} + +void +XMLDocument::Clear() +{ +#if defined( LIBXML2_DEFINED ) + if (m_document) + { + xmlDocPtr doc = m_document; + m_document = nullptr; + xmlFreeDoc(doc); + } +#endif +} + +bool +XMLDocument::IsValid() const +{ + return m_document != nullptr; +} + +void +XMLDocument::ErrorCallback (void *ctx, const char *format, ...) +{ + XMLDocument *document = (XMLDocument *)ctx; + va_list args; + va_start (args, format); + document->m_errors.PrintfVarArg(format, args); + document->m_errors.EOL(); + va_end (args); +} + +bool +XMLDocument::ParseFile (const char *path) +{ +#if defined( LIBXML2_DEFINED ) + Clear(); + xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback ); + m_document = xmlParseFile(path); + xmlSetGenericErrorFunc(nullptr, nullptr); +#endif + return IsValid(); +} + +bool +XMLDocument::ParseMemory (const char *xml, size_t xml_length, const char *url) +{ +#if defined( LIBXML2_DEFINED ) + Clear(); + xmlSetGenericErrorFunc( (void *)this, XMLDocument::ErrorCallback ); + m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0); + xmlSetGenericErrorFunc(nullptr, nullptr); +#endif + return IsValid(); + +} + +XMLNode +XMLDocument::GetRootElement(const char *required_name) +{ +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + { + XMLNode root_node(xmlDocGetRootElement(m_document)); + if (required_name) + { + llvm::StringRef actual_name = root_node.GetName(); + if (actual_name == required_name) + return root_node; + } + else + { + return root_node; + } + } +#endif + return XMLNode(); +} + +const std::string & +XMLDocument::GetErrors() const +{ + return m_errors.GetString(); +} + +bool +XMLDocument::XMLEnabled () +{ +#if defined( LIBXML2_DEFINED ) + return true; +#else + return false; +#endif +} + +#pragma mark -- XMLNode + +XMLNode::XMLNode() : + m_node(nullptr) +{ +} + +XMLNode::XMLNode(XMLNodeImpl node) : + m_node(node) +{ +} + +XMLNode::~XMLNode() +{ + +} + +void +XMLNode::Clear() +{ + m_node = nullptr; +} + +XMLNode +XMLNode::GetParent() const +{ +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + return XMLNode(m_node->parent); + else + return XMLNode(); +#else + return XMLNode(); +#endif + +} + +XMLNode +XMLNode::GetSibling() const +{ +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + return XMLNode(m_node->next); + else + return XMLNode(); +#else + return XMLNode(); +#endif + +} + +XMLNode +XMLNode::GetChild () const +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + return XMLNode(m_node->children); + else + return XMLNode(); +#else + return XMLNode(); +#endif + +} + +llvm::StringRef +XMLNode::GetAttributeValue(const char *name, const char *fail_value) const +{ + const char *attr_value = NULL; +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name); + else + attr_value = fail_value; +#else + attr_value = fail_value; +#endif + if (attr_value) + return llvm::StringRef(attr_value); + else + return llvm::StringRef(); +} + + + + +void +XMLNode::ForEachChildNode (NodeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + GetChild().ForEachSiblingNode(callback); +#endif +} + +void +XMLNode::ForEachChildElement (NodeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + XMLNode child = GetChild(); + if (child) + child.ForEachSiblingElement(callback); +#endif +} + +void +XMLNode::ForEachChildElementWithName (const char *name, NodeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + XMLNode child = GetChild(); + if (child) + child.ForEachSiblingElementWithName(name, callback); +#endif +} + +void +XMLNode::ForEachAttribute (AttributeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + for (xmlAttrPtr attr = m_node->properties; attr != nullptr; attr=attr->next) + { + // check if name matches + if (attr->name) + { + // check child is a text node + xmlNodePtr child = attr->children; + if (child->type == XML_TEXT_NODE) + { + llvm::StringRef attr_value; + if (child->content) + attr_value = llvm::StringRef((const char *)child->content); + if (callback(llvm::StringRef((const char *)attr->name), attr_value) == false) + return; + } + } + } + } +#endif +} + + +void +XMLNode::ForEachSiblingNode (NodeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + // iterate through all siblings + for (xmlNodePtr node = m_node; node; node=node->next) + { + if (callback(XMLNode(node)) == false) + return; + } + } +#endif +} + +void +XMLNode::ForEachSiblingElement (NodeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + // iterate through all siblings + for (xmlNodePtr node = m_node; node; node=node->next) + { + // we are looking for element nodes only + if (node->type != XML_ELEMENT_NODE) + continue; + + if (callback(XMLNode(node)) == false) + return; + } + } +#endif +} + +void +XMLNode::ForEachSiblingElementWithName (const char *name, NodeCallback const &callback) const +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + // iterate through all siblings + for (xmlNodePtr node = m_node; node; node=node->next) + { + // we are looking for element nodes only + if (node->type != XML_ELEMENT_NODE) + continue; + + // If name is nullptr, we take all nodes of type "t", else + // just the ones whose name matches + if (name) + { + if (strcmp((const char *)node->name, name) != 0) + continue; // Name mismatch, ignore this one + } + else + { + if (node->name) + continue; // nullptr name specified and this elemnt has a name, ignore this one + } + + if (callback(XMLNode(node)) == false) + return; + } + } +#endif +} + +llvm::StringRef +XMLNode::GetName() const +{ +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + { + if (m_node->name) + return llvm::StringRef((const char *)m_node->name); + } +#endif + return llvm::StringRef(); +} + +bool +XMLNode::GetElementText (std::string &text) const +{ + text.clear(); +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + { + bool success = false; + if (m_node->type == XML_ELEMENT_NODE) + { + // check child is a text node + for (xmlNodePtr node = m_node->children; + node != nullptr; + node = node->next) + { + if (node->type == XML_TEXT_NODE) + { + text.append((const char *)node->content); + success = true; + } + } + } + return success; + } +#endif + return false; +} + + +bool +XMLNode::GetElementTextAsUnsigned (uint64_t &value, uint64_t fail_value, int base) const +{ + bool success = false; +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + { + std::string text; + if (GetElementText(text)) + value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success); + } +#endif + if (!success) + value = fail_value; + return success; +} + +bool +XMLNode::GetElementTextAsFloat (double &value, double fail_value) const +{ + bool success = false; +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + { + std::string text; + if (GetElementText(text)) + { + value = atof(text.c_str()); + success = true; + } + } +#endif + if (!success) + value = fail_value; + return success; +} + + + +bool +XMLNode::NameIs (const char *name) const +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + // In case we are looking for a nullptr name or an exact pointer match + if (m_node->name == (const xmlChar *)name) + return true; + if (m_node->name) + return strcmp((const char *)m_node->name, name) == 0; + } +#endif + return false; +} + +XMLNode +XMLNode::FindFirstChildElementWithName (const char *name) const +{ + XMLNode result_node; + +#if defined( LIBXML2_DEFINED ) + ForEachChildElementWithName(name, [&result_node, name](const XMLNode& node) -> bool { + result_node = node; + // Stop iterating, we found the node we wanted + return false; + }); +#endif + + return result_node; +} + +bool +XMLNode::IsValid() const +{ + return m_node != nullptr; +} + +bool +XMLNode::IsElement () const +{ +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + return m_node->type == XML_ELEMENT_NODE; +#endif + return false; +} + + +XMLNode +XMLNode::GetElementForPath (const NamePath &path) +{ +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + if (path.empty()) + return *this; + else + { + XMLNode node = FindFirstChildElementWithName(path[0].c_str()); + const size_t n = path.size(); + for (size_t i=1; node && i<n; ++i) + node = node.FindFirstChildElementWithName(path[i].c_str()); + return node; + } + } +#endif + + return XMLNode(); +} + + +#pragma mark -- ApplePropertyList + +ApplePropertyList::ApplePropertyList() : + m_xml_doc(), + m_dict_node() +{ + +} + +ApplePropertyList::ApplePropertyList (const char *path) : + m_xml_doc(), + m_dict_node() +{ + ParseFile(path); +} + +ApplePropertyList::~ApplePropertyList() +{ +} + +const std::string & +ApplePropertyList::GetErrors() const +{ + return m_xml_doc.GetErrors(); +} + + +bool +ApplePropertyList::ParseFile (const char *path) +{ + if (m_xml_doc.ParseFile(path)) + { + XMLNode plist = m_xml_doc.GetRootElement("plist"); + if (plist) + { + plist.ForEachChildElementWithName("dict", [this](const XMLNode &dict) -> bool { + this->m_dict_node = dict; + return false; // Stop iterating + }); + return (bool)m_dict_node; + } + } + return false; +} + +bool +ApplePropertyList::IsValid() const +{ + return (bool)m_dict_node; +} + +bool +ApplePropertyList::GetValueAsString (const char *key, std::string &value) const +{ + XMLNode value_node = GetValueNode (key); + if (value_node) + return ApplePropertyList::ExtractStringFromValueNode(value_node, value); + return false; +} + +XMLNode +ApplePropertyList::GetValueNode (const char *key) const +{ + XMLNode value_node; +#if defined( LIBXML2_DEFINED ) + + if (IsValid()) + { + m_dict_node.ForEachChildElementWithName("key", [key, &value_node](const XMLNode &key_node) -> bool { + std::string key_name; + if (key_node.GetElementText(key_name)) + { + if (key_name.compare(key) == 0) + { + value_node = key_node.GetSibling(); + while (value_node && !value_node.IsElement()) + value_node = value_node.GetSibling(); + return false; // Stop iterating + } + } + return true; // Keep iterating + }); + } +#endif + return value_node; +} + +bool +ApplePropertyList::ExtractStringFromValueNode (const XMLNode &node, std::string &value) +{ + value.clear(); +#if defined( LIBXML2_DEFINED ) + if (node.IsValid()) + { + llvm::StringRef element_name = node.GetName(); + if (element_name == "true" || element_name == "false") + { + // The text value _is_ the element name itself... + value = std::move(element_name.str()); + return true; + } + else if (element_name == "dict" || element_name == "array") + return false; // dictionaries and arrays have no text value, so we fail + else + return node.GetElementText(value); + } +#endif + return false; +} + +#if defined( LIBXML2_DEFINED ) + +namespace { + + StructuredData::ObjectSP + CreatePlistValue (XMLNode node) + { + llvm::StringRef element_name = node.GetName(); + if (element_name == "array") + { + std::shared_ptr<StructuredData::Array> array_sp(new StructuredData::Array()); + node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool { + array_sp->AddItem(CreatePlistValue(node)); + return true; // Keep iterating through all child elements of the array + }); + return array_sp; + } + else if (element_name == "dict") + { + XMLNode key_node; + std::shared_ptr<StructuredData::Dictionary> dict_sp(new StructuredData::Dictionary()); + node.ForEachChildElement([&key_node, &dict_sp](const XMLNode &node) -> bool { + if (node.NameIs("key")) + { + // This is a "key" element node + key_node = node; + } + else + { + // This is a value node + if (key_node) + { + std::string key_name; + key_node.GetElementText(key_name); + dict_sp->AddItem(key_name, CreatePlistValue(node)); + key_node.Clear(); + } + } + return true; // Keep iterating through all child elements of the dictionary + }); + return dict_sp; + } + else if (element_name == "real") + { + double value = 0.0; + node.GetElementTextAsFloat(value); + return StructuredData::ObjectSP(new StructuredData::Float(value)); + } + else if (element_name == "integer") + { + uint64_t value = 0; + node.GetElementTextAsUnsigned(value, 0, 0); + return StructuredData::ObjectSP(new StructuredData::Integer(value)); + } + else if ((element_name == "string") || (element_name == "data") || (element_name == "date")) + { + std::string text; + node.GetElementText(text); + return StructuredData::ObjectSP(new StructuredData::String(std::move(text))); + } + else if (element_name == "true") + { + return StructuredData::ObjectSP(new StructuredData::Boolean(true)); + } + else if (element_name == "false") + { + return StructuredData::ObjectSP(new StructuredData::Boolean(false)); + } + return StructuredData::ObjectSP(new StructuredData::Null()); + } +} +#endif + +StructuredData::ObjectSP +ApplePropertyList::GetStructuredData() +{ + StructuredData::ObjectSP root_sp; +#if defined( LIBXML2_DEFINED ) + if (IsValid()) + { + return CreatePlistValue(m_dict_node); + } +#endif + return root_sp; +} + + diff --git a/source/Host/freebsd/Host.cpp b/source/Host/freebsd/Host.cpp index 2cbf4d8f4696..8b1c580af27d 100644 --- a/source/Host/freebsd/Host.cpp +++ b/source/Host/freebsd/Host.cpp @@ -38,6 +38,7 @@ #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" #include "Plugins/Process/Utility/FreeBSDSignals.h" @@ -50,35 +51,6 @@ extern "C" { using namespace lldb; using namespace lldb_private; -void -Host::Backtrace (Stream &strm, uint32_t max_frames) -{ - char backtrace_path[] = "/tmp/lldb-backtrace-tmp-XXXXXX"; - int backtrace_fd = ::mkstemp (backtrace_path); - if (backtrace_fd != -1) - { - std::vector<void *> frame_buffer (max_frames, NULL); - int count = ::backtrace (&frame_buffer[0], frame_buffer.size()); - ::backtrace_symbols_fd (&frame_buffer[0], count, backtrace_fd); - - const off_t buffer_size = ::lseek(backtrace_fd, 0, SEEK_CUR); - - if (::lseek(backtrace_fd, 0, SEEK_SET) == 0) - { - char *buffer = (char *)::malloc (buffer_size); - if (buffer) - { - ssize_t bytes_read = ::read (backtrace_fd, buffer, buffer_size); - if (bytes_read > 0) - strm.Write(buffer, bytes_read); - ::free (buffer); - } - } - ::close (backtrace_fd); - ::unlink (backtrace_path); - } -} - size_t Host::GetEnvironment (StringList &env) { @@ -312,3 +284,9 @@ Host::GetUnixSignals () return s_unix_signals_sp; } +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + return Error("unimplemented"); +} + diff --git a/source/Host/freebsd/HostThreadFreeBSD.cpp b/source/Host/freebsd/HostThreadFreeBSD.cpp index 7d611bb6894b..a4302a9e67bf 100644 --- a/source/Host/freebsd/HostThreadFreeBSD.cpp +++ b/source/Host/freebsd/HostThreadFreeBSD.cpp @@ -14,7 +14,9 @@ // C includes #include <errno.h> #include <pthread.h> +#if defined (__FreeBSD__) #include <pthread_np.h> +#endif #include <stdlib.h> #include <sys/sysctl.h> #include <sys/user.h> diff --git a/source/Host/freebsd/ThisThread.cpp b/source/Host/freebsd/ThisThread.cpp index fb25847be24f..e524fd4ace34 100644 --- a/source/Host/freebsd/ThisThread.cpp +++ b/source/Host/freebsd/ThisThread.cpp @@ -13,18 +13,27 @@ #include "llvm/ADT/SmallVector.h" #include <pthread.h> +#if defined (__FreeBSD__) #include <pthread_np.h> +#endif using namespace lldb_private; void ThisThread::SetName(llvm::StringRef name) { +#if defined (__FreeBSD__) // Kfreebsd does not have a simple alternative ::pthread_set_name_np(::pthread_self(), name.data()); +#endif } void ThisThread::GetName(llvm::SmallVectorImpl<char> &name) { +#if defined (__FreeBSD__) HostNativeThread::GetName(::pthread_getthreadid_np(), name); +#else +// Kfreebsd + HostNativeThread::GetName((unsigned)pthread_self(), name); +#endif } diff --git a/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/source/Host/posix/ConnectionFileDescriptorPosix.cpp index fe70c33bf5ae..f12f98c30b44 100644 --- a/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -39,7 +39,6 @@ #include "llvm/ADT/SmallVector.h" #endif // Project includes -#include "lldb/lldb-private-log.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Log.h" #include "lldb/Core/StreamString.h" @@ -82,6 +81,17 @@ ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) OpenCommandPipe(); } +ConnectionFileDescriptor::ConnectionFileDescriptor(Socket* socket) + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) + , m_child_processes_inherit(false) +{ + InitializeSocket(socket); +} + ConnectionFileDescriptor::~ConnectionFileDescriptor() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -156,6 +166,14 @@ ConnectionFileDescriptor::Connect(const char *s, Error *error_ptr) // unix://SOCKNAME return NamedSocketAccept(s + strlen("unix-accept://"), error_ptr); } + else if (strstr(s, "adb://") == s) + { + int port = -1; + sscanf(s, "adb://%*[^:]:%d", &port); + char host_and_port[sizeof("localhost:65535")]; + snprintf(host_and_port, sizeof(host_and_port), "localhost:%d", port); + return ConnectTCP(host_and_port, error_ptr); + } else if (strstr(s, "connect://") == s) { return ConnectTCP(s + strlen("connect://"), error_ptr); @@ -354,6 +372,9 @@ ConnectionFileDescriptor::Disconnect(Error *error_ptr) if (error_ptr) *error_ptr = error.Fail() ? error : error2; + // Close any pipes we were using for async interrupts + m_pipe.Close(); + m_uri.clear(); m_shutting_down = false; return status; @@ -376,8 +397,12 @@ ConnectionFileDescriptor::Read(void *dst, size_t dst_len, uint32_t timeout_usec, status = eConnectionStatusTimedOut; return 0; } - else if (m_shutting_down) - return eConnectionStatusError; + + if (m_shutting_down) + { + status = eConnectionStatusError; + return 0; + } status = BytesAvailable(timeout_usec, error_ptr); if (status != eConnectionStatusSuccess) @@ -671,8 +696,10 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr return eConnectionStatusSuccess; if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) { - // We got a command to exit. Read the data from that pipe: - char buffer[16]; + // There is an interrupt or exit command in the command pipe + // Read the data from that pipe: + char buffer[1]; + ssize_t bytes_read; do @@ -684,8 +711,9 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr { case 'q': if (log) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", - static_cast<void *>(this), static_cast<int>(bytes_read), buffer); + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() " + "got data: %c from the command channel.", + static_cast<void *>(this), buffer[0]); return eConnectionStatusEndOfFile; case 'i': // Interrupt the current read @@ -759,15 +787,7 @@ ConnectionFileDescriptor::SocketListenAndAccept(const char *s, Error *error_ptr) if (error.Fail()) return eConnectionStatusError; - m_write_sp.reset(socket); - m_read_sp = m_write_sp; - if (error.Fail()) - { - return eConnectionStatusError; - } - StreamString strm; - strm.Printf("connect://%s:%u",socket->GetRemoteIPAddress().c_str(), socket->GetRemotePortNumber()); - m_uri.swap(strm.GetString()); + InitializeSocket(socket); return eConnectionStatusSuccess; } @@ -832,3 +852,13 @@ ConnectionFileDescriptor::SetChildProcessesInherit(bool child_processes_inherit) { m_child_processes_inherit = child_processes_inherit; } + +void +ConnectionFileDescriptor::InitializeSocket(Socket* socket) +{ + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + StreamString strm; + strm.Printf("connect://%s:%u",socket->GetRemoteIPAddress().c_str(), socket->GetRemotePortNumber()); + m_uri.swap(strm.GetString()); +} diff --git a/source/Host/posix/FileSystem.cpp b/source/Host/posix/FileSystem.cpp index 571316811142..52698039b46e 100644 --- a/source/Host/posix/FileSystem.cpp +++ b/source/Host/posix/FileSystem.cpp @@ -10,8 +10,16 @@ #include "lldb/Host/FileSystem.h" // C includes +#include <dirent.h> +#include <sys/mount.h> +#include <sys/param.h> #include <sys/stat.h> #include <sys/types.h> +#ifdef __linux__ +#include <sys/statfs.h> +#include <sys/mount.h> +#include <linux/magic.h> +#endif // lldb Includes #include "lldb/Core/Error.h" @@ -28,70 +36,91 @@ FileSystem::GetNativePathSyntax() } Error -FileSystem::MakeDirectory(const char *path, uint32_t file_permissions) +FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) { - Error error; - if (path && path[0]) + if (file_spec) { - if (::mkdir(path, file_permissions) != 0) + Error error; + if (::mkdir(file_spec.GetCString(), file_permissions) == -1) { error.SetErrorToErrno(); + errno = 0; switch (error.GetError()) { case ENOENT: { // Parent directory doesn't exist, so lets make it if we can - FileSpec spec(path, false); - if (spec.GetDirectory() && spec.GetFilename()) + // Make the parent directory and try again + FileSpec parent_file_spec{file_spec.GetDirectory().GetCString(), false}; + error = MakeDirectory(parent_file_spec, file_permissions); + if (error.Fail()) + return error; + // Try and make the directory again now that the parent directory was made successfully + if (::mkdir(file_spec.GetCString(), file_permissions) == -1) { - // Make the parent directory and try again - Error error2 = MakeDirectory(spec.GetDirectory().GetCString(), file_permissions); - if (error2.Success()) - { - // Try and make the directory again now that the parent directory was made successfully - if (::mkdir(path, file_permissions) == 0) - error.Clear(); - else - error.SetErrorToErrno(); - } + error.SetErrorToErrno(); + return error; } } - break; - case EEXIST: { - FileSpec path_spec(path, false); - if (path_spec.IsDirectory()) - error.Clear(); // It is a directory and it already exists + if (file_spec.IsDirectory()) + return Error{}; // It is a directory and it already exists } - break; } } + return error; } - else - { - error.SetErrorString("empty path"); - } - return error; + return Error{"empty path"}; } Error -FileSystem::DeleteDirectory(const char *path, bool recurse) +FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) { Error error; - if (path && path[0]) + if (file_spec) { if (recurse) { - StreamString command; - command.Printf("rm -rf \"%s\"", path); - int status = ::system(command.GetString().c_str()); - if (status != 0) - error.SetError(status, eErrorTypeGeneric); + // Save all sub directories in a list so we don't recursively call this function + // and possibly run out of file descriptors if the directory is too deep. + std::vector<FileSpec> sub_directories; + + FileSpec::ForEachItemInDirectory (file_spec.GetCString(), [&error, &sub_directories](FileSpec::FileType file_type, const FileSpec &spec) -> FileSpec::EnumerateDirectoryResult { + if (file_type == FileSpec::eFileTypeDirectory) + { + // Save all directorires and process them after iterating through this directory + sub_directories.push_back(spec); + } + else + { + // Update sub_spec to point to the current file and delete it + error = FileSystem::Unlink(spec); + } + // If anything went wrong, stop iterating, else process the next file + if (error.Fail()) + return FileSpec::eEnumerateDirectoryResultQuit; + else + return FileSpec::eEnumerateDirectoryResultNext; + }); + + if (error.Success()) + { + // Now delete all sub directories with separate calls that aren't + // recursively calling into this function _while_ this function is + // iterating through the current directory. + for (const auto &sub_directory : sub_directories) + { + error = DeleteDirectory(sub_directory, recurse); + if (error.Fail()) + break; + } + } } - else + + if (error.Success()) { - if (::rmdir(path) != 0) + if (::rmdir(file_spec.GetCString()) != 0) error.SetErrorToErrno(); } } @@ -103,11 +132,11 @@ FileSystem::DeleteDirectory(const char *path, bool recurse) } Error -FileSystem::GetFilePermissions(const char *path, uint32_t &file_permissions) +FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { Error error; struct stat file_stats; - if (::stat(path, &file_stats) == 0) + if (::stat(file_spec.GetCString(), &file_stats) == 0) { // The bits in "st_mode" currently match the definitions // for the file mode bits in unix. @@ -121,10 +150,10 @@ FileSystem::GetFilePermissions(const char *path, uint32_t &file_permissions) } Error -FileSystem::SetFilePermissions(const char *path, uint32_t file_permissions) +FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { Error error; - if (::chmod(path, file_permissions) != 0) + if (::chmod(file_spec.GetCString(), file_permissions) != 0) error.SetErrorToErrno(); return error; } @@ -142,60 +171,72 @@ FileSystem::GetFileExists(const FileSpec &file_spec) } Error -FileSystem::Symlink(const char *src, const char *dst) +FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) { Error error; - if (::symlink(dst, src) == -1) + if (::link(dst.GetCString(), src.GetCString()) == -1) error.SetErrorToErrno(); return error; } Error -FileSystem::Unlink(const char *path) +FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { Error error; - if (::unlink(path) == -1) + if (::symlink(dst.GetCString(), src.GetCString()) == -1) error.SetErrorToErrno(); return error; } Error -FileSystem::Readlink(const char *path, char *buf, size_t buf_len) +FileSystem::Unlink(const FileSpec &file_spec) { Error error; - ssize_t count = ::readlink(path, buf, buf_len); + if (::unlink(file_spec.GetCString()) == -1) + error.SetErrorToErrno(); + return error; +} + +Error +FileSystem::Readlink(const FileSpec &src, FileSpec &dst) +{ + Error error; + char buf[PATH_MAX]; + ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1); if (count < 0) error.SetErrorToErrno(); - else if (static_cast<size_t>(count) < (buf_len - 1)) - buf[count] = '\0'; // Success else - error.SetErrorString("'buf' buffer is too small to contain link contents"); + { + buf[count] = '\0'; // Success + dst.SetFile(buf, false); + } return error; } -bool -FileSystem::CalculateMD5(const FileSpec &file_spec, uint64_t &low, uint64_t &high) +static bool IsLocal(const struct statfs& info) { -#if defined(__APPLE__) - StreamString md5_cmd_line; - md5_cmd_line.Printf("md5 -q '%s'", file_spec.GetPath().c_str()); - std::string hash_string; - Error err = Host::RunShellCommand(md5_cmd_line.GetData(), NULL, NULL, NULL, &hash_string, 60); - if (err.Fail()) - return false; - // a correctly formed MD5 is 16-bytes, that is 32 hex digits - // if the output is any other length it is probably wrong - if (hash_string.size() != 32) +#ifdef __linux__ + #define CIFS_MAGIC_NUMBER 0xFF534D42 + switch ((uint32_t)info.f_type) + { + case NFS_SUPER_MAGIC: + case SMB_SUPER_MAGIC: + case CIFS_MAGIC_NUMBER: return false; - std::string part1(hash_string, 0, 16); - std::string part2(hash_string, 16); - const char *part1_cstr = part1.c_str(); - const char *part2_cstr = part2.c_str(); - high = ::strtoull(part1_cstr, NULL, 16); - low = ::strtoull(part2_cstr, NULL, 16); - return true; + default: + return true; + } #else - // your own MD5 implementation here - return false; + return (info.f_flags & MNT_LOCAL) != 0; #endif } + +bool +FileSystem::IsLocal(const FileSpec &spec) +{ + struct statfs statfs_info; + std::string path (spec.GetPath()); + if (statfs(path.c_str(), &statfs_info) == 0) + return ::IsLocal(statfs_info); + return false; +} diff --git a/source/Host/posix/HostInfoPosix.cpp b/source/Host/posix/HostInfoPosix.cpp index 018d423ee9d3..c04db71e1b81 100644 --- a/source/Host/posix/HostInfoPosix.cpp +++ b/source/Host/posix/HostInfoPosix.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" - #include "lldb/Core/Log.h" #include "lldb/Host/posix/HostInfoPosix.h" @@ -17,6 +16,7 @@ #include <grp.h> #include <limits.h> +#include <mutex> #include <netdb.h> #include <pwd.h> #include <sys/types.h> @@ -47,9 +47,31 @@ HostInfoPosix::GetHostname(std::string &s) return false; } +#ifdef __ANDROID_NDK__ +#include <android/api-level.h> +#endif +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 +#define USE_GETPWUID +#endif + +#ifdef USE_GETPWUID +static std::mutex s_getpwuid_lock; +#endif + const char * HostInfoPosix::LookupUserName(uint32_t uid, std::string &user_name) { +#ifdef USE_GETPWUID + // getpwuid_r is missing from android-9 + // make getpwuid thread safe with a mutex + std::lock_guard<std::mutex> lock(s_getpwuid_lock); + struct passwd *user_info_ptr = ::getpwuid(uid); + if (user_info_ptr) + { + user_name.assign(user_info_ptr->pw_name); + return user_name.c_str(); + } +#else struct passwd user_info; struct passwd *user_info_ptr = &user_info; char user_buffer[PATH_MAX]; @@ -62,8 +84,9 @@ HostInfoPosix::LookupUserName(uint32_t uid, std::string &user_name) return user_name.c_str(); } } +#endif user_name.clear(); - return NULL; + return nullptr; } const char * @@ -153,11 +176,8 @@ HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) char *lib_pos = ::strstr(raw_path, "/lib"); if (lib_pos != nullptr) { - // First terminate the raw path at the start of lib. - *lib_pos = '\0'; - // Now write in bin in place of lib. - ::strncpy(lib_pos, "/bin", PATH_MAX - (lib_pos - raw_path)); + ::snprintf(lib_pos, PATH_MAX - (lib_pos - raw_path), "/bin"); if (log) log->Printf("Host::%s() derived the bin path as: %s", __FUNCTION__, raw_path); diff --git a/source/Host/posix/HostProcessPosix.cpp b/source/Host/posix/HostProcessPosix.cpp index 8e19add048ee..5761a79da27f 100644 --- a/source/Host/posix/HostProcessPosix.cpp +++ b/source/Host/posix/HostProcessPosix.cpp @@ -69,28 +69,25 @@ Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const // Use special code here because proc/[pid]/exe is a symbolic link. char link_path[PATH_MAX]; - char exe_path[PATH_MAX] = ""; - if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) <= 0) + if (snprintf(link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) != 1) { error.SetErrorString("Unable to build /proc/<pid>/exe string"); return error; } - error = FileSystem::Readlink(link_path, exe_path, llvm::array_lengthof(exe_path)); + error = FileSystem::Readlink(FileSpec{link_path, false}, file_spec); if (!error.Success()) return error; - const ssize_t len = strlen(exe_path); // If the binary has been deleted, the link name has " (deleted)" appended. // Remove if there. - static const ssize_t deleted_len = strlen(" (deleted)"); - if (len > deleted_len && - !strcmp(exe_path + len - deleted_len, " (deleted)")) + if (file_spec.GetFilename().GetStringRef().endswith(" (deleted)")) { - exe_path[len - deleted_len] = 0; + const char *filename = file_spec.GetFilename().GetCString(); + static const size_t deleted_len = strlen(" (deleted)"); + const size_t len = file_spec.GetFilename().GetLength(); + file_spec.GetFilename().SetCStringWithLength(filename, len - deleted_len); } - - file_spec.SetFile(exe_path, false); return error; } diff --git a/source/Host/posix/LockFilePosix.cpp b/source/Host/posix/LockFilePosix.cpp new file mode 100644 index 000000000000..e52b648799b7 --- /dev/null +++ b/source/Host/posix/LockFilePosix.cpp @@ -0,0 +1,77 @@ +//===-- LockFilePosix.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/LockFilePosix.h" + +#include <fcntl.h> + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + +Error fileLock (int fd, int cmd, int lock_type, const uint64_t start, const uint64_t len) +{ + struct flock fl; + + fl.l_type = lock_type; + fl.l_whence = SEEK_SET; + fl.l_start = start; + fl.l_len = len; + fl.l_pid = ::getpid (); + + Error error; + if (::fcntl (fd, cmd, &fl) == -1) + error.SetErrorToErrno (); + + return error; +} + +} // namespace + +LockFilePosix::LockFilePosix (int fd) + : LockFileBase (fd) +{ +} + +LockFilePosix::~LockFilePosix () +{ + Unlock (); +} + +Error +LockFilePosix::DoWriteLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_fd, F_SETLKW, F_WRLCK, start, len); +} + +Error +LockFilePosix::DoTryWriteLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_fd, F_SETLK, F_WRLCK, start, len); +} + +Error +LockFilePosix::DoReadLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_fd, F_SETLKW, F_RDLCK, start, len); +} + +Error +LockFilePosix::DoTryReadLock (const uint64_t start, const uint64_t len) +{ + return fileLock (m_fd, F_SETLK, F_RDLCK, start, len); +} + +Error +LockFilePosix::DoUnlock () +{ + return fileLock (m_fd, F_SETLK, F_UNLCK, m_start, m_len); +} diff --git a/source/Host/posix/PipePosix.cpp b/source/Host/posix/PipePosix.cpp index 1650f1e7979b..0ed319facf93 100644 --- a/source/Host/posix/PipePosix.cpp +++ b/source/Host/posix/PipePosix.cpp @@ -129,9 +129,27 @@ SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler } PipePosix::PipePosix() + : m_fds{ + PipePosix::kInvalidDescriptor, + PipePosix::kInvalidDescriptor + } {} + +PipePosix::PipePosix(int read_fd, int write_fd) + : m_fds{read_fd, write_fd} {} + +PipePosix::PipePosix(PipePosix &&pipe_posix) + : PipeBase{std::move(pipe_posix)}, + m_fds{ + pipe_posix.ReleaseReadFileDescriptor(), + pipe_posix.ReleaseWriteFileDescriptor() + } {} + +PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) { - m_fds[READ] = PipePosix::kInvalidDescriptor; - m_fds[WRITE] = PipePosix::kInvalidDescriptor; + PipeBase::operator=(std::move(pipe_posix)); + m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor(); + m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor(); + return *this; } PipePosix::~PipePosix() @@ -317,7 +335,7 @@ PipePosix::Close() Error PipePosix::Delete(llvm::StringRef name) { - return FileSystem::Unlink(name.data()); + return FileSystem::Unlink(FileSpec{name.data(), true}); } bool |