diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Host')
56 files changed, 15881 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Host/common/Condition.cpp b/contrib/llvm/tools/lldb/source/Host/common/Condition.cpp new file mode 100644 index 0000000..1c1afb4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Condition.cpp @@ -0,0 +1,108 @@ +//===-- Condition.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <errno.h> + +#include "lldb/Host/Condition.h" +#include "lldb/Host/TimeValue.h" + + +using namespace lldb_private; + +#ifndef _WIN32 + +//---------------------------------------------------------------------- +// Default constructor +// +// The default constructor will initialize a new pthread condition +// and maintain the condition in the object state. +//---------------------------------------------------------------------- +Condition::Condition () : + m_condition() +{ + ::pthread_cond_init (&m_condition, NULL); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Destroys the pthread condition that the object owns. +//---------------------------------------------------------------------- +Condition::~Condition () +{ + ::pthread_cond_destroy (&m_condition); +} + +//---------------------------------------------------------------------- +// Unblock all threads waiting for a condition variable +//---------------------------------------------------------------------- +int +Condition::Broadcast () +{ + return ::pthread_cond_broadcast (&m_condition); +} + +//---------------------------------------------------------------------- +// Unblocks one thread waiting for the condition variable +//---------------------------------------------------------------------- +int +Condition::Signal () +{ + return ::pthread_cond_signal (&m_condition); +} + +//---------------------------------------------------------------------- +// The Wait() function atomically blocks the current thread +// waiting on the owned condition variable, and unblocks the mutex +// specified by "mutex". The waiting thread unblocks only after +// another thread calls Signal(), or Broadcast() with the same +// condition variable, or if "abstime" is valid (non-NULL) this +// function will return when the system time reaches the time +// specified in "abstime". If "abstime" is NULL this function will +// wait for an infinite amount of time for the condition variable +// to be signaled or broadcasted. +// +// The current thread re-acquires the lock on "mutex". +//---------------------------------------------------------------------- +int +Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out) +{ + int err = 0; + do + { + if (abstime && abstime->IsValid()) + { + struct timespec abstime_ts = abstime->GetAsTimeSpec(); + err = ::pthread_cond_timedwait (&m_condition, mutex.GetMutex(), &abstime_ts); + } + else + err = ::pthread_cond_wait (&m_condition, mutex.GetMutex()); + } while (err == EINTR); + + if (timed_out != NULL) + { + if (err == ETIMEDOUT) + *timed_out = true; + else + *timed_out = false; + } + + return err; +} + +#endif + +//---------------------------------------------------------------------- +// Get accessor to the pthread condition object +//---------------------------------------------------------------------- +lldb::condition_t * +Condition::GetCondition() +{ + return &m_condition; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp b/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp new file mode 100644 index 0000000..4640154 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp @@ -0,0 +1,1443 @@ +//===-- Editline.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <iomanip> +#include <iostream> +#include <limits.h> + +#include "lldb/Host/Editline.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#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; + +// Workaround for what looks like an OS X-specific issue, but other platforms +// may benefit from something similar if issues arise. The libedit library +// doesn't explicitly initialize the curses termcap library, which it gets away +// with until TERM is set to VT100 where it stumbles over an implementation +// assumption that may not exist on other platforms. The setupterm() function +// would normally require headers that don't work gracefully in this context, so +// the function declaraction has been hoisted here. +#if defined(__APPLE__) +extern "C" { + int setupterm(char *term, int fildes, int *errret); +} +#define USE_SETUPTERM_WORKAROUND +#endif + +// Editline uses careful cursor management to achieve the illusion of editing a multi-line block of text +// with a single line editor. Preserving this illusion requires fairly careful management of cursor +// state. Read and understand the relationship between DisplayInput(), MoveCursor(), SetCurrentLine(), +// and SaveEditedLine() before making changes. + +#define ESCAPE "\x1b" +#define ANSI_FAINT ESCAPE "[2m" +#define ANSI_UNFAINT ESCAPE "[22m" +#define ANSI_CLEAR_BELOW ESCAPE "[J" +#define ANSI_CLEAR_RIGHT ESCAPE "[K" +#define ANSI_SET_COLUMN_N ESCAPE "[%dG" +#define ANSI_UP_N_ROWS ESCAPE "[%dA" +#define ANSI_DOWN_N_ROWS ESCAPE "[%dB" + +#if LLDB_EDITLINE_USE_WCHAR + +#define EditLineConstString(str) L##str +#define EditLineStringFormatSpec "%ls" + +#else + +#define EditLineConstString(str) str +#define EditLineStringFormatSpec "%s" + +// use #defines so wide version functions and structs will resolve to old versions +// for case of libedit not built with wide char support +#define history_w history +#define history_winit history_init +#define history_wend history_end +#define HistoryW History +#define HistEventW HistEvent +#define LineInfoW LineInfo + +#define el_wgets el_gets +#define el_wgetc el_getc +#define el_wpush el_push +#define el_wparse el_parse +#define el_wset el_set +#define el_wget el_get +#define el_wline el_line +#define el_winsertstr el_insertstr +#define el_wdeletestr el_deletestr + +#endif // #if LLDB_EDITLINE_USE_WCHAR + +bool +IsOnlySpaces (const EditLineStringType & content) +{ + for (wchar_t ch : content) + { + if (ch != EditLineCharType(' ')) + return false; + } + return true; +} + +EditLineStringType +CombineLines (const std::vector<EditLineStringType> & lines) +{ + EditLineStringStreamType combined_stream; + for (EditLineStringType line : lines) + { + combined_stream << line.c_str() << "\n"; + } + return combined_stream.str(); +} + +std::vector<EditLineStringType> +SplitLines (const EditLineStringType & input) +{ + std::vector<EditLineStringType> result; + size_t start = 0; + while (start < input.length()) + { + size_t end = input.find ('\n', start); + if (end == std::string::npos) + { + result.insert (result.end(), input.substr (start)); + break; + } + result.insert (result.end(), input.substr (start, end - start)); + start = end + 1; + } + return result; +} + +EditLineStringType +FixIndentation (const EditLineStringType & line, int indent_correction) +{ + if (indent_correction == 0) + return line; + if (indent_correction < 0) + return line.substr (-indent_correction); + return EditLineStringType (indent_correction, EditLineCharType(' ')) + line; +} + +int +GetIndentation (const EditLineStringType & line) +{ + int space_count = 0; + for (EditLineCharType ch : line) + { + if (ch != EditLineCharType(' ')) + break; + ++space_count; + } + return space_count; +} + +bool +IsInputPending (FILE * file) +{ + // FIXME: This will be broken on Windows if we ever re-enable Editline. You can't use select + // on something that isn't a socket. This will have to be re-written to not use a FILE*, but + // instead use some kind of yet-to-be-created abstraction that select-like functionality on + // non-socket objects. + const int fd = fileno (file); + fd_set fds; + FD_ZERO (&fds); + FD_SET (fd, &fds); + timeval timeout = { 0, 0 }; + return select (fd + 1, &fds, NULL, NULL, &timeout); +} + +namespace lldb_private +{ + namespace line_editor + { + typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP; + + // EditlineHistory objects are sometimes shared between multiple + // Editline instances with the same program name. + + class EditlineHistory + { + private: + // Use static GetHistory() function to get a EditlineHistorySP to one of these objects + EditlineHistory (const std::string &prefix, uint32_t size, bool unique_entries) : + m_history (NULL), + m_event (), + m_prefix (prefix), + m_path () + { + m_history = history_winit(); + history_w (m_history, &m_event, H_SETSIZE, size); + if (unique_entries) + history_w (m_history, &m_event, H_SETUNIQUE, 1); + } + + const char * + GetHistoryFilePath() + { + if (m_path.empty() && m_history && !m_prefix.empty()) + { + FileSpec parent_path{"~/.lldb", true}; + char history_path[PATH_MAX]; + if (FileSystem::MakeDirectory(parent_path, lldb::eFilePermissionsDirectoryDefault).Success()) + { + snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str()); + } + else + { + snprintf (history_path, sizeof (history_path), "~/%s-widehistory", m_prefix.c_str()); + } + m_path = FileSpec (history_path, true).GetPath(); + } + if (m_path.empty()) + return NULL; + return m_path.c_str(); + } + + public: + + ~EditlineHistory() + { + Save(); + + if (m_history) + { + history_wend (m_history); + m_history = NULL; + } + } + + static EditlineHistorySP + GetHistory (const std::string &prefix) + { + typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap; + static Mutex g_mutex (Mutex::eMutexTypeRecursive); + static WeakHistoryMap g_weak_map; + Mutex::Locker locker (g_mutex); + WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix); + EditlineHistorySP history_sp; + if (pos != g_weak_map.end()) + { + history_sp = pos->second.lock(); + if (history_sp) + return history_sp; + g_weak_map.erase (pos); + } + history_sp.reset (new EditlineHistory (prefix, 800, true)); + g_weak_map[prefix] = history_sp; + return history_sp; + } + + bool IsValid() const + { + return m_history != NULL; + } + + HistoryW * + GetHistoryPtr () + { + return m_history; + } + + void + Enter (const EditLineCharType *line_cstr) + { + if (m_history) + history_w (m_history, &m_event, H_ENTER, line_cstr); + } + + bool + Load () + { + if (m_history) + { + const char *path = GetHistoryFilePath(); + if (path) + { + history_w (m_history, &m_event, H_LOAD, path); + return true; + } + } + return false; + } + + bool + Save () + { + if (m_history) + { + const char *path = GetHistoryFilePath(); + if (path) + { + history_w (m_history, &m_event, H_SAVE, path); + return true; + } + } + return false; + } + + protected: + HistoryW * m_history; // The history object + HistEventW m_event; // The history event needed to contain all history events + std::string m_prefix; // The prefix name (usually the editline program name) to use when loading/saving history + std::string m_path; // Path to the history file + }; + } +} + +//------------------------------------------------------------------ +// Editline private methods +//------------------------------------------------------------------ + +void +Editline::SetBaseLineNumber (int line_number) +{ + std::stringstream line_number_stream; + line_number_stream << line_number; + m_base_line_number = line_number; + m_line_number_digits = std::max (3, (int)line_number_stream.str().length() + 1); +} + +std::string +Editline::PromptForIndex (int line_index) +{ + bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; + std::string prompt = m_set_prompt; + if (use_line_numbers && prompt.length() == 0) + { + prompt = ": "; + } + std::string continuation_prompt = prompt; + if (m_set_continuation_prompt.length() > 0) + { + continuation_prompt = m_set_continuation_prompt; + + // Ensure that both prompts are the same length through space padding + while (continuation_prompt.length() < prompt.length()) + { + continuation_prompt += ' '; + } + while (prompt.length() < continuation_prompt.length()) + { + prompt += ' '; + } + } + + if (use_line_numbers) + { + StreamString prompt_stream; + prompt_stream.Printf("%*d%s", m_line_number_digits, m_base_line_number + line_index, + (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); + return std::move (prompt_stream.GetString()); + } + return (line_index == 0) ? prompt : continuation_prompt; +} + +void +Editline::SetCurrentLine (int line_index) +{ + m_current_line_index = line_index; + m_current_prompt = PromptForIndex (line_index); +} + +int +Editline::GetPromptWidth() +{ + return (int)PromptForIndex (0).length(); +} + +bool +Editline::IsEmacs() +{ + const char * editor; + el_get (m_editline, EL_EDITOR, &editor); + return editor[0] == 'e'; +} + +bool +Editline::IsOnlySpaces() +{ + const LineInfoW * info = el_wline (m_editline); + for (const EditLineCharType * character = info->buffer; character < info->lastchar; character++) + { + if (*character != ' ') + return false; + } + return true; +} + +int +Editline::GetLineIndexForLocation (CursorLocation location, int cursor_row) +{ + int line = 0; + if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd || + location == CursorLocation::EditingCursor) + { + for (unsigned index = 0; index < m_current_line_index; index++) + { + line += CountRowsForLine (m_input_lines[index]); + } + if (location == CursorLocation::EditingCursor) + { + line += cursor_row; + } + else if (location == CursorLocation::BlockEnd) + { + for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++) + { + line += CountRowsForLine (m_input_lines[index]); + } + --line; + } + } + return line; +} + +void +Editline::MoveCursor (CursorLocation from, CursorLocation to) +{ + const LineInfoW * info = el_wline (m_editline); + int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); + int editline_cursor_row = editline_cursor_position / m_terminal_width; + + // Determine relative starting and ending lines + int fromLine = GetLineIndexForLocation (from, editline_cursor_row); + int toLine = GetLineIndexForLocation (to, editline_cursor_row); + if (toLine != fromLine) + { + fprintf (m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs (toLine - fromLine)); + } + + // Determine target column + int toColumn = 1; + if (to == CursorLocation::EditingCursor) + { + toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; + } + else if (to == CursorLocation::BlockEnd) + { + toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1; + } + fprintf (m_output_file, ANSI_SET_COLUMN_N, toColumn); +} + +void +Editline::DisplayInput (int firstIndex) +{ + fprintf (m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); + int line_count = (int)m_input_lines.size(); + const char *faint = m_color_prompts ? ANSI_FAINT : ""; + const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; + + for (int index = firstIndex; index < line_count; index++) + { + fprintf (m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ", + faint, + PromptForIndex (index).c_str(), + unfaint, + m_input_lines[index].c_str()); + if (index < line_count - 1) + fprintf (m_output_file, "\n"); + } +} + + +int +Editline::CountRowsForLine (const EditLineStringType & content) +{ + auto prompt = PromptForIndex (0); // Prompt width is constant during an edit session + int line_length = (int)(content.length() + prompt.length()); + return (line_length / m_terminal_width) + 1; +} + +void +Editline::SaveEditedLine() +{ + const LineInfoW * info = el_wline (m_editline); + m_input_lines[m_current_line_index] = EditLineStringType (info->buffer, info->lastchar - info->buffer); +} + +StringList +Editline::GetInputAsStringList(int line_count) +{ + StringList lines; + for (EditLineStringType line : m_input_lines) + { + if (line_count == 0) + break; +#if LLDB_EDITLINE_USE_WCHAR + lines.AppendString (m_utf8conv.to_bytes (line)); +#else + lines.AppendString(line); +#endif + --line_count; + } + return lines; +} + +unsigned char +Editline::RecallHistory (bool earlier) +{ + if (!m_history_sp || !m_history_sp->IsValid()) + return CC_ERROR; + + HistoryW * pHistory = m_history_sp->GetHistoryPtr(); + HistEventW history_event; + std::vector<EditLineStringType> new_input_lines; + + // Treat moving from the "live" entry differently + if (!m_in_history) + { + if (earlier == false) + return CC_ERROR; // Can't go newer than the "live" entry + if (history_w (pHistory, &history_event, H_FIRST) == -1) + return CC_ERROR; + + // Save any edits to the "live" entry in case we return by moving forward in history + // (it would be more bash-like to save over any current entry, but libedit doesn't + // offer the ability to add entries anywhere except the end.) + SaveEditedLine(); + m_live_history_lines = m_input_lines; + m_in_history = true; + } + else + { + if (history_w (pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) + { + // Can't move earlier than the earliest entry + if (earlier) + return CC_ERROR; + + // ... but moving to newer than the newest yields the "live" entry + new_input_lines = m_live_history_lines; + m_in_history = false; + } + } + + // If we're pulling the lines from history, split them apart + if (m_in_history) + new_input_lines = SplitLines (history_event.str); + + // Erase the current edit session and replace it with a new one + MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); + m_input_lines = new_input_lines; + DisplayInput(); + + // Prepare to edit the last line when moving to previous entry, or the first line + // when moving to next entry + SetCurrentLine (m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; +} + +int +Editline::GetCharacter (EditLineCharType * c) +{ + const LineInfoW * info = el_wline (m_editline); + + // Paint a faint version of the desired prompt over the version libedit draws + // (will only be requested if colors are supported) + if (m_needs_prompt_repaint) + { + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + fprintf (m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT); + MoveCursor (CursorLocation::EditingPrompt, CursorLocation::EditingCursor); + m_needs_prompt_repaint = false; + } + + if (m_multiline_enabled) + { + // Detect when the number of rows used for this input line changes due to an edit + int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); + int new_line_rows = (lineLength / m_terminal_width) + 1; + if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) + { + // Respond by repainting the current state from this line on + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + SaveEditedLine(); + DisplayInput (m_current_line_index); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); + } + m_current_line_rows = new_line_rows; + } + + // Read an actual character + while (true) + { + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char ch = 0; + + // 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_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 + // After the initial interruptible read, this is guaranteed not to block + ungetc (ch, m_input_file); + *c = fgetwc (m_input_file); + if (*c != WEOF) + return 1; +#else + *c = ch; + if(ch != (char)EOF) + return 1; +#endif + } + else + { + switch (status) + { + 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 + case lldb::eConnectionStatusNoConnection: // No connection + case lldb::eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + m_editor_status = EditorStatus::EndOfInput; + return 0; + } + } + } +} + +const char * +Editline::Prompt() +{ + if (m_color_prompts) + m_needs_prompt_repaint = true; + return m_current_prompt.c_str(); +} + +unsigned char +Editline::BreakLineCommand (int ch) +{ + // Preserve any content beyond the cursor, truncate and save the current line + const LineInfoW * info = el_wline (m_editline); + auto current_line = EditLineStringType (info->buffer, info->cursor - info->buffer); + auto new_line_fragment = EditLineStringType (info->cursor, info->lastchar - info->cursor); + m_input_lines[m_current_line_index] = current_line; + + // Ignore whitespace-only extra fragments when breaking a line + if (::IsOnlySpaces (new_line_fragment)) + new_line_fragment = EditLineConstString(""); + + // Establish the new cursor position at the start of a line when inserting a line break + m_revert_cursor_index = 0; + + // Don't perform end of input detection or automatic formatting when pasting + if (!IsInputPending (m_input_file)) + { + // If this is the end of the last line, treat this as a potential exit + if (m_current_line_index == m_input_lines.size() - 1 && new_line_fragment.length() == 0) + { + bool end_of_input = true; + if (m_is_input_complete_callback) + { + SaveEditedLine(); + auto lines = GetInputAsStringList(); + end_of_input = m_is_input_complete_callback (this, lines, m_is_input_complete_callback_baton); + + // The completion test is allowed to change the input lines when complete + if (end_of_input) + { + m_input_lines.clear(); + for (unsigned index = 0; index < lines.GetSize(); index++) + { +#if LLDB_EDITLINE_USE_WCHAR + m_input_lines.insert (m_input_lines.end(), m_utf8conv.from_bytes (lines[index])); +#else + m_input_lines.insert (m_input_lines.end(), lines[index]); +#endif + } + } + } + if (end_of_input) + { + fprintf (m_output_file, "\n"); + m_editor_status = EditorStatus::Complete; + return CC_NEWLINE; + } + } + + // Apply smart indentation + if (m_fix_indentation_callback) + { + StringList lines = GetInputAsStringList (m_current_line_index + 1); +#if LLDB_EDITLINE_USE_WCHAR + lines.AppendString (m_utf8conv.to_bytes (new_line_fragment)); +#else + lines.AppendString (new_line_fragment); +#endif + + int indent_correction = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); + new_line_fragment = FixIndentation(new_line_fragment, indent_correction); + m_revert_cursor_index = GetIndentation(new_line_fragment); + } + } + + // Insert the new line and repaint everything from the split line on down + m_input_lines.insert (m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + DisplayInput (m_current_line_index); + + // Reposition the cursor to the right line and prepare to edit the new line + SetCurrentLine (m_current_line_index + 1); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; +} + +unsigned char +Editline::DeleteNextCharCommand (int ch) +{ + LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); + + // Just delete the next character normally if possible + if (info->cursor < info->lastchar) + { + info->cursor++; + el_deletestr (m_editline, 1); + return CC_REFRESH; + } + + // Fail when at the end of the last line, except when ^D is pressed on + // the line is empty, in which case it is treated as EOF + if (m_current_line_index == m_input_lines.size() - 1) + { + if (ch == 4 && info->buffer == info->lastchar) + { + fprintf (m_output_file, "^D\n"); + m_editor_status = EditorStatus::EndOfInput; + return CC_EOF; + } + return CC_ERROR; + } + + // Prepare to combine this line with the one below + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + + // Insert the next line of text at the cursor and restore the cursor position + const EditLineCharType * cursor = info->cursor; + el_winsertstr (m_editline, m_input_lines[m_current_line_index + 1].c_str()); + info->cursor = cursor; + SaveEditedLine(); + + // Delete the extra line + m_input_lines.erase (m_input_lines.begin() + m_current_line_index + 1); + + // Clear and repaint from this line on down + DisplayInput (m_current_line_index); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); + return CC_REFRESH; +} + +unsigned char +Editline::DeletePreviousCharCommand (int ch) +{ + 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) + { + el_deletestr (m_editline, 1); + return CC_REFRESH; + } + + // No prior line and no prior character? Let the user know + if (m_current_line_index == 0) + return CC_ERROR; + + // No prior character, but prior line? Combine with the line above + SaveEditedLine(); + SetCurrentLine (m_current_line_index - 1); + auto priorLine = m_input_lines[m_current_line_index]; + m_input_lines.erase (m_input_lines.begin() + m_current_line_index); + m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; + + // Repaint from the new line down + fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine (priorLine), 1); + DisplayInput (m_current_line_index); + + // Put the cursor back where libedit expects it to be before returning to editing + // by telling libedit about the newly inserted text + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + el_winsertstr (m_editline, priorLine.c_str()); + return CC_REDISPLAY; +} + +unsigned char +Editline::PreviousLineCommand (int ch) +{ + SaveEditedLine(); + + if (m_current_line_index == 0) { + return RecallHistory (true); + } + + // Start from a known location + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + + // Treat moving up from a blank last line as a deletion of that line + if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) + { + m_input_lines.erase (m_input_lines.begin() + m_current_line_index); + fprintf (m_output_file, ANSI_CLEAR_BELOW); + } + + SetCurrentLine (m_current_line_index - 1); + fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, + CountRowsForLine (m_input_lines[m_current_line_index]), 1); + return CC_NEWLINE; +} + +unsigned char +Editline::NextLineCommand (int ch) +{ + SaveEditedLine(); + + // Handle attempts to move down from the last line + if (m_current_line_index == m_input_lines.size() - 1) + { + // Don't add an extra line if the existing last line is blank, move through history instead + if (IsOnlySpaces()) + { + return RecallHistory (false); + } + + // Determine indentation for the new line + int indentation = 0; + if (m_fix_indentation_callback) + { + StringList lines = GetInputAsStringList(); + lines.AppendString(""); + indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); + } + m_input_lines.insert (m_input_lines.end(), EditLineStringType (indentation, EditLineCharType(' '))); + } + + // Move down past the current line using newlines to force scrolling if needed + SetCurrentLine (m_current_line_index + 1); + const LineInfoW * info = el_wline (m_editline); + int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); + int cursor_row = cursor_position / m_terminal_width; + for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) + { + fprintf (m_output_file, "\n"); + } + return CC_NEWLINE; +} + +unsigned char +Editline::FixIndentationCommand (int ch) +{ + if (!m_fix_indentation_callback) + return CC_NORM; + + // Insert the character typed before proceeding + EditLineCharType inserted[] = { (EditLineCharType)ch, 0 }; + el_winsertstr (m_editline, inserted); + LineInfoW * info = const_cast<LineInfoW *>(el_wline (m_editline)); + int cursor_position = info->cursor - info->buffer; + + // Save the edits and determine the correct indentation level + SaveEditedLine(); + StringList lines = GetInputAsStringList (m_current_line_index + 1); + int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton); + + // If it is already correct no special work is needed + if (indent_correction == 0) + return CC_REFRESH; + + // Change the indentation level of the line + std::string currentLine = lines.GetStringAtIndex (m_current_line_index); + if (indent_correction > 0) + { + currentLine = currentLine.insert (0, indent_correction, ' '); + } + else + { + currentLine = currentLine.erase (0, -indent_correction); + } +#if LLDB_EDITLINE_USE_WCHAR + m_input_lines[m_current_line_index] = m_utf8conv.from_bytes (currentLine); +#else + m_input_lines[m_current_line_index] = currentLine; +#endif + + // Update the display to reflect the change + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + DisplayInput (m_current_line_index); + + // Reposition the cursor back on the original line and prepare to restart editing + // with a new cursor position + SetCurrentLine (m_current_line_index); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + m_revert_cursor_index = cursor_position + indent_correction; + return CC_NEWLINE; +} + +unsigned char +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 = const_cast<LineInfoW *>(el_wline (m_editline)); + info->cursor = info->buffer + m_revert_cursor_index; + if (info->cursor > info->lastchar) + { + info->cursor = info->lastchar; + } + m_revert_cursor_index = -1; + } + return CC_REFRESH; +} + +unsigned char +Editline::BufferStartCommand (int ch) +{ + SaveEditedLine(); + MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); + SetCurrentLine (0); + m_revert_cursor_index = 0; + return CC_NEWLINE; +} + +unsigned char +Editline::BufferEndCommand (int ch) +{ + SaveEditedLine(); + MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockEnd); + SetCurrentLine ((int)m_input_lines.size() - 1); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; +} + +unsigned char +Editline::TabCommand (int ch) +{ + if (m_completion_callback == nullptr) + return CC_ERROR; + + const LineInfo *line_info = el_line (m_editline); + StringList completions; + int page_size = 40; + + const int num_completions = m_completion_callback (line_info->buffer, + line_info->cursor, + line_info->lastchar, + 0, // Don't skip any matches (start at match zero) + -1, // Get all the matches + completions, + m_completion_callback_baton); + + if (num_completions == 0) + return CC_ERROR; + // if (num_completions == -1) + // { + // el_insertstr (m_editline, m_completion_key); + // return CC_REDISPLAY; + // } + // else + if (num_completions == -2) + { + // Replace the entire line with the first string... + el_deletestr (m_editline, line_info->cursor - line_info->buffer); + el_insertstr (m_editline, completions.GetStringAtIndex (0)); + return CC_REDISPLAY; + } + + // If we get a longer match display that first. + const char *completion_str = completions.GetStringAtIndex (0); + if (completion_str != nullptr && *completion_str != '\0') + { + el_insertstr (m_editline, completion_str); + return CC_REDISPLAY; + } + + if (num_completions > 1) + { + int num_elements = num_completions + 1; + fprintf (m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); + if (num_completions < page_size) + { + for (int i = 1; i < num_elements; i++) + { + completion_str = completions.GetStringAtIndex (i); + fprintf (m_output_file, "\n\t%s", completion_str); + } + fprintf (m_output_file, "\n"); + } + else + { + int cur_pos = 1; + char reply; + int got_char; + while (cur_pos < num_elements) + { + int endpoint = cur_pos + page_size; + if (endpoint > num_elements) + endpoint = num_elements; + for (; cur_pos < endpoint; cur_pos++) + { + completion_str = completions.GetStringAtIndex (cur_pos); + fprintf (m_output_file, "\n\t%s", completion_str); + } + + if (cur_pos >= num_elements) + { + fprintf (m_output_file, "\n"); + break; + } + + fprintf (m_output_file, "\nMore (Y/n/a): "); + reply = 'n'; + got_char = el_getc(m_editline, &reply); + if (got_char == -1 || reply == 'n') + break; + if (reply == 'a') + page_size = num_elements - cur_pos; + } + } + DisplayInput(); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); + } + return CC_REDISPLAY; +} + +void +Editline::ConfigureEditor (bool multiline) +{ + if (m_editline && m_multiline_enabled == multiline) + return; + m_multiline_enabled = multiline; + + if (m_editline) + { + // Disable edit mode to stop the terminal from flushing all input + // during the call to el_end() since we expect to have multiple editline + // instances in this program. + el_set (m_editline, EL_EDITMODE, 0); + el_end (m_editline); + } + + m_editline = el_init (m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); + TerminalSizeChanged(); + + if (m_history_sp && m_history_sp->IsValid()) + { + m_history_sp->Load(); + el_wset (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); + } + el_set (m_editline, EL_CLIENTDATA, this); + el_set (m_editline, EL_SIGNAL, 0); + el_set (m_editline, EL_EDITOR, "emacs"); + el_set (m_editline, EL_PROMPT, (EditlinePromptCallbackType)([] (EditLine *editline) { + return Editline::InstanceFor (editline)->Prompt(); + })); + + el_wset (m_editline, EL_GETCFN, + (EditlineGetCharCallbackType)([] (EditLine * editline, EditLineCharType * c) { + return Editline::InstanceFor (editline)->GetCharacter (c); + })); + + // Commands used for multiline support, registered whether or not they're used + el_set (m_editline, EL_ADDFN, "lldb-break-line", "Insert a line break", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->BreakLineCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-delete-next-char", "Delete next character", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->DeleteNextCharCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-delete-previous-char", "Delete previous character", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->DeletePreviousCharCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-previous-line", "Move to previous line", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->PreviousLineCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-next-line", "Move to next line", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->NextLineCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-buffer-start", "Move to start of buffer", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->BufferStartCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-buffer-end", "Move to end of buffer", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->BufferEndCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-fix-indentation", "Fix line indentation", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->FixIndentationCommand (ch); + })); + + // Register the complete callback under two names for compatibility with older clients using + // custom .editrc files (largely becuase libedit has a bad bug where if you have a bind command + // that tries to bind to a function name that doesn't exist, it can corrupt the heap and + // crash your process later.) + EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->TabCommand (ch); + }; + el_set (m_editline, EL_ADDFN, "lldb-complete", "Invoke completion", complete_callback); + el_set (m_editline, EL_ADDFN, "lldb_complete", "Invoke completion", complete_callback); + + // General bindings we don't mind being overridden + if (!multiline) { + el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string + } + el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode + el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete + + // Allow user-specific customization prior to registering bindings we absolutely require + el_source (m_editline, NULL); + + // Register an internal binding that external developers shouldn't use + el_set (m_editline, EL_ADDFN, "lldb-revert-line", "Revert line to saved state", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->RevertLineCommand (ch); + })); + + // Register keys that perform auto-indent correction + if (m_fix_indentation_callback && m_fix_indentation_callback_chars) + { + char bind_key[2] = { 0, 0 }; + const char * indent_chars = m_fix_indentation_callback_chars; + while (*indent_chars) + { + bind_key[0] = *indent_chars; + el_set (m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); + ++indent_chars; + } + } + + // Multi-line editor bindings + if (multiline) + { + el_set (m_editline, EL_BIND, "\n", "lldb-break-line", NULL); + el_set (m_editline, EL_BIND, "\r", "lldb-break-line", NULL); + el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL); + el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); + el_set (m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); + + // Editor-specific bindings + if (IsEmacs()) + { + el_set (m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); + el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); + } + else + { + el_set (m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); + + el_set (m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); + el_set (m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); + el_set (m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); + el_set (m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); + + // Escape is absorbed exiting edit mode, so re-register important sequences + // without the prefix + el_set (m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); + el_set (m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); + } + } +} + +//------------------------------------------------------------------ +// Editline public methods +//------------------------------------------------------------------ + +Editline * +Editline::InstanceFor (EditLine * editline) +{ + Editline * editor; + el_get (editline, EL_CLIENTDATA, &editor); + return editor; +} + +Editline::Editline (const char * editline_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts) : + m_editor_status (EditorStatus::Complete), + m_color_prompts(color_prompts), + m_input_file (input_file), + m_output_file (output_file), + m_error_file (error_file), + m_input_connection (fileno(input_file), false) +{ + // Get a shared history instance + m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; + m_history_sp = EditlineHistory::GetHistory (m_editor_name); +} + +Editline::~Editline() +{ + if (m_editline) + { + // Disable edit mode to stop the terminal from flushing all input + // during the call to el_end() since we expect to have multiple editline + // instances in this program. + el_set (m_editline, EL_EDITMODE, 0); + el_end (m_editline); + m_editline = nullptr; + } + + // EditlineHistory objects are sometimes shared between multiple + // Editline instances with the same program name. So just release + // our shared pointer and if we are the last owner, it will save the + // history to the history save file automatically. + m_history_sp.reset(); +} + +void +Editline::SetPrompt (const char * prompt) +{ + m_set_prompt = prompt == nullptr ? "" : prompt; +} + +void +Editline::SetContinuationPrompt (const char * continuation_prompt) +{ + m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; +} + +void +Editline::TerminalSizeChanged() +{ + if (m_editline != nullptr) + { + el_resize (m_editline); + int columns; + // Despite the man page claiming non-zero indicates success, it's actually zero + if (el_get (m_editline, EL_GETTC, "co", &columns) == 0) + { + m_terminal_width = columns; + if (m_current_line_rows != -1) + { + const LineInfoW * info = el_wline (m_editline); + int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); + m_current_line_rows = (lineLength / columns) + 1; + } + } + else + { + m_terminal_width = INT_MAX; + m_current_line_rows = 1; + } + } +} + +const char * +Editline::GetPrompt() +{ + return m_set_prompt.c_str(); +} + +uint32_t +Editline::GetCurrentLine() +{ + return m_current_line_index; +} + +bool +Editline::Interrupt() +{ + 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::Cancel() +{ + 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(); + } + m_editor_status = EditorStatus::Interrupted; + return result; +} + +void +Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton) +{ + m_completion_callback = callback; + m_completion_callback_baton = baton; +} + +void +Editline::SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton) +{ + m_is_input_complete_callback = callback; + m_is_input_complete_callback_baton = baton; +} + +bool +Editline::SetFixIndentationCallback (FixIndentationCallbackType callback, + void * baton, + const char * indent_chars) +{ + m_fix_indentation_callback = callback; + m_fix_indentation_callback_baton = baton; + m_fix_indentation_callback_chars = indent_chars; + return false; +} + +bool +Editline::GetLine (std::string &line, bool &interrupted) +{ + ConfigureEditor (false); + 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_revert_cursor_index = -1; + +#ifdef USE_SETUPTERM_WORKAROUND + setupterm((char *)0, fileno(m_output_file), (int *)0); +#endif + + int count; + auto input = el_wgets (m_editline, &count); + + interrupted = m_editor_status == EditorStatus::Interrupted; + if (!interrupted) + { + if (input == nullptr) + { + fprintf (m_output_file, "\n"); + m_editor_status = EditorStatus::EndOfInput; + } + else + { + m_history_sp->Enter (input); +#if LLDB_EDITLINE_USE_WCHAR + line = m_utf8conv.to_bytes (SplitLines (input)[0]); +#else + line = SplitLines (input)[0]; +#endif + m_editor_status = EditorStatus::Complete; + } + } + return m_editor_status != EditorStatus::EndOfInput; +} + +bool +Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted) +{ + ConfigureEditor (true); + + // Print the initial input lines, then move the cursor back up to the start of input + SetBaseLineNumber (first_line_number); + 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_in_history = false; + + m_revert_cursor_index = -1; + while (m_editor_status == EditorStatus::Editing) + { +#ifdef USE_SETUPTERM_WORKAROUND + setupterm((char *)0, fileno(m_output_file), (int *)0); +#endif + int count; + m_current_line_rows = -1; + el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content + el_wgets (m_editline, &count); + } + + interrupted = m_editor_status == EditorStatus::Interrupted; + if (!interrupted) + { + // Save the completed entry in history before returning + m_history_sp->Enter (CombineLines (m_input_lines).c_str()); + + lines = GetInputAsStringList(); + } + 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/contrib/llvm/tools/lldb/source/Host/common/File.cpp b/contrib/llvm/tools/lldb/source/Host/common/File.cpp new file mode 100644 index 0000000..71a6149 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/File.cpp @@ -0,0 +1,1090 @@ +//===-- File.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/File.h" + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/stat.h> + +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#else +#include <sys/ioctl.h> +#endif + +#include "llvm/Support/Process.h" // for llvm::sys::Process::FileDescriptorHasColors() + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSpec.h" + +using namespace lldb; +using namespace lldb_private; + +static const char * +GetStreamOpenModeFromOptions (uint32_t options) +{ + if (options & File::eOpenOptionAppend) + { + if (options & File::eOpenOptionRead) + { + if (options & File::eOpenOptionCanCreateNewOnly) + return "a+x"; + else + return "a+"; + } + else if (options & File::eOpenOptionWrite) + { + if (options & File::eOpenOptionCanCreateNewOnly) + return "ax"; + else + return "a"; + } + } + else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite) + { + if (options & File::eOpenOptionCanCreate) + { + if (options & File::eOpenOptionCanCreateNewOnly) + return "w+x"; + else + return "w+"; + } + else + return "r+"; + } + else if (options & File::eOpenOptionRead) + { + return "r"; + } + else if (options & File::eOpenOptionWrite) + { + return "w"; + } + return NULL; +} + +int File::kInvalidDescriptor = -1; +FILE * File::kInvalidStream = NULL; + +File::File(const char *path, uint32_t options, uint32_t permissions) : + IOObject(eFDTypeFile, false), + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (), + m_own_stream (false), + m_is_interactive (eLazyBoolCalculate), + m_is_real_terminal (eLazyBoolCalculate) +{ + Open (path, options, permissions); +} + +File::File (const FileSpec& filespec, + uint32_t options, + uint32_t permissions) : + IOObject(eFDTypeFile, false), + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_own_stream (false), + m_is_interactive (eLazyBoolCalculate), + m_is_real_terminal (eLazyBoolCalculate) + +{ + if (filespec) + { + Open (filespec.GetPath().c_str(), options, permissions); + } +} + +File::File (const File &rhs) : + IOObject(eFDTypeFile, false), + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_own_stream (false), + m_is_interactive (eLazyBoolCalculate), + m_is_real_terminal (eLazyBoolCalculate) +{ + Duplicate (rhs); +} + + +File & +File::operator = (const File &rhs) +{ + if (this != &rhs) + Duplicate (rhs); + return *this; +} + +File::~File() +{ + Close (); +} + + +int +File::GetDescriptor() const +{ + if (DescriptorIsValid()) + return m_descriptor; + + // Don't open the file descriptor if we don't need to, just get it from the + // stream if we have one. + if (StreamIsValid()) + { +#if defined(LLVM_ON_WIN32) + return _fileno(m_stream); +#else + return fileno(m_stream); +#endif + } + + // Invalid descriptor and invalid stream, return invalid descriptor. + return kInvalidDescriptor; +} + +IOObject::WaitableHandle +File::GetWaitableHandle() +{ + return m_descriptor; +} + + +void +File::SetDescriptor (int fd, bool transfer_ownership) +{ + if (IsValid()) + Close(); + m_descriptor = fd; + m_should_close_fd = transfer_ownership; +} + + +FILE * +File::GetStream () +{ + if (!StreamIsValid()) + { + if (DescriptorIsValid()) + { + const char *mode = GetStreamOpenModeFromOptions (m_options); + if (mode) + { + if (!m_should_close_fd) + { + // We must duplicate the file descriptor if we don't own it because + // when you call fdopen, the stream will own the fd +#ifdef _WIN32 + m_descriptor = ::_dup(GetDescriptor()); +#else + m_descriptor = ::fcntl(GetDescriptor(), F_DUPFD); +#endif + m_should_close_fd = true; + } + + do + { + m_stream = ::fdopen (m_descriptor, mode); + } while (m_stream == NULL && errno == EINTR); + + // If we got a stream, then we own the stream and should no + // longer own the descriptor because fclose() will close it for us + + if (m_stream) + { + m_own_stream = true; + m_should_close_fd = false; + } + } + } + } + return m_stream; +} + + +void +File::SetStream (FILE *fh, bool transfer_ownership) +{ + if (IsValid()) + Close(); + m_stream = fh; + m_own_stream = transfer_ownership; +} + +Error +File::Duplicate (const File &rhs) +{ + Error error; + if (IsValid ()) + Close(); + + if (rhs.DescriptorIsValid()) + { +#ifdef _WIN32 + m_descriptor = ::_dup(rhs.GetDescriptor()); +#else + m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD); +#endif + if (!DescriptorIsValid()) + error.SetErrorToErrno(); + else + { + m_options = rhs.m_options; + m_should_close_fd = true; + } + } + else + { + error.SetErrorString ("invalid file to duplicate"); + } + return error; +} + +Error +File::Open (const char *path, uint32_t options, uint32_t permissions) +{ + Error error; + if (IsValid()) + Close (); + + int oflag = 0; + const bool read = options & eOpenOptionRead; + const bool write = options & eOpenOptionWrite; + if (write) + { + if (read) + oflag |= O_RDWR; + else + oflag |= O_WRONLY; + + if (options & eOpenOptionAppend) + oflag |= O_APPEND; + + if (options & eOpenOptionTruncate) + oflag |= O_TRUNC; + + if (options & eOpenOptionCanCreate) + oflag |= O_CREAT; + + if (options & eOpenOptionCanCreateNewOnly) + oflag |= O_CREAT | O_EXCL; + } + else if (read) + { + oflag |= O_RDONLY; + +#ifndef _WIN32 + if (options & eOpenoptionDontFollowSymlinks) + oflag |= O_NOFOLLOW; +#endif + } + +#ifndef _WIN32 + if (options & eOpenOptionNonBlocking) + oflag |= O_NONBLOCK; + if (options & eOpenOptionCloseOnExec) + oflag |= O_CLOEXEC; +#else + oflag |= O_BINARY; +#endif + + mode_t mode = 0; + if (oflag & O_CREAT) + { + if (permissions & lldb::eFilePermissionsUserRead) mode |= S_IRUSR; + if (permissions & lldb::eFilePermissionsUserWrite) mode |= S_IWUSR; + if (permissions & lldb::eFilePermissionsUserExecute) mode |= S_IXUSR; + if (permissions & lldb::eFilePermissionsGroupRead) mode |= S_IRGRP; + if (permissions & lldb::eFilePermissionsGroupWrite) mode |= S_IWGRP; + if (permissions & lldb::eFilePermissionsGroupExecute) mode |= S_IXGRP; + if (permissions & lldb::eFilePermissionsWorldRead) mode |= S_IROTH; + if (permissions & lldb::eFilePermissionsWorldWrite) mode |= S_IWOTH; + if (permissions & lldb::eFilePermissionsWorldExecute) mode |= S_IXOTH; + } + + do + { + m_descriptor = ::open(path, oflag, mode); + } while (m_descriptor < 0 && errno == EINTR); + + if (!DescriptorIsValid()) + error.SetErrorToErrno(); + else + { + m_should_close_fd = true; + m_options = options; + } + + return error; +} + +uint32_t +File::GetPermissions(const FileSpec &file_spec, Error &error) +{ + if (file_spec) + { + struct stat file_stats; + if (::stat(file_spec.GetCString(), &file_stats) == -1) + error.SetErrorToErrno(); + else + { + error.Clear(); + return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + } + } + else + error.SetErrorString ("empty file spec"); + return 0; +} + +uint32_t +File::GetPermissions(Error &error) const +{ + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + struct stat file_stats; + if (::fstat (fd, &file_stats) == -1) + error.SetErrorToErrno(); + else + { + error.Clear(); + return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + } + } + else + { + error.SetErrorString ("invalid file descriptor"); + } + return 0; +} + + +Error +File::Close () +{ + Error error; + if (StreamIsValid() && m_own_stream) + { + if (::fclose (m_stream) == EOF) + error.SetErrorToErrno(); + } + + if (DescriptorIsValid() && m_should_close_fd) + { + if (::close (m_descriptor) != 0) + error.SetErrorToErrno(); + } + m_descriptor = kInvalidDescriptor; + m_stream = kInvalidStream; + m_options = 0; + m_own_stream = false; + m_should_close_fd = false; + m_is_interactive = eLazyBoolCalculate; + m_is_real_terminal = eLazyBoolCalculate; + return error; +} + + +Error +File::GetFileSpec (FileSpec &file_spec) const +{ + Error error; +#ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED + if (IsValid ()) + { + char path[PATH_MAX]; + if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) + error.SetErrorToErrno(); + else + file_spec.SetFile (path, false); + } + else + { + error.SetErrorString("invalid file handle"); + } +#elif defined(__linux__) + char proc[64]; + char path[PATH_MAX]; + if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0) + error.SetErrorString ("cannot resolve file descriptor"); + else + { + ssize_t len; + if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1) + error.SetErrorToErrno(); + else + { + path[len] = '\0'; + file_spec.SetFile (path, false); + } + } +#else + error.SetErrorString ("File::GetFileSpec is not supported on this platform"); +#endif + + if (error.Fail()) + file_spec.Clear(); + return error; +} + +off_t +File::SeekFromStart (off_t offset, Error *error_ptr) +{ + off_t result = 0; + if (DescriptorIsValid()) + { + result = ::lseek (m_descriptor, offset, SEEK_SET); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (StreamIsValid ()) + { + result = ::fseek(m_stream, offset, SEEK_SET); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; +} + +off_t +File::SeekFromCurrent (off_t offset, Error *error_ptr) +{ + off_t result = -1; + if (DescriptorIsValid()) + { + result = ::lseek (m_descriptor, offset, SEEK_CUR); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (StreamIsValid ()) + { + result = ::fseek(m_stream, offset, SEEK_CUR); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; +} + +off_t +File::SeekFromEnd (off_t offset, Error *error_ptr) +{ + off_t result = -1; + if (DescriptorIsValid()) + { + result = ::lseek (m_descriptor, offset, SEEK_END); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (StreamIsValid ()) + { + result = ::fseek(m_stream, offset, SEEK_END); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } + } + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; +} + +Error +File::Flush () +{ + Error error; + if (StreamIsValid()) + { + int err = 0; + do + { + err = ::fflush (m_stream); + } while (err == EOF && errno == EINTR); + + if (err == EOF) + error.SetErrorToErrno(); + } + else if (!DescriptorIsValid()) + { + error.SetErrorString("invalid file handle"); + } + return error; +} + + +Error +File::Sync () +{ + Error error; + if (DescriptorIsValid()) + { +#ifdef _WIN32 + int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor)); + if (err == 0) + error.SetErrorToGenericError(); +#else + int err = 0; + do + { + err = ::fsync (m_descriptor); + } while (err == -1 && errno == EINTR); + + if (err == -1) + error.SetErrorToErrno(); +#endif + } + else + { + error.SetErrorString("invalid file handle"); + } + 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()) + { + do + { + bytes_read = ::read (m_descriptor, buf, num_bytes); + } while (bytes_read < 0 && errno == EINTR); + + if (bytes_read == -1) + { + error.SetErrorToErrno(); + num_bytes = 0; + } + else + num_bytes = bytes_read; + } + else if (StreamIsValid()) + { + bytes_read = ::fread (buf, 1, num_bytes, m_stream); + + if (bytes_read == 0) + { + if (::feof(m_stream)) + error.SetErrorString ("feof"); + else if (::ferror (m_stream)) + error.SetErrorString ("ferror"); + num_bytes = 0; + } + else + num_bytes = bytes_read; + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + return error; +} + +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()) + { + do + { + bytes_written = ::write (m_descriptor, buf, num_bytes); + } while (bytes_written < 0 && errno == EINTR); + + if (bytes_written == -1) + { + error.SetErrorToErrno(); + num_bytes = 0; + } + else + num_bytes = bytes_written; + } + else if (StreamIsValid()) + { + bytes_written = ::fwrite (buf, 1, num_bytes, m_stream); + + if (bytes_written == 0) + { + if (::feof(m_stream)) + error.SetErrorString ("feof"); + else if (::ferror (m_stream)) + error.SetErrorString ("ferror"); + num_bytes = 0; + } + else + num_bytes = bytes_written; + + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + + return error; +} + + +Error +File::Read (void *buf, size_t &num_bytes, off_t &offset) +{ + 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) + { + ssize_t bytes_read = -1; + do + { + bytes_read = ::pread (fd, buf, num_bytes, offset); + } while (bytes_read < 0 && errno == EINTR); + + if (bytes_read < 0) + { + num_bytes = 0; + error.SetErrorToErrno(); + } + else + { + offset += bytes_read; + num_bytes = bytes_read; + } + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } +#else + long cur = ::lseek(m_descriptor, 0, SEEK_CUR); + SeekFromStart(offset); + error = Read(buf, num_bytes); + if (!error.Fail()) + SeekFromStart(cur); +#endif + return error; +} + +Error +File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp) +{ + Error error; + + if (num_bytes > 0) + { + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + struct stat file_stats; + if (::fstat (fd, &file_stats) == 0) + { + if (file_stats.st_size > offset) + { + const size_t bytes_left = file_stats.st_size - offset; + if (num_bytes > bytes_left) + num_bytes = bytes_left; + + 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()); + data_heap_ap->SetByteSize(num_bytes_plus_nul_char); + + if (data_heap_ap.get()) + { + error = Read (data_heap_ap->GetBytes(), num_bytes, offset); + if (error.Success()) + { + // Make sure we read exactly what we asked for and if we got + // less, adjust the array + if (num_bytes_plus_nul_char < data_heap_ap->GetByteSize()) + data_heap_ap->SetByteSize(num_bytes_plus_nul_char); + data_buffer_sp.reset(data_heap_ap.release()); + return error; + } + } + } + else + error.SetErrorString("file is empty"); + } + else + error.SetErrorToErrno(); + } + else + error.SetErrorString("invalid file handle"); + } + else + error.SetErrorString("invalid file handle"); + + num_bytes = 0; + data_buffer_sp.reset(); + return error; +} + +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) + { +#ifndef _WIN32 + ssize_t bytes_written = -1; + do + { + bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset); + } while (bytes_written < 0 && errno == EINTR); + + if (bytes_written < 0) + { + num_bytes = 0; + error.SetErrorToErrno(); + } + else + { + offset += bytes_written; + num_bytes = bytes_written; + } +#else + long cur = ::lseek(m_descriptor, 0, SEEK_CUR); + error = Write(buf, num_bytes); + long after = ::lseek(m_descriptor, 0, SEEK_CUR); + + if (!error.Fail()) + SeekFromStart(cur); + + offset = after; +#endif + } + else + { + num_bytes = 0; + error.SetErrorString("invalid file handle"); + } + return error; +} + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +File::Printf (const char *format, ...) +{ + va_list args; + va_start (args, format); + size_t result = PrintfVarArg (format, args); + va_end (args); + return result; +} + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +File::PrintfVarArg (const char *format, va_list args) +{ + size_t result = 0; + if (DescriptorIsValid()) + { + char *s = NULL; + result = vasprintf(&s, format, args); + if (s != NULL) + { + if (result > 0) + { + size_t s_len = result; + Write (s, s_len); + result = s_len; + } + free (s); + } + } + else if (StreamIsValid()) + { + result = ::vfprintf (m_stream, format, args); + } + return result; +} + +mode_t +File::ConvertOpenOptionsForPOSIXOpen (uint32_t open_options) +{ + mode_t mode = 0; + if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite) + mode |= O_RDWR; + else if (open_options & eOpenOptionWrite) + mode |= O_WRONLY; + + if (open_options & eOpenOptionAppend) + mode |= O_APPEND; + + if (open_options & eOpenOptionTruncate) + mode |= O_TRUNC; + + if (open_options & eOpenOptionNonBlocking) + mode |= O_NONBLOCK; + + if (open_options & eOpenOptionCanCreateNewOnly) + mode |= O_CREAT | O_EXCL; + else if (open_options & eOpenOptionCanCreate) + mode |= O_CREAT; + + return mode; +} + +void +File::CalculateInteractiveAndTerminal () +{ + const int fd = GetDescriptor(); + if (fd >= 0) + { + m_is_interactive = eLazyBoolNo; + m_is_real_terminal = eLazyBoolNo; +#if (defined(_WIN32) || defined(__ANDROID_NDK__)) + if (_isatty(fd)) + { + m_is_interactive = eLazyBoolYes; + m_is_real_terminal = eLazyBoolYes; + } +#else + if (isatty(fd)) + { + m_is_interactive = eLazyBoolYes; + struct winsize window_size; + if (::ioctl (fd, TIOCGWINSZ, &window_size) == 0) + { + if (window_size.ws_col > 0) + { + m_is_real_terminal = eLazyBoolYes; + if (llvm::sys::Process::FileDescriptorHasColors(fd)) + m_supports_colors = eLazyBoolYes; + } + } + } +#endif + } +} + +bool +File::GetIsInteractive () +{ + if (m_is_interactive == eLazyBoolCalculate) + CalculateInteractiveAndTerminal (); + return m_is_interactive == eLazyBoolYes; +} + +bool +File::GetIsRealTerminal () +{ + if (m_is_real_terminal == eLazyBoolCalculate) + CalculateInteractiveAndTerminal(); + return m_is_real_terminal == eLazyBoolYes; +} + +bool +File::GetIsTerminalWithColors () +{ + if (m_supports_colors == eLazyBoolCalculate) + CalculateInteractiveAndTerminal(); + return m_supports_colors == eLazyBoolYes; +} + diff --git a/contrib/llvm/tools/lldb/source/Host/common/FileCache.cpp b/contrib/llvm/tools/lldb/source/Host/common/FileCache.cpp new file mode 100644 index 0000000..96b2a2e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/FileCache.cpp @@ -0,0 +1,127 @@ +//===-- FileCache.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/FileCache.h" + +#include "lldb/Host/File.h" + +using namespace lldb; +using namespace lldb_private; + +FileCache *FileCache::m_instance = nullptr; + +FileCache & +FileCache::GetInstance() +{ + if (m_instance == nullptr) + m_instance = new FileCache(); + + return *m_instance; +} + +lldb::user_id_t +FileCache::OpenFile(const FileSpec &file_spec, uint32_t flags, uint32_t mode, Error &error) +{ + std::string path(file_spec.GetPath()); + if (path.empty()) + { + error.SetErrorString("empty path"); + return UINT64_MAX; + } + FileSP file_sp(new File()); + error = file_sp->Open(path.c_str(), flags, mode); + if (file_sp->IsValid() == false) + return UINT64_MAX; + lldb::user_id_t fd = file_sp->GetDescriptor(); + m_cache[fd] = file_sp; + return fd; +} + +bool +FileCache::CloseFile(lldb::user_id_t fd, Error &error) +{ + if (fd == UINT64_MAX) + { + error.SetErrorString("invalid file descriptor"); + return false; + } + FDToFileMap::iterator pos = m_cache.find(fd); + if (pos == m_cache.end()) + { + error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd); + return false; + } + FileSP file_sp = pos->second; + if (!file_sp) + { + error.SetErrorString("invalid host backing file"); + return false; + } + error = file_sp->Close(); + m_cache.erase(pos); + return error.Success(); +} + +uint64_t +FileCache::WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Error &error) +{ + if (fd == UINT64_MAX) + { + error.SetErrorString("invalid file descriptor"); + return UINT64_MAX; + } + FDToFileMap::iterator pos = m_cache.find(fd); + if (pos == m_cache.end()) + { + error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd); + return false; + } + FileSP file_sp = pos->second; + if (!file_sp) + { + error.SetErrorString("invalid host backing file"); + return UINT64_MAX; + } + if (static_cast<uint64_t>(file_sp->SeekFromStart(offset, &error)) != offset || error.Fail()) + return UINT64_MAX; + size_t bytes_written = src_len; + error = file_sp->Write(src, bytes_written); + if (error.Fail()) + return UINT64_MAX; + return bytes_written; +} + +uint64_t +FileCache::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Error &error) +{ + if (fd == UINT64_MAX) + { + error.SetErrorString("invalid file descriptor"); + return UINT64_MAX; + } + FDToFileMap::iterator pos = m_cache.find(fd); + if (pos == m_cache.end()) + { + error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd); + return false; + } + FileSP file_sp = pos->second; + if (!file_sp) + { + error.SetErrorString("invalid host backing file"); + return UINT64_MAX; + } + if (static_cast<uint64_t>(file_sp->SeekFromStart(offset, &error)) != offset || error.Fail()) + return UINT64_MAX; + size_t bytes_read = dst_len; + error = file_sp->Read(dst, bytes_read); + if (error.Fail()) + return UINT64_MAX; + return bytes_read; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp b/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp new file mode 100644 index 0000000..8885a79 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp @@ -0,0 +1,1554 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef _WIN32 +#include <dirent.h> +#else +#include "lldb/Host/windows/windows.h" +#endif +#include <fcntl.h> +#ifndef _MSC_VER +#include <libgen.h> +#endif +#include <sys/stat.h> +#include <set> +#include <string.h> +#include <fstream> + +#include "lldb/Host/Config.h" // Have to include this before we test the define... +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +#include <pwd.h> +#endif + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/CleanUp.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" + +using namespace lldb; +using namespace lldb_private; + +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]; + if (file_spec->GetPath (resolved_path, sizeof(resolved_path))) + return ::stat (resolved_path, stats_ptr) == 0; + 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. +void +FileSpec::ResolveUsername (llvm::SmallVectorImpl<char> &path) +{ +#if LLDB_CONFIG_TILDE_RESOLVES_TO_USER + if (path.empty() || path[0] != '~') + return; + + llvm::StringRef path_str(path.data(), path.size()); + size_t slash_pos = path_str.find('/', 1); + if (slash_pos == 1 || path.size() == 1) + { + // A path of ~/ resolves to the current user's home dir + llvm::SmallString<64> home_dir; + if (!llvm::sys::path::home_directory(home_dir)) + return; + + // Overwrite the ~ with the first character of the homedir, and insert + // the rest. This way we only trigger one move, whereas an insert + // followed by a delete (or vice versa) would trigger two. + path[0] = home_dir[0]; + path.insert(path.begin() + 1, home_dir.begin() + 1, home_dir.end()); + return; + } + + auto username_begin = path.begin()+1; + auto username_end = (slash_pos == llvm::StringRef::npos) + ? path.end() + : (path.begin() + slash_pos); + size_t replacement_length = std::distance(path.begin(), username_end); + + llvm::SmallString<20> username(username_begin, username_end); + struct passwd *user_entry = ::getpwnam(username.c_str()); + if (user_entry != nullptr) + { + // Copy over the first n characters of the path, where n is the smaller of the length + // of the home directory and the slash pos. + llvm::StringRef homedir(user_entry->pw_dir); + size_t initial_copy_length = std::min(homedir.size(), replacement_length); + auto src_begin = homedir.begin(); + auto src_end = src_begin + initial_copy_length; + std::copy(src_begin, src_end, path.begin()); + if (replacement_length > homedir.size()) + { + // We copied the entire home directory, but the ~username portion of the path was + // longer, so there's characters that need to be removed. + path.erase(path.begin() + initial_copy_length, username_end); + } + else if (replacement_length < homedir.size()) + { + // We copied all the way up to the slash in the destination, but there's still more + // characters that need to be inserted. + path.insert(username_end, src_end, homedir.end()); + } + } + else + { + // Unable to resolve username (user doesn't exist?) + path.clear(); + } +#endif +} + +size_t +FileSpec::ResolvePartialUsername (const char *partial_name, StringList &matches) +{ +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + size_t extant_entries = matches.GetSize(); + + setpwent(); + struct passwd *user_entry; + const char *name_start = partial_name + 1; + std::set<std::string> name_list; + + while ((user_entry = getpwent()) != NULL) + { + if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name) + { + std::string tmp_buf("~"); + tmp_buf.append(user_entry->pw_name); + tmp_buf.push_back('/'); + name_list.insert(tmp_buf); + } + } + std::set<std::string>::iterator pos, end = name_list.end(); + for (pos = name_list.begin(); pos != end; pos++) + { + matches.AppendString((*pos).c_str()); + } + return matches.GetSize() - extant_entries; +#else + // Resolving home directories is not supported, just copy the path... + return 0; +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +} + +void +FileSpec::Resolve (llvm::SmallVectorImpl<char> &path) +{ + if (path.empty()) + return; + +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + if (path[0] == '~') + 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() : + m_directory(), + m_filename(), + m_syntax(FileSystem::GetNativePathSyntax()) +{ +} + +//------------------------------------------------------------------ +// Default constructor that can take an optional full path to a +// file on disk. +//------------------------------------------------------------------ +FileSpec::FileSpec(const char *pathname, bool resolve_path, PathSyntax syntax) : + m_directory(), + m_filename(), + m_is_resolved(false), + m_syntax(syntax) +{ + if (pathname && pathname[0]) + 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 +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec& rhs) : + m_directory (rhs.m_directory), + m_filename (rhs.m_filename), + m_is_resolved (rhs.m_is_resolved), + m_syntax (rhs.m_syntax) +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec* rhs) : + m_directory(), + m_filename() +{ + if (rhs) + *this = *rhs; +} + +//------------------------------------------------------------------ +// Virtual destructor in case anyone inherits from this class. +//------------------------------------------------------------------ +FileSpec::~FileSpec() +{ +} + +//------------------------------------------------------------------ +// Assignment operator. +//------------------------------------------------------------------ +const FileSpec& +FileSpec::operator= (const FileSpec& rhs) +{ + if (this != &rhs) + { + m_directory = rhs.m_directory; + m_filename = rhs.m_filename; + m_is_resolved = rhs.m_is_resolved; + m_syntax = rhs.m_syntax; + } + return *this; +} + +//------------------------------------------------------------------ +// 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 +// string values for quick comparison and efficient memory usage. +//------------------------------------------------------------------ +void +FileSpec::SetFile (const char *pathname, bool resolve, PathSyntax syntax) +{ + m_filename.Clear(); + m_directory.Clear(); + m_is_resolved = false; + m_syntax = (syntax == ePathSyntaxHostNative) ? FileSystem::GetNativePathSyntax() : syntax; + + if (pathname == NULL || pathname[0] == '\0') + return; + + llvm::SmallString<64> normalized(pathname); + + if (resolve) + { + FileSpec::Resolve (normalized); + m_is_resolved = true; + } + + // Only normalize after resolving the path. Resolution will modify the path + // string, potentially adding wrong kinds of slashes to the path, that need + // to be re-normalized. + Normalize(normalized, syntax); + + llvm::StringRef resolve_path_ref(normalized.c_str()); + llvm::StringRef filename_ref = llvm::sys::path::filename(resolve_path_ref); + if (!filename_ref.empty()) + { + m_filename.SetString (filename_ref); + llvm::StringRef directory_ref = llvm::sys::path::parent_path(resolve_path_ref); + if (!directory_ref.empty()) + m_directory.SetString(directory_ref); + } + else + 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: +// +// if (file_spec) +// {} +//---------------------------------------------------------------------- +FileSpec::operator bool() const +{ + return m_filename || m_directory; +} + +//---------------------------------------------------------------------- +// Logical NOT operator. This allows code to check any FileSpec +// objects to see if they are invalid using code such as: +// +// if (!file_spec) +// {} +//---------------------------------------------------------------------- +bool +FileSpec::operator!() const +{ + return !m_directory && !m_filename; +} + +//------------------------------------------------------------------ +// Equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator== (const FileSpec& rhs) const +{ + if (m_filename == rhs.m_filename) + { + if (m_directory == rhs.m_directory) + return true; + + // TODO: determine if we want to keep this code in here. + // The code below was added to handle a case where we were + // trying to set a file and line breakpoint and one path + // was resolved, and the other not and the directory was + // in a mount point that resolved to a more complete path: + // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling + // this out... + if (IsResolved() && rhs.IsResolved()) + { + // Both paths are resolved, no need to look further... + return false; + } + + FileSpec resolved_lhs(*this); + + // If "this" isn't resolved, resolve it + if (!IsResolved()) + { + if (resolved_lhs.ResolvePath()) + { + // This path wasn't resolved but now it is. Check if the resolved + // directory is the same as our unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + m_is_resolved = (m_directory == resolved_lhs.m_directory); + } + else + return false; + } + + FileSpec resolved_rhs(rhs); + if (!rhs.IsResolved()) + { + if (resolved_rhs.ResolvePath()) + { + // rhs's path wasn't resolved but now it is. Check if the resolved + // directory is the same as rhs's unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory); + } + else + return false; + } + + // If we reach this point in the code we were able to resolve both paths + // and since we only resolve the paths if the basenames are equal, then + // we can just check if both directories are equal... + return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory(); + } + return false; +} + +//------------------------------------------------------------------ +// Not equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator!= (const FileSpec& rhs) const +{ + return !(*this == rhs); +} + +//------------------------------------------------------------------ +// Less than operator +//------------------------------------------------------------------ +bool +FileSpec::operator< (const FileSpec& rhs) const +{ + return FileSpec::Compare(*this, rhs, true) < 0; +} + +//------------------------------------------------------------------ +// Dump a FileSpec object to a stream +//------------------------------------------------------------------ +Stream& +lldb_private::operator << (Stream &s, const FileSpec& f) +{ + f.Dump(&s); + return s; +} + +//------------------------------------------------------------------ +// Clear this object by releasing both the directory and filename +// string values and making them both the empty string. +//------------------------------------------------------------------ +void +FileSpec::Clear() +{ + m_directory.Clear(); + m_filename.Clear(); +} + +//------------------------------------------------------------------ +// Compare two FileSpec objects. If "full" is true, then both +// the directory and the filename must match. If "full" is false, +// then the directory names for "a" and "b" are only compared if +// they are both non-empty. This allows a FileSpec object to only +// contain a filename and it can match FileSpec objects that have +// matching filenames with different paths. +// +// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" +// and "1" if "a" is greater than "b". +//------------------------------------------------------------------ +int +FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) +{ + int result = 0; + + // If full is true, then we must compare both the directory and filename. + + // If full is false, then if either directory is empty, then we match on + // the basename only, and if both directories have valid values, we still + // do a full compare. This allows for matching when we just have a filename + // in one of the FileSpec objects. + + if (full || (a.m_directory && b.m_directory)) + { + result = ConstString::Compare(a.m_directory, b.m_directory); + if (result) + return result; + } + return ConstString::Compare (a.m_filename, b.m_filename); +} + +bool +FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full, bool remove_backups) +{ + if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) + return a.m_filename == b.m_filename; + else if (remove_backups == false) + return a == b; + else + { + if (a.m_filename != b.m_filename) + return false; + if (a.m_directory == b.m_directory) + return true; + ConstString a_without_dots; + ConstString b_without_dots; + + RemoveBackupDots (a.m_directory, a_without_dots); + RemoveBackupDots (b.m_directory, b_without_dots); + return a_without_dots == b_without_dots; + } +} + +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(); + result_const_str.Clear(); + if (!input || input[0] == '\0') + return; + + const char win_sep = '\\'; + const char unix_sep = '/'; + char found_sep; + const char *win_backup = "\\.."; + const char *unix_backup = "/.."; + + bool is_win = false; + + // Determine the platform for the path (win or unix): + + if (input[0] == win_sep) + is_win = true; + else if (input[0] == unix_sep) + is_win = false; + else if (input[1] == ':') + is_win = true; + else if (strchr(input, unix_sep) != nullptr) + is_win = false; + else if (strchr(input, win_sep) != nullptr) + is_win = true; + else + { + // No separators at all, no reason to do any work here. + result_const_str = input_const_str; + return; + } + + llvm::StringRef backup_sep; + if (is_win) + { + found_sep = win_sep; + backup_sep = win_backup; + } + else + { + found_sep = unix_sep; + backup_sep = unix_backup; + } + + llvm::StringRef input_ref(input); + llvm::StringRef curpos(input); + + bool had_dots = false; + std::string result; + + while (1) + { + // Start of loop + llvm::StringRef before_sep; + std::pair<llvm::StringRef, llvm::StringRef> around_sep = curpos.split(backup_sep); + + before_sep = around_sep.first; + curpos = around_sep.second; + + if (curpos.empty()) + { + 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()); + } + } + break; + } + had_dots = true; + + unsigned num_backups = 1; + while (curpos.startswith(backup_sep)) + { + num_backups++; + curpos = curpos.slice(backup_sep.size(), curpos.size()); + } + + size_t end_pos = before_sep.size(); + while (num_backups-- > 0) + { + end_pos = before_sep.rfind(found_sep, end_pos); + if (end_pos == llvm::StringRef::npos) + { + result_const_str = input_const_str; + return; + } + } + result.append(before_sep.data(), end_pos); + } + + if (had_dots) + result_const_str.SetCString(result.c_str()); + else + result_const_str = input_const_str; + + return; +} + +//------------------------------------------------------------------ +// Dump the object to the supplied stream. If the object contains +// a valid directory name, it will be displayed followed by a +// directory delimiter, and the filename. +//------------------------------------------------------------------ +void +FileSpec::Dump(Stream *s) const +{ + if (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); + } +} + +//------------------------------------------------------------------ +// Returns true if the file exists. +//------------------------------------------------------------------ +bool +FileSpec::Exists () const +{ + struct stat file_stats; + return GetFileStats (this, &file_stats); +} + +bool +FileSpec::Readable () const +{ + const uint32_t permissions = GetPermissions(); + if (permissions & eFilePermissionsEveryoneR) + return true; + return false; +} + +bool +FileSpec::ResolveExecutableLocation () +{ + if (!m_directory) + { + const char *file_cstr = m_filename.GetCString(); + if (file_cstr) + { + const std::string file_str (file_cstr); + llvm::ErrorOr<std::string> error_or_path = llvm::sys::findProgramByName (file_str); + if (!error_or_path) + return false; + std::string path = error_or_path.get(); + llvm::StringRef dir_ref = llvm::sys::path::parent_path(path); + if (!dir_ref.empty()) + { + // FindProgramByName returns "." if it can't find the file. + if (strcmp (".", dir_ref.data()) == 0) + return false; + + m_directory.SetCString (dir_ref.data()); + if (Exists()) + return true; + else + { + // If FindProgramByName found the file, it returns the directory + filename in its return results. + // We need to separate them. + FileSpec tmp_file (dir_ref.data(), false); + if (tmp_file.Exists()) + { + m_directory = tmp_file.m_directory; + return true; + } + } + } + } + } + + return false; +} + +bool +FileSpec::ResolvePath () +{ + if (m_is_resolved) + return true; // We have already resolved this path + + char path_buf[PATH_MAX]; + if (!GetPath (path_buf, PATH_MAX, false)) + return false; + // SetFile(...) will set m_is_resolved correctly if it can resolve the path + SetFile (path_buf, true); + return m_is_resolved; +} + +uint64_t +FileSpec::GetByteSize() const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + return file_stats.st_size; + return 0; +} + +FileSpec::PathSyntax +FileSpec::GetPathSyntax() const +{ + return m_syntax; +} + +FileSpec::FileType +FileSpec::GetFileType () const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + { + mode_t file_type = file_stats.st_mode & S_IFMT; + switch (file_type) + { + case S_IFDIR: return eFileTypeDirectory; + case S_IFREG: return eFileTypeRegular; +#ifndef _WIN32 + case S_IFIFO: return eFileTypePipe; + case S_IFSOCK: return eFileTypeSocket; + case S_IFLNK: return eFileTypeSymbolicLink; +#endif + default: + break; + } + return eFileTypeUnknown; + } + return eFileTypeInvalid; +} + +bool +FileSpec::IsSymbolicLink () const +{ + char resolved_path[PATH_MAX]; + if (!GetPath (resolved_path, sizeof (resolved_path))) + return false; + +#ifdef _WIN32 + auto attrs = ::GetFileAttributes (resolved_path); + if (attrs == INVALID_FILE_ATTRIBUTES) + return false; + + return (attrs & FILE_ATTRIBUTE_REPARSE_POINT); +#else + struct stat file_stats; + if (::lstat (resolved_path, &file_stats) != 0) + return false; + + return (file_stats.st_mode & S_IFMT) == S_IFLNK; +#endif +} + +uint32_t +FileSpec::GetPermissions () const +{ + uint32_t file_permissions = 0; + if (*this) + FileSystem::GetFilePermissions(*this, file_permissions); + return file_permissions; +} + +TimeValue +FileSpec::GetModificationTime () const +{ + TimeValue mod_time; + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + mod_time.OffsetWithSeconds(file_stats.st_mtime); + return mod_time; +} + +//------------------------------------------------------------------ +// Directory string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetDirectory() +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Directory string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetDirectory() const +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Filename string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetFilename() +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Filename string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetFilename() const +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Extract the directory and path into a fixed buffer. This is +// needed as the directory and path are stored in separate string +// values. +//------------------------------------------------------------------ +size_t +FileSpec::GetPath(char *path, size_t path_max_len, bool denormalize) const +{ + if (!path) + return 0; + + std::string result = GetPath(denormalize); + ::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 +{ + llvm::SmallString<64> result; + 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 +{ + if (m_filename) + { + const char *filename = m_filename.GetCString(); + const char* dot_pos = strrchr(filename, '.'); + if (dot_pos && dot_pos[1] != '\0') + return ConstString(dot_pos+1); + } + return ConstString(); +} + +ConstString +FileSpec::GetFileNameStrippingExtension () const +{ + const char *filename = m_filename.GetCString(); + if (filename == NULL) + return ConstString(); + + const char* dot_pos = strrchr(filename, '.'); + if (dot_pos == NULL) + return m_filename; + + return ConstString(filename, dot_pos-filename); +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data is memory mapped and +// will lazily page in data from the file as memory is accessed. +// The data that is mapped will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const +{ + DataBufferSP data_sp; + std::unique_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap()); + if (mmap_data.get()) + { + const size_t mapped_length = mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size); + if (((file_size == SIZE_MAX) && (mapped_length > 0)) || (mapped_length >= file_size)) + data_sp.reset(mmap_data.release()); + } + 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 +// returns the size in bytes of this object, not any shared string +// values it may refer to. +//------------------------------------------------------------------ +size_t +FileSpec::MemorySize() const +{ + return m_filename.MemorySize() + m_directory.MemorySize(); +} + + +size_t +FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const +{ + Error error; + size_t bytes_read = 0; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + off_t file_offset_after_seek = file_offset; + bytes_read = dst_len; + error = file.Read(dst, bytes_read, file_offset_after_seek); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return bytes_read; +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data copies into a heap based +// buffer that lives in the DataBuffer shared pointer object returned. +// The data that is cached will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::ReadFileContents (off_t file_offset, size_t file_size, Error *error_ptr) const +{ + Error error; + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + const bool null_terminate = false; + error = file.Read (file_size, file_offset, null_terminate, data_sp); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return data_sp; +} + +DataBufferSP +FileSpec::ReadFileContentsAsCString(Error *error_ptr) +{ + Error error; + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + off_t offset = 0; + size_t length = SIZE_MAX; + const bool null_terminate = true; + error = file.Read (length, offset, null_terminate, data_sp); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return data_sp; +} + +size_t +FileSpec::ReadFileLines (STLStringArray &lines) +{ + lines.clear(); + char path[PATH_MAX]; + if (GetPath(path, sizeof(path))) + { + std::ifstream file_stream (path); + + if (file_stream) + { + std::string line; + while (getline (file_stream, line)) + lines.push_back (line); + } + } + return lines.size(); +} + +FileSpec::EnumerateDirectoryResult +FileSpec::ForEachItemInDirectory (const char *dir_path, DirectoryCallback const &callback) +{ + if (dir_path && dir_path[0]) + { +#if _WIN32 + std::string szDir(dir_path); + szDir += "\\*"; + + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile(szDir.c_str(), &ffd); + + if (hFind == INVALID_HANDLE_VALUE) + { + return eEnumerateDirectoryResultNext; + } + + do + { + FileSpec::FileType file_type = eFileTypeUnknown; + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + size_t len = strlen(ffd.cFileName); + + if (len == 1 && ffd.cFileName[0] == '.') + continue; + + if (len == 2 && ffd.cFileName[0] == '.' && ffd.cFileName[1] == '.') + continue; + + file_type = eFileTypeDirectory; + } + else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) + { + file_type = eFileTypeOther; + } + else + { + file_type = eFileTypeRegular; + } + + 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); + + EnumerateDirectoryResult result = callback (file_type, child_path_spec); + + switch (result) + { + 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::ForEachItemInDirectory(child_path, callback) == eEnumerateDirectoryResultQuit) + { + // 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 + // keep enumerating. + return eEnumerateDirectoryResultNext; + + case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level + return eEnumerateDirectoryResultQuit; + } + } + } while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); +#else + lldb_utility::CleanUp <DIR *, int> dir_path_dir(opendir(dir_path), NULL, closedir); + if (dir_path_dir.is_valid()) + { + char dir_path_last_char = dir_path[strlen(dir_path) - 1]; + + long path_max = fpathconf (dirfd (dir_path_dir.get()), _PC_NAME_MAX); +#if defined (__APPLE_) && defined (__DARWIN_MAXPATHLEN) + if (path_max < __DARWIN_MAXPATHLEN) + path_max = __DARWIN_MAXPATHLEN; +#endif + struct dirent *buf, *dp; + buf = (struct dirent *) malloc (offsetof (struct dirent, d_name) + path_max + 1); + + while (buf && readdir_r(dir_path_dir.get(), buf, &dp) == 0 && dp) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + size_t len = strlen(dp->d_name); + + if (len == 1 && dp->d_name[0] == '.') + continue; + + if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + FileSpec::FileType file_type = eFileTypeUnknown; + + switch (dp->d_type) + { + 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; break; +#endif + } + + 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)) + { + // Don't resolve the file type or path + FileSpec child_path_spec (child_path, false); + + EnumerateDirectoryResult result = callback (file_type, child_path_spec); + + switch (result) + { + 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::ForEachItemInDirectory (child_path, callback) == eEnumerateDirectoryResultQuit) + { + // 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 + // keep enumerating. + if (buf) + free (buf); + return eEnumerateDirectoryResultNext; + + case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level + if (buf) + free (buf); + return eEnumerateDirectoryResultQuit; + } + } + } + if (buf) + { + free (buf); + } + } +#endif + } + // By default when exiting a directory, we tell the parent enumeration + // to continue enumerating. + 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 +{ + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + return FileSpec(new_path,resolve); + 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); + return FileSpec(stream.GetData(),resolve); +} + +FileSpec +FileSpec::CopyByRemovingLastPathComponent () const +{ + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + return FileSpec("",resolve); + if (m_directory.IsEmpty()) + return FileSpec("",resolve); + if (m_filename.IsEmpty()) + { + const char* dir_cstr = m_directory.GetCString(); + const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); + + // check for obvious cases before doing the full thing + if (!last_slash_ptr) + return FileSpec("",resolve); + if (last_slash_ptr == dir_cstr) + return FileSpec("/",resolve); + + size_t last_slash_pos = last_slash_ptr - dir_cstr+1; + ConstString new_path(dir_cstr,last_slash_pos); + return FileSpec(new_path.GetCString(),resolve); + } + else + return FileSpec(m_directory.GetCString(),resolve); +} + +ConstString +FileSpec::GetLastPathComponent () const +{ + if (m_filename) + return m_filename; + if (m_directory) + { + const char* dir_cstr = m_directory.GetCString(); + const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); + if (last_slash_ptr == NULL) + return m_directory; + if (last_slash_ptr == dir_cstr) + { + if (last_slash_ptr[1] == 0) + return ConstString(last_slash_ptr); + else + return ConstString(last_slash_ptr+1); + } + if (last_slash_ptr[1] != 0) + return ConstString(last_slash_ptr+1); + const char* penultimate_slash_ptr = last_slash_ptr; + while (*penultimate_slash_ptr) + { + --penultimate_slash_ptr; + if (penultimate_slash_ptr == dir_cstr) + break; + if (*penultimate_slash_ptr == '/') + break; + } + ConstString result(penultimate_slash_ptr+1,last_slash_ptr-penultimate_slash_ptr); + return result; + } + return ConstString(); +} + +void +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); + return; + } + StreamString stream; + if (m_filename.IsEmpty()) + stream.Printf("%s/%s", new_path, m_directory.GetCString()); + else if (m_directory.IsEmpty()) + stream.Printf("%s/%s", new_path, m_filename.GetCString()); + else + 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() || (m_filename.GetLength() == 1 && m_filename.GetCString()[0] == '.')) + 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; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + { + SetFile("",resolve); + return; + } + if (m_directory.IsEmpty()) + { + SetFile("",resolve); + return; + } + if (m_filename.IsEmpty()) + { + const char* dir_cstr = m_directory.GetCString(); + const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); + + // check for obvious cases before doing the full thing + if (!last_slash_ptr) + { + SetFile("",resolve); + return; + } + if (last_slash_ptr == dir_cstr) + { + SetFile("/",resolve); + return; + } + size_t last_slash_pos = last_slash_ptr - dir_cstr+1; + ConstString new_path(dir_cstr,last_slash_pos); + SetFile(new_path.GetCString(),resolve); + } + else + SetFile(m_directory.GetCString(),resolve); +} +//------------------------------------------------------------------ +/// Returns true if the filespec represents an implementation source +/// file (files with a ".c", ".cpp", ".m", ".mm" (many more) +/// extension). +/// +/// @return +/// \b true if the filespec represents an implementation source +/// file, \b false otherwise. +//------------------------------------------------------------------ +bool +FileSpec::IsSourceImplementationFile () const +{ + ConstString extension (GetFileNameExtension()); + if (extension) + { + static RegularExpression g_source_file_regex ("^([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|[cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO][rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])$"); + return g_source_file_regex.Execute (extension.GetCString()); + } + return false; +} + +bool +FileSpec::IsRelative() const +{ + const char *dir = m_directory.GetCString(); + llvm::StringRef directory(dir ? dir : ""); + + if (directory.size() > 0) + { + if (PathSyntaxIsPosix(m_syntax)) + { + // If the path doesn't start with '/' or '~', return true + switch (directory[0]) + { + case '/': + case '~': + return false; + default: + return true; + } + } + else + { + if (directory.size() >= 2 && directory[1] == ':') + return false; + if (directory[0] == '/') + return false; + return true; + } + } + else if (m_filename) + { + // No directory, just a basename, return true + return true; + } + return false; +} + +bool +FileSpec::IsAbsolute() const +{ + return !FileSpec::IsRelative(); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/FileSystem.cpp b/contrib/llvm/tools/lldb/source/Host/common/FileSystem.cpp new file mode 100644 index 0000000..5a5dbc7 --- /dev/null +++ b/contrib/llvm/tools/lldb/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/contrib/llvm/tools/lldb/source/Host/common/GetOptInc.cpp b/contrib/llvm/tools/lldb/source/Host/common/GetOptInc.cpp new file mode 100644 index 0000000..7689f36 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/GetOptInc.cpp @@ -0,0 +1,473 @@ +#include "lldb/Host/common/GetOptInc.h" + +#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || defined(REPLACE_GETOPT_LONG_ONLY) + +// getopt.cpp +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#if defined(REPLACE_GETOPT) +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static const char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* +* Compute the greatest common divisor of a and b. +*/ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +static void pass() {} +#define warnx(a, ...) pass(); + +/* +* Exchange the block from nonopt_start to nonopt_end with the block +* from nonopt_end to opt_end (keeping the same order of arguments +* in each block). +*/ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, +char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end + i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **)nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* +* parse_long_options -- +* Parse long options in argc/argv argument vector. +* Returns -1 if short_too is set and the option does not match long_options. +*/ +static int +parse_long_options(char * const *nargv, const char *options, +const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = const_cast<char*>(place); + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } + else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } + else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } + else + return (long_options[match].val); +} + +/* +* getopt_internal -- +* Parse argc/argv argument vector. Called by user level routines. +*/ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, +const struct option *long_options, int *idx, int flags) +{ + const char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1 || optreset) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } + else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } + else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = const_cast<char*>(place); + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } + else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* +* getopt -- +* Parse argc/argv argument vector. +* +* [eventually this will replace the BSD getopt] +*/ +#if defined(REPLACE_GETOPT) +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif + +/* +* getopt_long -- +* Parse argc/argv argument vector. +*/ +#if defined(REPLACE_GETOPT_LONG) +int +getopt_long(int nargc, char * const *nargv, const char *options, +const struct option *long_options, int *idx) +{ + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} +#endif + +/* +* getopt_long_only -- +* Parse argc/argv argument vector. +*/ +#if defined(REPLACE_GETOPT_LONG_ONLY) +int +getopt_long_only(int nargc, char * const *nargv, const char *options, +const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE | FLAG_LONGONLY)); +} +#endif + +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/common/Host.cpp b/contrib/llvm/tools/lldb/source/Host/common/Host.cpp new file mode 100644 index 0000000..e89f4de --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Host.cpp @@ -0,0 +1,1082 @@ +//===-- Host.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C includes +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <unistd.h> +#include <dlfcn.h> +#include <grp.h> +#include <netdb.h> +#include <pwd.h> +#include <sys/stat.h> +#endif + +#if defined (__APPLE__) +#include <mach/mach_port.h> +#include <mach/mach_init.h> +#include <mach-o/dyld.h> +#endif + +#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) || defined(__NetBSD__) +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) +#include <spawn.h> +#endif +#include <sys/wait.h> +#include <sys/syscall.h> +#endif + +#if defined (__FreeBSD__) +#include <pthread_np.h> +#endif + +// C++ includes +#include <limits> + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/ProcessLauncher.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/lldb-private-forward.h" +#include "llvm/Support/FileSystem.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/CleanUp.h" +#include "llvm/ADT/SmallString.h" + +#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 + +#if defined (__APPLE__) +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +#endif + +extern "C" +{ + int __pthread_chdir(const char *path); + int __pthread_fchdir (int fildes); +} + +#endif + +using namespace lldb; +using namespace lldb_private; + +#if !defined (__APPLE__) && !defined (_WIN32) +struct MonitorInfo +{ + lldb::pid_t pid; // The process ID to monitor + Host::MonitorChildProcessCallback callback; // The callback function to call when "pid" exits or signals + void *callback_baton; // The callback baton for the callback function + bool monitor_signals; // If true, call the callback when "pid" gets signaled. +}; + +static thread_result_t +MonitorChildProcessThreadFunction (void *arg); + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ + MonitorInfo * info_ptr = new MonitorInfo(); + + info_ptr->pid = pid; + info_ptr->callback = callback; + info_ptr->callback_baton = callback_baton; + info_ptr->monitor_signals = monitor_signals; + + char thread_name[256]; + ::snprintf(thread_name, sizeof(thread_name), "<lldb.host.wait4(pid=%" PRIu64 ")>", pid); + return ThreadLauncher::LaunchThread(thread_name, MonitorChildProcessThreadFunction, info_ptr, NULL); +} + +#ifndef __linux__ +//------------------------------------------------------------------ +// Scoped class that will disable thread canceling when it is +// constructed, and exception safely restore the previous value it +// when it goes out of scope. +//------------------------------------------------------------------ +class ScopedPThreadCancelDisabler +{ +public: + ScopedPThreadCancelDisabler() + { + // Disable the ability for this thread to be cancelled + int err = ::pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &m_old_state); + if (err != 0) + m_old_state = -1; + } + + ~ScopedPThreadCancelDisabler() + { + // Restore the ability for this thread to be cancelled to what it + // previously was. + if (m_old_state != -1) + ::pthread_setcancelstate (m_old_state, 0); + } +private: + int m_old_state; // Save the old cancelability state. +}; +#endif // __linux__ + +#ifdef __linux__ +#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) +static __thread volatile sig_atomic_t g_usr1_called; +#else +static thread_local volatile sig_atomic_t g_usr1_called; +#endif + +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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + const char *function = __FUNCTION__; + if (log) + log->Printf ("%s (arg = %p) thread starting...", function, arg); + + MonitorInfo *info = (MonitorInfo *)arg; + + const Host::MonitorChildProcessCallback callback = info->callback; + void * const callback_baton = info->callback_baton; + const bool monitor_signals = info->monitor_signals; + + assert (info->pid <= UINT32_MAX); + const ::pid_t pid = monitor_signals ? -1 * getpgid(info->pid) : info->pid; + + delete info; + + int status = -1; +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) + #define __WALL 0 +#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); + + if (CheckForMonitorCancellation ()) + break; + + // Get signals from all children with same process group of pid + const ::pid_t wait_pid = ::waitpid (pid, &status, options); + + if (CheckForMonitorCancellation ()) + break; + + if (wait_pid == -1) + { + if (errno == EINTR) + continue; + else + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because waitpid failed (%s)...", __FUNCTION__, arg, strerror(errno)); + break; + } + } + else if (wait_pid > 0) + { + bool exited = false; + int signal = 0; + int exit_status = 0; + const char *status_cstr = NULL; + if (WIFSTOPPED(status)) + { + signal = WSTOPSIG(status); + status_cstr = "STOPPED"; + } + else if (WIFEXITED(status)) + { + exit_status = WEXITSTATUS(status); + status_cstr = "EXITED"; + exited = true; + } + else if (WIFSIGNALED(status)) + { + signal = WTERMSIG(status); + status_cstr = "SIGNALED"; + if (wait_pid == abs(pid)) { + exited = true; + exit_status = -1; + } + } + else + { + status_cstr = "(\?\?\?)"; + } + + // Scope for pthread_cancel_disabler + { +#ifndef __linux__ + ScopedPThreadCancelDisabler pthread_cancel_disabler; +#endif + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i) => pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", + function, + pid, + options, + wait_pid, + status, + status_cstr, + signal, + exit_status); + + if (exited || (signal != 0 && monitor_signals)) + { + bool callback_return = false; + if (callback) + callback_return = callback (callback_baton, wait_pid, exited, signal, exit_status); + + // If our process exited, then this thread should exit + if (exited && wait_pid == abs(pid)) + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because pid received exit signal...", __FUNCTION__, arg); + break; + } + // If the callback returns true, it means this process should + // exit + if (callback_return) + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because callback returned true...", __FUNCTION__, arg); + break; + } + } + } + } + } + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("%s (arg = %p) thread exiting...", __FUNCTION__, arg); + + return NULL; +} + +#endif // #if !defined (__APPLE__) && !defined (_WIN32) + +#if !defined (__APPLE__) + +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ + vfprintf (stderr, format, args); +} + +#endif + +void +Host::SystemLog (SystemLogType type, const char *format, ...) +{ + va_list args; + va_start (args, format); + SystemLog (type, format, args); + va_end (args); +} + +lldb::pid_t +Host::GetCurrentProcessID() +{ + return ::getpid(); +} + +#ifndef _WIN32 + +lldb::tid_t +Host::GetCurrentThreadID() +{ +#if defined (__APPLE__) + // Calling "mach_thread_self()" bumps the reference count on the thread + // port, so we need to deallocate it. mach_task_self() doesn't bump the ref + // count. + thread_port_t thread_self = mach_thread_self(); + mach_port_deallocate(mach_task_self(), thread_self); + return thread_self; +#elif defined(__FreeBSD__) + return lldb::tid_t(pthread_getthreadid_np()); +#elif defined(__ANDROID_NDK__) + return lldb::tid_t(gettid()); +#elif defined(__linux__) + return lldb::tid_t(syscall(SYS_gettid)); +#else + return lldb::tid_t(pthread_self()); +#endif +} + +lldb::thread_t +Host::GetCurrentThread () +{ + return lldb::thread_t(pthread_self()); +} + +const char * +Host::GetSignalAsCString (int signo) +{ + switch (signo) + { + case SIGHUP: return "SIGHUP"; // 1 hangup + case SIGINT: return "SIGINT"; // 2 interrupt + case SIGQUIT: return "SIGQUIT"; // 3 quit + case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught) + case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught) + case SIGABRT: return "SIGABRT"; // 6 abort() +#if defined(SIGPOLL) +#if !defined(SIGIO) || (SIGPOLL != SIGIO) +// Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to +// fail with 'multiple define cases with same value' + case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) +#endif +#endif +#if defined(SIGEMT) + case SIGEMT: return "SIGEMT"; // 7 EMT instruction +#endif + case SIGFPE: return "SIGFPE"; // 8 floating point exception + case SIGKILL: return "SIGKILL"; // 9 kill (cannot be caught or ignored) + case SIGBUS: return "SIGBUS"; // 10 bus error + case SIGSEGV: return "SIGSEGV"; // 11 segmentation violation + case SIGSYS: return "SIGSYS"; // 12 bad argument to system call + case SIGPIPE: return "SIGPIPE"; // 13 write on a pipe with no one to read it + case SIGALRM: return "SIGALRM"; // 14 alarm clock + case SIGTERM: return "SIGTERM"; // 15 software termination signal from kill + case SIGURG: return "SIGURG"; // 16 urgent condition on IO channel + case SIGSTOP: return "SIGSTOP"; // 17 sendable stop signal not from tty + case SIGTSTP: return "SIGTSTP"; // 18 stop signal from tty + case SIGCONT: return "SIGCONT"; // 19 continue a stopped process + case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit + case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read + case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local<OSTOP) +#if defined(SIGIO) + case SIGIO: return "SIGIO"; // 23 input/output possible signal +#endif + case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit + case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit + case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm + case SIGPROF: return "SIGPROF"; // 27 profiling time alarm +#if defined(SIGWINCH) + case SIGWINCH: return "SIGWINCH"; // 28 window size changes +#endif +#if defined(SIGINFO) + case SIGINFO: return "SIGINFO"; // 29 information request +#endif + case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1 + case SIGUSR2: return "SIGUSR2"; // 31 user defined signal 2 + default: + break; + } + return NULL; +} + +#endif + +#ifndef _WIN32 + +lldb::thread_key_t +Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) +{ + pthread_key_t key; + ::pthread_key_create (&key, callback); + return key; +} + +void* +Host::ThreadLocalStorageGet(lldb::thread_key_t key) +{ + return ::pthread_getspecific (key); +} + +void +Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) +{ + ::pthread_setspecific (key, value); +} + +#endif + +#if !defined (__APPLE__) // see Host.mm + +bool +Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle) +{ + bundle.Clear(); + return false; +} + +bool +Host::ResolveExecutableInBundle (FileSpec &file) +{ + return false; +} +#endif + +#ifndef _WIN32 + +FileSpec +Host::GetModuleFileSpecForHostAddress (const void *host_addr) +{ + FileSpec module_filespec; +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) + Dl_info info; + if (::dladdr (host_addr, &info)) + { + if (info.dli_fname) + module_filespec.SetFile(info.dli_fname, true); + } +#endif + return module_filespec; +} + +#endif + +#if !defined(__linux__) +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + return false; +} +#endif + +struct ShellInfo +{ + ShellInfo () : + process_reaped (false), + can_delete (false), + pid (LLDB_INVALID_PROCESS_ID), + signo(-1), + status(-1) + { + } + + lldb_private::Predicate<bool> process_reaped; + lldb_private::Predicate<bool> can_delete; + lldb::pid_t pid; + int signo; + int status; +}; + +static bool +MonitorShellCommand (void *callback_baton, + lldb::pid_t pid, + bool exited, // True if the process did exit + int signo, // Zero for no signal + int status) // Exit value of process if signal is zero +{ + ShellInfo *shell_info = (ShellInfo *)callback_baton; + shell_info->pid = pid; + shell_info->signo = signo; + shell_info->status = status; + // Let the thread running Host::RunShellCommand() know that the process + // exited and that ShellInfo has been filled in by broadcasting to it + shell_info->process_reaped.SetValue(1, eBroadcastAlways); + // Now wait for a handshake back from that thread running Host::RunShellCommand + // so we know that we can delete shell_info_ptr + shell_info->can_delete.WaitForValueEqualTo(true); + // Sleep a bit to allow the shell_info->can_delete.SetValue() to complete... + usleep(1000); + // Now delete the shell info that was passed into this function + delete shell_info; + return true; +} + +Error +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; + launch_info.SetArchitecture(HostInfo::GetArchitecture()); + if (run_in_default_shell) + { + // Run the command in a shell + launch_info.SetShell(HostInfo::GetDefaultShell()); + launch_info.GetArguments().AppendArguments(args); + const bool localhost = true; + const bool will_debug = false; + const bool first_arg_is_full_shell_command = false; + launch_info.ConvertArgumentsForLaunchingInShell (error, + localhost, + will_debug, + first_arg_is_full_shell_command, + 0); + } + else + { + // No shell, just run it + const bool first_arg_is_executable = true; + launch_info.SetArguments(args, first_arg_is_executable); + } + + 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 + // output of the command into this file. We will later read this file + // if all goes well and fill the data into "command_output_ptr" + FileSpec tmpdir_file_spec; + if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) + { + tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%"); + llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), output_file_path); + } + else + { + 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_spec) + { + launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false, true); + launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); + } + else + { + launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true); + launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true); + } + + // The process monitor callback will delete the 'shell_info_ptr' below... + std::unique_ptr<ShellInfo> shell_info_ap (new ShellInfo()); + + const bool monitor_signals = false; + launch_info.SetMonitorProcessCallback(MonitorShellCommand, shell_info_ap.get(), monitor_signals); + + 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 + // "shell_info" to the MonitorShellCommand callback function that will + // get called when the process dies. We release the unique pointer as it + // doesn't need to delete the ShellInfo anymore. + ShellInfo *shell_info = shell_info_ap.release(); + TimeValue *timeout_ptr = nullptr; + TimeValue timeout_time(TimeValue::Now()); + if (timeout_sec > 0) { + timeout_time.OffsetWithSeconds(timeout_sec); + timeout_ptr = &timeout_time; + } + bool timed_out = false; + shell_info->process_reaped.WaitForValueEqualTo(true, timeout_ptr, &timed_out); + 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 + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(1); + timed_out = false; + shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out); + } + else + { + if (status_ptr) + *status_ptr = shell_info->status; + + if (signo_ptr) + *signo_ptr = shell_info->signo; + + if (command_output_ptr) + { + command_output_ptr->clear(); + uint64_t file_size = output_file_spec.GetByteSize(); + if (file_size > 0) + { + if (file_size > command_output_ptr->max_size()) + { + error.SetErrorStringWithFormat("shell command output is too large to fit into a std::string"); + } + else + { + 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); + } + } + } + } + shell_info->can_delete.SetValue(true, eBroadcastAlways); + } + + if (FileSystem::GetFileExists(output_file_spec)) + 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) +{ + short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + +#if defined (__APPLE__) + if (launch_info.GetFlags().Test (eLaunchFlagExec)) + flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag + + if (launch_info.GetFlags().Test (eLaunchFlagDebug)) + flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag + + if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) + flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag + + if (launch_info.GetLaunchInSeparateProcessGroup()) + flags |= POSIX_SPAWN_SETPGROUP; + +#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT +#if defined (__APPLE__) && (defined (__x86_64__) || defined (__i386__)) + static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; + if (g_use_close_on_exec_flag == eLazyBoolCalculate) + { + g_use_close_on_exec_flag = eLazyBoolNo; + + uint32_t major, minor, update; + if (HostInfo::GetOSVersion(major, minor, update)) + { + // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or earlier + if (major > 10 || (major == 10 && minor > 7)) + { + // Only enable for 10.8 and later OS versions + g_use_close_on_exec_flag = eLazyBoolYes; + } + } + } +#else + static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; +#endif + // Close all files exception those with file actions if this is supported. + if (g_use_close_on_exec_flag == eLazyBoolYes) + flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; +#endif +#endif // #if defined (__APPLE__) + return flags; +} + +Error +Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid) +{ + Error error; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + posix_spawnattr_t attr; + error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); + + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_init ( &attr )"); + if (error.Fail()) + return error; + + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp <posix_spawnattr_t *, int> posix_spawnattr_cleanup(&attr, posix_spawnattr_destroy); + + sigset_t no_signals; + sigset_t all_signals; + sigemptyset (&no_signals); + sigfillset (&all_signals); + ::posix_spawnattr_setsigmask(&attr, &no_signals); +#if defined (__linux__) || defined (__FreeBSD__) + ::posix_spawnattr_setsigdefault(&attr, &no_signals); +#else + ::posix_spawnattr_setsigdefault(&attr, &all_signals); +#endif + + short flags = GetPosixspawnFlags(launch_info); + + error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags); + if (error.Fail()) + return error; + + // posix_spawnattr_setbinpref_np appears to be an Apple extension per: + // http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/ +#if defined (__APPLE__) && !defined (__arm__) + + // Don't set the binpref if a shell was provided. After all, that's only going to affect what version of the shell + // is launched, not what fork of the binary is launched. We insert "arch --arch <ARCH> as part of the shell invocation + // to do that job on OSX. + + if (launch_info.GetShell() == nullptr) + { + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + cpu_type_t cpu = arch_spec.GetMachOCPUType(); + cpu_type_t sub = arch_spec.GetMachOCPUSubType(); + if (cpu != 0 && + cpu != static_cast<cpu_type_t>(UINT32_MAX) && + cpu != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE) && + !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try to set the CPU type or we will fail + { + size_t ocount = 0; + error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu, (uint64_t)ocount); + + if (error.Fail() || ocount != 1) + return error; + } + } + +#endif + + const char *tmp_argv[2]; + char * const *argv = const_cast<char * const*>(launch_info.GetArguments().GetConstArgumentVector()); + char * const *envp = const_cast<char * const*>(launch_info.GetEnvironmentEntries().GetConstArgumentVector()); + if (argv == NULL) + { + // posix_spawn gets very unhappy if it doesn't have at least the program + // name in argv[0]. One of the side affects I have noticed is the environment + // variables don't make it into the child process if "argv == NULL"!!! + tmp_argv[0] = exe_path; + tmp_argv[1] = NULL; + argv = const_cast<char * const*>(tmp_argv); + } + +#if !defined (__APPLE__) + // manage the working directory + char current_dir[PATH_MAX]; + current_dir[0] = '\0'; +#endif + + 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.GetCString()) < 0) { + if (errno == ENOENT) { + 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.GetCString()); + } else { + error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution."); + } + return error; + } +#else + if (::getcwd(current_dir, sizeof(current_dir)) == NULL) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log, "unable to save the current directory"); + return error; + } + + if (::chdir(working_dir.GetCString()) == -1) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log, "unable to change working directory to %s", + working_dir.GetCString()); + return error; + } +#endif + } + + ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; + const size_t num_file_actions = launch_info.GetNumFileActions (); + if (num_file_actions > 0) + { + posix_spawn_file_actions_t file_actions; + error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )"); + if (error.Fail()) + return error; + + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> posix_spawn_file_actions_cleanup (&file_actions, posix_spawn_file_actions_destroy); + + for (size_t i=0; i<num_file_actions; ++i) + { + const FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); + if (launch_file_action) + { + if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, error)) + return error; + } + } + + error.SetError(::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), eErrorTypePOSIX); + + if (error.Fail() || log) + { + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", result_pid, + exe_path, static_cast<void *>(&file_actions), static_cast<void *>(&attr), reinterpret_cast<const void *>(argv), + reinterpret_cast<const void *>(envp)); + if (log) + { + for (int ii=0; argv[ii]; ++ii) + log->Printf("argv[%i] = '%s'", ii, argv[ii]); + } + } + + } + else + { + error.SetError(::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), eErrorTypePOSIX); + + if (error.Fail() || log) + { + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )", + result_pid, exe_path, static_cast<void *>(&attr), reinterpret_cast<const void *>(argv), + reinterpret_cast<const void *>(envp)); + if (log) + { + for (int ii=0; argv[ii]; ++ii) + log->Printf("argv[%i] = '%s'", ii, argv[ii]); + } + } + } + pid = result_pid; + + if (working_dir) + { +#if defined (__APPLE__) + // No more thread specific current working directory + __pthread_fchdir (-1); +#else + if (::chdir(current_dir) == -1 && error.Success()) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log, "unable to change current directory back to %s", + current_dir); + } +#endif + } + + return error; +} + +bool +Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Error &error) +{ + if (info == NULL) + return false; + + posix_spawn_file_actions_t *file_actions = reinterpret_cast<posix_spawn_file_actions_t *>(_file_actions); + + switch (info->GetAction()) + { + case FileAction::eFileActionNone: + error.Clear(); + break; + + case FileAction::eFileActionClose: + if (info->GetFD() == -1) + error.SetErrorString("invalid fd for posix_spawn_file_actions_addclose(...)"); + else + { + error.SetError(::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), eErrorTypePOSIX); + if (log && (error.Fail() || log)) + error.PutToLog(log, "posix_spawn_file_actions_addclose (action=%p, fd=%i)", + static_cast<void *>(file_actions), info->GetFD()); + } + break; + + case FileAction::eFileActionDuplicate: + if (info->GetFD() == -1) + error.SetErrorString("invalid fd for posix_spawn_file_actions_adddup2(...)"); + else if (info->GetActionArgument() == -1) + error.SetErrorString("invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); + else + { + error.SetError( + ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), info->GetActionArgument()), + eErrorTypePOSIX); + if (log && (error.Fail() || log)) + error.PutToLog(log, "posix_spawn_file_actions_adddup2 (action=%p, fd=%i, dup_fd=%i)", + static_cast<void *>(file_actions), info->GetFD(), info->GetActionArgument()); + } + break; + + case FileAction::eFileActionOpen: + if (info->GetFD() == -1) + error.SetErrorString("invalid fd in posix_spawn_file_actions_addopen(...)"); + else + { + int oflag = info->GetActionArgument(); + + mode_t mode = 0; + + if (oflag & O_CREAT) + mode = 0640; + + error.SetError( + ::posix_spawn_file_actions_addopen(file_actions, info->GetFD(), info->GetPath(), oflag, mode), + eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, + "posix_spawn_file_actions_addopen (action=%p, fd=%i, path='%s', oflag=%i, mode=%i)", + static_cast<void *>(file_actions), info->GetFD(), info->GetPath(), oflag, mode); + } + break; + } + return error.Success(); +} +#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, +// FreeBSD and NetBSD. + +Error +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 + MonitoringProcessLauncher launcher(std::move(delegate_launcher)); + + Error error; + HostProcess process = launcher.LaunchProcess(launch_info, error); + + // TODO(zturner): It would be better if the entire HostProcess were returned instead of writing + // it into this structure. + launch_info.SetProcessID(process.GetProcessId()); + + return error; +} +#endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) + +#ifndef _WIN32 +void +Host::Kill(lldb::pid_t pid, int signo) +{ + ::kill(pid, signo); +} + +#endif + +#if !defined (__APPLE__) +bool +Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) +{ + return false; +} + +void +Host::SetCrashDescriptionWithFormat (const char *format, ...) +{ +} + +void +Host::SetCrashDescription (const char *description) +{ +} + +#endif + +const UnixSignalsSP & +Host::GetUnixSignals() +{ + static const auto s_unix_signals_sp = UnixSignals::Create(HostInfo::GetArchitecture()); + return s_unix_signals_sp; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/HostInfoBase.cpp b/contrib/llvm/tools/lldb/source/Host/common/HostInfoBase.cpp new file mode 100644 index 0000000..f7ba755 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/HostInfoBase.cpp @@ -0,0 +1,425 @@ +//===-- HostInfoBase.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/Config.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostInfoBase.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#include <thread> +#include <mutex> // std::once + +using namespace lldb; +using namespace lldb_private; + +namespace +{ + void + CleanupProcessSpecificLLDBTempDir() + { + // Get the process specific LLDB temporary directory and delete it. + FileSpec tmpdir_file_spec; + if (!HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) + return; + + // 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, true); + } + + //---------------------------------------------------------------------- + // The HostInfoBaseFields is a work around for windows not supporting + // static variables correctly in a thread safe way. Really each of the + // variables in HostInfoBaseFields should live in the functions in which + // they are used and each one should be static, but the work around is + // in place to avoid this restriction. Ick. + //---------------------------------------------------------------------- + + struct HostInfoBaseFields + { + uint32_t m_number_cpus; + std::string m_vendor_string; + std::string m_os_string; + std::string m_host_triple; + + ArchSpec m_host_arch_32; + ArchSpec m_host_arch_64; + + FileSpec m_lldb_so_dir; + FileSpec m_lldb_support_exe_dir; + FileSpec m_lldb_headers_dir; + FileSpec m_lldb_python_dir; + FileSpec m_lldb_clang_resource_dir; + FileSpec m_lldb_system_plugin_dir; + FileSpec m_lldb_user_plugin_dir; + FileSpec m_lldb_process_tmp_dir; + FileSpec m_lldb_global_tmp_dir; + }; + + HostInfoBaseFields *g_fields = nullptr; +} + +void +HostInfoBase::Initialize() +{ + g_fields = new HostInfoBaseFields(); +} + +uint32_t +HostInfoBase::GetNumberCPUS() +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + g_fields->m_number_cpus = std::thread::hardware_concurrency(); + }); + return g_fields->m_number_cpus; +} + +uint32_t +HostInfoBase::GetMaxThreadNameLength() +{ + return 0; +} + +llvm::StringRef +HostInfoBase::GetVendorString() +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + g_fields->m_vendor_string = HostInfo::GetArchitecture().GetTriple().getVendorName().str(); + }); + return g_fields->m_vendor_string; +} + +llvm::StringRef +HostInfoBase::GetOSString() +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + g_fields->m_os_string = std::move(HostInfo::GetArchitecture().GetTriple().getOSName()); + }); + return g_fields->m_os_string; +} + +llvm::StringRef +HostInfoBase::GetTargetTriple() +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + g_fields->m_host_triple = HostInfo::GetArchitecture().GetTriple().getTriple(); + }); + return g_fields->m_host_triple; +} + +const ArchSpec & +HostInfoBase::GetArchitecture(ArchitectureKind arch_kind) +{ + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + HostInfo::ComputeHostArchitectureSupport(g_fields->m_host_arch_32, g_fields->m_host_arch_64); + }); + + // If an explicit 32 or 64-bit architecture was requested, return that. + if (arch_kind == eArchKind32) + return g_fields->m_host_arch_32; + if (arch_kind == eArchKind64) + return g_fields->m_host_arch_64; + + // Otherwise prefer the 64-bit architecture if it is valid. + return (g_fields->m_host_arch_64.IsValid()) ? g_fields->m_host_arch_64 : g_fields->m_host_arch_32; +} + +bool +HostInfoBase::GetLLDBPath(lldb::PathType type, FileSpec &file_spec) +{ + file_spec.Clear(); + +#if defined(LLDB_DISABLE_PYTHON) + if (type == lldb::ePathTypePythonDir) + return false; +#endif + + FileSpec *result = nullptr; + switch (type) + { + case lldb::ePathTypeLLDBShlibDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeSharedLibraryDirectory (g_fields->m_lldb_so_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBShlibDir) => '%s'", g_fields->m_lldb_so_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_so_dir; + } + break; + case lldb::ePathTypeSupportExecutableDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeSupportExeDirectory (g_fields->m_lldb_support_exe_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeSupportExecutableDir) => '%s'", + g_fields->m_lldb_support_exe_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_support_exe_dir; + } + break; + case lldb::ePathTypeHeaderDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeHeaderDirectory (g_fields->m_lldb_headers_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeHeaderDir) => '%s'", g_fields->m_lldb_headers_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_headers_dir; + } + break; + case lldb::ePathTypePythonDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputePythonDirectory (g_fields->m_lldb_python_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypePythonDir) => '%s'", g_fields->m_lldb_python_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_python_dir; + } + break; + case lldb::ePathTypeClangDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeClangDirectory (g_fields->m_lldb_clang_resource_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeClangResourceDir) => '%s'", g_fields->m_lldb_clang_resource_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_clang_resource_dir; + } + break; + case lldb::ePathTypeLLDBSystemPlugins: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeSystemPluginsDirectory (g_fields->m_lldb_system_plugin_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBSystemPlugins) => '%s'", + g_fields->m_lldb_system_plugin_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_system_plugin_dir; + } + break; + case lldb::ePathTypeLLDBUserPlugins: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + success = HostInfo::ComputeUserPluginsDirectory (g_fields->m_lldb_user_plugin_dir); + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBUserPlugins) => '%s'", + g_fields->m_lldb_user_plugin_dir.GetPath().c_str()); + }); + if (success) + result = &g_fields->m_lldb_user_plugin_dir; + } + break; + case lldb::ePathTypeLLDBTempSystemDir: + { + static std::once_flag g_once_flag; + static bool success = false; + std::call_once(g_once_flag, []() { + 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_process_tmp_dir.GetPath().c_str()); + }); + if (success) + 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; + } + + if (!result) + return false; + file_spec = *result; + return true; +} + +bool +HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) +{ + // To get paths related to LLDB we get the path to the executable that + // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB", + // on linux this is assumed to be the "lldb" main executable. If LLDB on + // linux is actually in a shared library (liblldb.so) then this function will + // need to be modified to "do the right thing". + + FileSpec lldb_file_spec( + Host::GetModuleFileSpecForHostAddress(reinterpret_cast<void *>(reinterpret_cast<intptr_t>(HostInfoBase::GetLLDBPath)))); + + // This is necessary because when running the testsuite the shlib might be a symbolic link inside the Python resource dir. + FileSystem::ResolveSymbolicLink(lldb_file_spec, lldb_file_spec); + + // Remove the filename so that this FileSpec only represents the directory. + file_spec.GetDirectory() = lldb_file_spec.GetDirectory(); + + return (bool)file_spec.GetDirectory(); +} + +bool +HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + return GetLLDBPath(lldb::ePathTypeLLDBShlibDir, file_spec); +} + +bool +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) +{ + llvm::SmallVector<char, 16> tmpdir; + llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir); + file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()), true); + return true; +} + +bool +HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) +{ + file_spec.Clear(); + + FileSpec temp_file_spec; + if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec)) + return false; + + temp_file_spec.AppendPathComponent("lldb"); + if (!FileSystem::MakeDirectory(temp_file_spec, eFilePermissionsDirectoryDefault).Success()) + return false; + + file_spec.GetDirectory().SetCString(temp_file_spec.GetCString()); + return true; +} + +bool +HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) +{ + // TODO(zturner): Figure out how to compute the header directory for all platforms. + return false; +} + +bool +HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ + // TODO(zturner): Figure out how to compute the system plugins directory for all platforms. + return false; +} + +bool +HostInfoBase::ComputeClangDirectory(FileSpec &file_spec) +{ + return false; +} + +bool +HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ + // TODO(zturner): Figure out how to compute the user plugins directory for all platforms. + return false; +} + +void +HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ + llvm::Triple triple(llvm::sys::getProcessTriple()); + + arch_32.Clear(); + arch_64.Clear(); + + switch (triple.getArch()) + { + default: + arch_32.SetTriple(triple); + break; + + case llvm::Triple::aarch64: + case llvm::Triple::ppc64: + case llvm::Triple::x86_64: + arch_64.SetTriple(triple); + arch_32.SetTriple(triple.get32BitArchVariant()); + break; + + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + case llvm::Triple::sparcv9: + arch_64.SetTriple(triple); + break; + } +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/HostNativeThreadBase.cpp b/contrib/llvm/tools/lldb/source/Host/common/HostNativeThreadBase.cpp new file mode 100644 index 0000000..9fea54d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/HostNativeThreadBase.cpp @@ -0,0 +1,82 @@ +//===-- HostNativeThreadBase.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/Core/Log.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "llvm/ADT/StringExtras.h" + +using namespace lldb; +using namespace lldb_private; + +HostNativeThreadBase::HostNativeThreadBase() + : m_thread(LLDB_INVALID_HOST_THREAD) + , m_result(0) +{ +} + +HostNativeThreadBase::HostNativeThreadBase(thread_t thread) + : m_thread(thread) + , m_result(0) +{ +} + +lldb::thread_t +HostNativeThreadBase::GetSystemHandle() const +{ + return m_thread; +} + +lldb::thread_result_t +HostNativeThreadBase::GetResult() const +{ + return m_result; +} + +bool +HostNativeThreadBase::IsJoinable() const +{ + return m_thread != LLDB_INVALID_HOST_THREAD; +} + +void +HostNativeThreadBase::Reset() +{ + m_thread = LLDB_INVALID_HOST_THREAD; + m_result = 0; +} + +lldb::thread_t +HostNativeThreadBase::Release() +{ + lldb::thread_t result = m_thread; + m_thread = LLDB_INVALID_HOST_THREAD; + m_result = 0; + + return result; +} + +lldb::thread_result_t +HostNativeThreadBase::ThreadCreateTrampoline(lldb::thread_arg_t arg) +{ + ThreadLauncher::HostThreadCreateInfo *info = (ThreadLauncher::HostThreadCreateInfo *)arg; + ThisThread::SetName(info->thread_name.c_str(), HostInfo::GetMaxThreadNameLength()); + + thread_func_t thread_fptr = info->thread_fptr; + thread_arg_t thread_arg = info->thread_arg; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("thread created"); + + delete info; + return thread_fptr(thread_arg); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/HostProcess.cpp b/contrib/llvm/tools/lldb/source/Host/common/HostProcess.cpp new file mode 100644 index 0000000..58a2146 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/HostProcess.cpp @@ -0,0 +1,65 @@ +//===-- HostProcess.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/HostNativeProcess.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostThread.h" + +using namespace lldb; +using namespace lldb_private; + +HostProcess::HostProcess() + : m_native_process(new HostNativeProcess) +{ +} + +HostProcess::HostProcess(lldb::process_t process) + : m_native_process(new HostNativeProcess(process)) +{ +} + +HostProcess::~HostProcess() +{ +} + +Error HostProcess::Terminate() +{ + return m_native_process->Terminate(); +} + +Error HostProcess::GetMainModule(FileSpec &file_spec) const +{ + return m_native_process->GetMainModule(file_spec); +} + +lldb::pid_t HostProcess::GetProcessId() const +{ + return m_native_process->GetProcessId(); +} + +bool HostProcess::IsRunning() const +{ + return m_native_process->IsRunning(); +} + +HostThread +HostProcess::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +{ + return m_native_process->StartMonitoring(callback, callback_baton, monitor_signals); +} + +HostNativeProcessBase &HostProcess::GetNativeProcess() +{ + return *m_native_process; +} + +const HostNativeProcessBase &HostProcess::GetNativeProcess() const +{ + return *m_native_process; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/HostThread.cpp b/contrib/llvm/tools/lldb/source/Host/common/HostThread.cpp new file mode 100644 index 0000000..7757477 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/HostThread.cpp @@ -0,0 +1,78 @@ +//===-- HostThread.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/HostNativeThread.h" +#include "lldb/Host/HostThread.h" + +using namespace lldb; +using namespace lldb_private; + +HostThread::HostThread() + : m_native_thread(new HostNativeThread) +{ +} + +HostThread::HostThread(lldb::thread_t thread) + : m_native_thread(new HostNativeThread(thread)) +{ +} + +Error +HostThread::Join(lldb::thread_result_t *result) +{ + return m_native_thread->Join(result); +} + +Error +HostThread::Cancel() +{ + return m_native_thread->Cancel(); +} + +void +HostThread::Reset() +{ + return m_native_thread->Reset(); +} + +lldb::thread_t +HostThread::Release() +{ + return m_native_thread->Release(); +} + +bool +HostThread::IsJoinable() const +{ + return m_native_thread->IsJoinable(); +} + +HostNativeThread & +HostThread::GetNativeThread() +{ + return static_cast<HostNativeThread &>(*m_native_thread); +} + +const HostNativeThread & +HostThread::GetNativeThread() const +{ + return static_cast<const HostNativeThread &>(*m_native_thread); +} + +lldb::thread_result_t +HostThread::GetResult() const +{ + return m_native_thread->GetResult(); +} + +bool +HostThread::EqualsThread(lldb::thread_t thread) const +{ + return m_native_thread->GetSystemHandle() == thread; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/IOObject.cpp b/contrib/llvm/tools/lldb/source/Host/common/IOObject.cpp new file mode 100644 index 0000000..6f7de44 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/IOObject.cpp @@ -0,0 +1,14 @@ +//===-- IOObject.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/IOObject.h" + +using namespace lldb_private; + +const IOObject::WaitableHandle IOObject::kInvalidHandleValue = -1; diff --git a/contrib/llvm/tools/lldb/source/Host/common/LockFileBase.cpp b/contrib/llvm/tools/lldb/source/Host/common/LockFileBase.cpp new file mode 100644 index 0000000..f746945 --- /dev/null +++ b/contrib/llvm/tools/lldb/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/contrib/llvm/tools/lldb/source/Host/common/MonitoringProcessLauncher.cpp b/contrib/llvm/tools/lldb/source/Host/common/MonitoringProcessLauncher.cpp new file mode 100644 index 0000000..0fad44a --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/MonitoringProcessLauncher.cpp @@ -0,0 +1,102 @@ +//===-- ProcessLauncherWindows.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/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +using namespace lldb; +using namespace lldb_private; + +MonitoringProcessLauncher::MonitoringProcessLauncher(std::unique_ptr<ProcessLauncher> delegate_launcher) + : m_delegate_launcher(std::move(delegate_launcher)) +{ +} + +HostProcess +MonitoringProcessLauncher::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + ProcessLaunchInfo resolved_info(launch_info); + + error.Clear(); + char exe_path[PATH_MAX]; + + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + + const ArchSpec &arch_spec = resolved_info.GetArchitecture(); + + FileSpec exe_spec(resolved_info.GetExecutableFile()); + + FileSpec::FileType file_type = exe_spec.GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) + { + ModuleSpec module_spec(exe_spec, arch_spec); + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable(module_spec, exe_module_sp, NULL); + + if (error.Fail()) + return HostProcess(); + + if (exe_module_sp) + exe_spec = exe_module_sp->GetFileSpec(); + } + + if (exe_spec.Exists()) + { + exe_spec.GetPath(exe_path, sizeof(exe_path)); + } + else + { + resolved_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("executable doesn't exist: '%s'", exe_path); + return HostProcess(); + } + + resolved_info.SetExecutableFile(exe_spec, false); + assert(!resolved_info.GetFlags().Test(eLaunchFlagLaunchInTTY)); + + HostProcess process = m_delegate_launcher->LaunchProcess(resolved_info, error); + + if (process.GetProcessId() != LLDB_INVALID_PROCESS_ID) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + Host::MonitorChildProcessCallback callback = launch_info.GetMonitorProcessCallback(); + + void *baton = nullptr; + bool monitor_signals = false; + if (callback) + { + // If the ProcessLaunchInfo specified a callback, use that. + baton = launch_info.GetMonitorProcessBaton(); + monitor_signals = launch_info.GetMonitorSignals(); + } + else + { + callback = Process::SetProcessExitStatus; + } + + process.StartMonitoring(callback, baton, monitor_signals); + if (log) + log->PutCString("started monitoring child process."); + } + else + { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString("process launch failed for unknown reasons"); + } + return process; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Mutex.cpp b/contrib/llvm/tools/lldb/source/Host/common/Mutex.cpp new file mode 100644 index 0000000..98f5321 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Mutex.cpp @@ -0,0 +1,398 @@ +//===-- Mutex.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/Mutex.h" +#include "lldb/Host/Host.h" + +#ifndef _WIN32 +#include <pthread.h> +#endif +#include <string.h> +#include <stdio.h> + +#if 0 +// This logging is way too verbose to enable even for a log channel. +// This logging can be enabled by changing the "#if 0", but should be +// reverted prior to checking in. +#include <cstdio> +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +// Enable extra mutex error checking +#if 0 // LLDB_CONFIGURATION_DEBUG +#define ENABLE_MUTEX_ERROR_CHECKING 1 +#include <inttypes.h> +#endif + +#if ENABLE_MUTEX_ERROR_CHECKING +#include <set> + +enum MutexAction +{ + eMutexActionInitialized, + eMutexActionDestroyed, + eMutexActionAssertInitialized +}; + +static bool +error_check_mutex (pthread_mutex_t *m, MutexAction action) +{ + typedef std::set<pthread_mutex_t *> mutex_set; + static pthread_mutex_t g_mutex_set_mutex = PTHREAD_MUTEX_INITIALIZER; + static mutex_set g_initialized_mutex_set; + static mutex_set g_destroyed_mutex_set; + + bool success = true; + int err; + // Manually call lock so we don't to any of this error checking + err = ::pthread_mutex_lock (&g_mutex_set_mutex); + assert(err == 0); + switch (action) + { + case eMutexActionInitialized: + // Make sure this isn't already in our initialized mutex set... + assert (g_initialized_mutex_set.find(m) == g_initialized_mutex_set.end()); + // Remove this from the destroyed set in case it was ever in there + g_destroyed_mutex_set.erase(m); + // Add the mutex to the initialized set + g_initialized_mutex_set.insert(m); + break; + + case eMutexActionDestroyed: + // Make sure this isn't already in our destroyed mutex set... + assert (g_destroyed_mutex_set.find(m) == g_destroyed_mutex_set.end()); + // Remove this from the initialized so we can put it into the destroyed set + g_initialized_mutex_set.erase(m); + // Add the mutex to the destroyed set + g_destroyed_mutex_set.insert(m); + break; + case eMutexActionAssertInitialized: + // This function will return true if "m" is in the initialized mutex set + success = g_initialized_mutex_set.find(m) != g_initialized_mutex_set.end(); + assert (success); + break; + } + // Manually call unlock so we don't to any of this error checking + err = ::pthread_mutex_unlock (&g_mutex_set_mutex); + assert(err == 0); + return success; +} + +#endif + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor. +// +// This will create a scoped mutex locking object that doesn't have +// a mutex to lock. One will need to be provided using the Reset() +// method. +//---------------------------------------------------------------------- +Mutex::Locker::Locker () : + m_mutex_ptr(NULL) +{ +} + +//---------------------------------------------------------------------- +// Constructor with a Mutex object. +// +// This will create a scoped mutex locking object that extracts the +// mutex owned by "m" and locks it. +//---------------------------------------------------------------------- +Mutex::Locker::Locker (Mutex& m) : + m_mutex_ptr(NULL) +{ + Lock (m); +} + +//---------------------------------------------------------------------- +// Constructor with a Mutex object pointer. +// +// This will create a scoped mutex locking object that extracts the +// mutex owned by "m" and locks it. +//---------------------------------------------------------------------- +Mutex::Locker::Locker (Mutex* m) : + m_mutex_ptr(NULL) +{ + if (m) + Lock (m); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Unlocks any owned mutex object (if it is valid). +//---------------------------------------------------------------------- +Mutex::Locker::~Locker () +{ + Unlock(); +} + +//---------------------------------------------------------------------- +// Unlock the current mutex in this object (if this owns a valid +// mutex) and lock the new "mutex" object if it is non-NULL. +//---------------------------------------------------------------------- +void +Mutex::Locker::Lock (Mutex &mutex) +{ + // We already have this mutex locked or both are NULL... + if (m_mutex_ptr == &mutex) + return; + + Unlock (); + + m_mutex_ptr = &mutex; + m_mutex_ptr->Lock(); +} + +void +Mutex::Locker::Unlock () +{ + if (m_mutex_ptr) + { + m_mutex_ptr->Unlock (); + m_mutex_ptr = NULL; + } +} + +bool +Mutex::Locker::TryLock (Mutex &mutex, const char *failure_message) +{ + // We already have this mutex locked! + if (m_mutex_ptr == &mutex) + return true; + + Unlock (); + + if (mutex.TryLock(failure_message) == 0) + m_mutex_ptr = &mutex; + + return m_mutex_ptr != NULL; +} + +#ifndef _WIN32 + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with no attributes. +//---------------------------------------------------------------------- +Mutex::Mutex () : + m_mutex() +{ + int err; + err = ::pthread_mutex_init (&m_mutex, NULL); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionInitialized); +#endif + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with "type" as the mutex type. +//---------------------------------------------------------------------- +Mutex::Mutex (Mutex::Type type) : + m_mutex() +{ + int err; + ::pthread_mutexattr_t attr; + err = ::pthread_mutexattr_init (&attr); + assert(err == 0); + switch (type) + { + case eMutexTypeNormal: +#if ENABLE_MUTEX_ERROR_CHECKING + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK); +#else + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL); +#endif + break; + + case eMutexTypeRecursive: + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + break; + } + assert(err == 0); + err = ::pthread_mutex_init (&m_mutex, &attr); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionInitialized); +#endif + assert(err == 0); + err = ::pthread_mutexattr_destroy (&attr); + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Destructor. +// +// Destroys the mutex owned by this object. +//---------------------------------------------------------------------- +Mutex::~Mutex() +{ +#if ENABLE_MUTEX_ERROR_CHECKING + int err = ::pthread_mutex_destroy (&m_mutex); + assert(err == 0); + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionDestroyed); + else + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_destroy() => err = %i (%s)", __PRETTY_FUNCTION__, err, strerror(err)); + assert(err == 0); + } + memset (&m_mutex, '\xba', sizeof(m_mutex)); +#else + ::pthread_mutex_destroy (&m_mutex); +#endif +} + +//---------------------------------------------------------------------- +// Locks the mutex owned by this object, if the mutex is already +// locked, the calling thread will block until the mutex becomes +// available. +// +// RETURNS +// The error code from the pthread_mutex_lock() function call. +//---------------------------------------------------------------------- +int +Mutex::Lock() +{ + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex); + +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_lock (&m_mutex); + + +#if ENABLE_MUTEX_ERROR_CHECKING + if (err) + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_lock(%p) => err = %i (%s)", __PRETTY_FUNCTION__, &m_mutex, err, strerror(err)); + assert(err == 0); + } +#endif + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +//---------------------------------------------------------------------- +// Attempts to lock the mutex owned by this object without blocking. +// If the mutex is already locked, TryLock() will not block waiting +// for the mutex, but will return an error condition. +// +// RETURNS +// The error code from the pthread_mutex_trylock() function call. +//---------------------------------------------------------------------- +int +Mutex::TryLock(const char *failure_message) +{ +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_trylock (&m_mutex); + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_trylock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +//---------------------------------------------------------------------- +// If the current thread holds the lock on the owned mutex, then +// Unlock() will unlock the mutex. Calling Unlock() on this object +// that the calling thread does not hold will result in undefined +// behavior. +// +// RETURNS +// The error code from the pthread_mutex_unlock() function call. +//---------------------------------------------------------------------- +int +Mutex::Unlock() +{ +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_unlock (&m_mutex); + +#if ENABLE_MUTEX_ERROR_CHECKING + if (err) + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_unlock(%p) => err = %i (%s)", __PRETTY_FUNCTION__, &m_mutex, err, strerror(err)); + assert(err == 0); + } +#endif + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_unlock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +#endif + +//---------------------------------------------------------------------- +// Mutex get accessor. +//---------------------------------------------------------------------- +lldb::mutex_t * +Mutex::GetMutex() +{ + return &m_mutex; +} + +#ifdef LLDB_CONFIGURATION_DEBUG +int +TrackingMutex::Unlock () +{ + if (!m_failure_message.empty()) + Host::SetCrashDescriptionWithFormat ("Unlocking lock (on thread %p) that thread: %p failed to get: %s", + pthread_self(), + m_thread_that_tried, + m_failure_message.c_str()); + assert (m_failure_message.empty()); + return Mutex::Unlock(); +} + +int +LoggingMutex::Lock () +{ + printf("locking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::Lock(); + m_locked = true; + printf("%d\n",x); + return x; +} + +int +LoggingMutex::Unlock () +{ + printf("unlocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::Unlock(); + m_locked = false; + printf("%d\n",x); + return x; +} + +int +LoggingMutex::TryLock (const char *failure_message) +{ + printf("trylocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::TryLock(failure_message); + if (x == 0) + m_locked = true; + printf("%d\n",x); + return x; +} + +#endif + + diff --git a/contrib/llvm/tools/lldb/source/Host/common/NativeBreakpoint.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeBreakpoint.cpp new file mode 100644 index 0000000..622b4ea --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeBreakpoint.cpp @@ -0,0 +1,116 @@ +//===-- NativeBreakpoint.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/common/NativeBreakpoint.h" + +#include "lldb/lldb-defines.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +NativeBreakpoint::NativeBreakpoint (lldb::addr_t addr) : + m_addr (addr), + m_ref_count (1), + m_enabled (true) +{ + assert (addr != LLDB_INVALID_ADDRESS && "breakpoint set for invalid address"); +} + +NativeBreakpoint::~NativeBreakpoint () +{ +} + +void +NativeBreakpoint::AddRef () +{ + ++m_ref_count; + + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " bumped up, new ref count %" PRIu32, __FUNCTION__, m_addr, m_ref_count); +} + +int32_t +NativeBreakpoint::DecRef () +{ + --m_ref_count; + + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " ref count decremented, new ref count %" PRIu32, __FUNCTION__, m_addr, m_ref_count); + + return m_ref_count; +} + +Error +NativeBreakpoint::Enable () +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (m_enabled) + { + // We're already enabled. Just log and exit. + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " already enabled, ignoring.", __FUNCTION__, m_addr); + return Error (); + } + + // Log and enable. + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " enabling...", __FUNCTION__, m_addr); + + Error error = DoEnable (); + if (error.Success ()) + { + m_enabled = true; + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " enable SUCCESS.", __FUNCTION__, m_addr); + } + else + { + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " enable FAIL: %s", __FUNCTION__, m_addr, error.AsCString ()); + } + + return error; +} + +Error +NativeBreakpoint::Disable () +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + if (!m_enabled) + { + // We're already disabled. Just log and exit. + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " already disabled, ignoring.", __FUNCTION__, m_addr); + return Error (); + } + + // Log and disable. + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " disabling...", __FUNCTION__, m_addr); + + Error error = DoDisable (); + if (error.Success ()) + { + m_enabled = false; + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " disable SUCCESS.", __FUNCTION__, m_addr); + } + else + { + if (log) + log->Printf ("NativeBreakpoint::%s addr = 0x%" PRIx64 " disable FAIL: %s", __FUNCTION__, m_addr, error.AsCString ()); + } + + return error; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/NativeBreakpointList.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeBreakpointList.cpp new file mode 100644 index 0000000..52b9baf --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeBreakpointList.cpp @@ -0,0 +1,221 @@ +//===-- NativeBreakpointList.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/common/NativeBreakpointList.h" + +#include "lldb/Core/Log.h" + +#include "lldb/Host/common/NativeBreakpoint.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" + +using namespace lldb; +using namespace lldb_private; + +NativeBreakpointList::NativeBreakpointList () : + m_mutex (Mutex::eMutexTypeRecursive) +{ +} + +Error +NativeBreakpointList::AddRef (lldb::addr_t addr, size_t size_hint, bool hardware, CreateBreakpointFunc create_func) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 ", size_hint = %lu, hardware = %s", __FUNCTION__, addr, size_hint, hardware ? "true" : "false"); + + Mutex::Locker locker (m_mutex); + + // Check if the breakpoint is already set. + auto iter = m_breakpoints.find (addr); + if (iter != m_breakpoints.end ()) + { + // Yes - bump up ref count. + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- already enabled, upping ref count", __FUNCTION__, addr); + + iter->second->AddRef (); + return Error (); + } + + // Create a new breakpoint using the given create func. + if (log) + log->Printf ("NativeBreakpointList::%s creating breakpoint for addr = 0x%" PRIx64 ", size_hint = %lu, hardware = %s", __FUNCTION__, addr, size_hint, hardware ? "true" : "false"); + + NativeBreakpointSP breakpoint_sp; + Error error = create_func (addr, size_hint, hardware, breakpoint_sp); + if (error.Fail ()) + { + if (log) + log->Printf ("NativeBreakpointList::%s creating breakpoint for addr = 0x%" PRIx64 ", size_hint = %lu, hardware = %s -- FAILED: %s", __FUNCTION__, addr, size_hint, hardware ? "true" : "false", error.AsCString ()); + return error; + } + + // Remember the breakpoint. + assert (breakpoint_sp && "NativeBreakpoint create function succeeded but returned NULL breakpoint"); + m_breakpoints.insert (BreakpointMap::value_type (addr, breakpoint_sp)); + + return error; +} + +Error +NativeBreakpointList::DecRef (lldb::addr_t addr) +{ + Error error; + + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + Mutex::Locker locker (m_mutex); + + // Check if the breakpoint is already set. + auto iter = m_breakpoints.find (addr); + if (iter == m_breakpoints.end ()) + { + // Not found! + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- NOT FOUND", __FUNCTION__, addr); + error.SetErrorString ("breakpoint not found"); + return error; + } + + // Decrement ref count. + const int32_t new_ref_count = iter->second->DecRef (); + assert (new_ref_count >= 0 && "NativeBreakpoint ref count went negative"); + + if (new_ref_count > 0) + { + // Still references to this breakpoint. Leave it alone. + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- new breakpoint ref count %" PRIu32, __FUNCTION__, addr, new_ref_count); + return error; + } + + // Breakpoint has no more references. Disable it if it's not + // already disabled. + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- removing due to no remaining references", __FUNCTION__, addr); + + // If it's enabled, we need to disable it. + if (iter->second->IsEnabled ()) + { + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- currently enabled, now disabling", __FUNCTION__, addr); + error = iter->second->Disable (); + if (error.Fail ()) + { + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- removal FAILED: %s", __FUNCTION__, addr, error.AsCString ()); + // Continue since we still want to take it out of the breakpoint list. + } + } + else + { + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- already disabled, nothing to do", __FUNCTION__, addr); + } + + // Take the breakpoint out of the list. + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- removed from breakpoint map", __FUNCTION__, addr); + + m_breakpoints.erase (iter); + return error; +} + +Error +NativeBreakpointList::EnableBreakpoint (lldb::addr_t addr) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + Mutex::Locker locker (m_mutex); + + // Ensure we have said breakpoint. + auto iter = m_breakpoints.find (addr); + if (iter == m_breakpoints.end ()) + { + // Not found! + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- NOT FOUND", __FUNCTION__, addr); + return Error ("breakpoint not found"); + } + + // Enable it. + return iter->second->Enable (); +} + +Error +NativeBreakpointList::DisableBreakpoint (lldb::addr_t addr) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + Mutex::Locker locker (m_mutex); + + // Ensure we have said breakpoint. + auto iter = m_breakpoints.find (addr); + if (iter == m_breakpoints.end ()) + { + // Not found! + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64 " -- NOT FOUND", __FUNCTION__, addr); + return Error ("breakpoint not found"); + } + + // Disable it. + return iter->second->Disable (); +} + +Error +NativeBreakpointList::GetBreakpoint (lldb::addr_t addr, NativeBreakpointSP &breakpoint_sp) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeBreakpointList::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + Mutex::Locker locker (m_mutex); + + // Ensure we have said breakpoint. + auto iter = m_breakpoints.find (addr); + if (iter == m_breakpoints.end ()) + { + // Not found! + breakpoint_sp.reset (); + return Error ("breakpoint not found"); + } + + // Disable it. + breakpoint_sp = iter->second; + 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/contrib/llvm/tools/lldb/source/Host/common/NativeProcessProtocol.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeProcessProtocol.cpp new file mode 100644 index 0000000..7d2f401 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeProcessProtocol.cpp @@ -0,0 +1,461 @@ +//===-- NativeProcessProtocol.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/common/NativeProcessProtocol.h" + +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/common/NativeRegisterContext.h" + +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" + +using namespace lldb; +using namespace lldb_private; + +// ----------------------------------------------------------------------------- +// NativeProcessProtocol Members +// ----------------------------------------------------------------------------- + +NativeProcessProtocol::NativeProcessProtocol (lldb::pid_t pid) : + m_pid (pid), + m_threads (), + m_current_thread_id (LLDB_INVALID_THREAD_ID), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_state (lldb::eStateInvalid), + m_state_mutex (Mutex::eMutexTypeRecursive), + m_exit_type (eExitTypeInvalid), + m_exit_status (0), + m_exit_description (), + m_delegates_mutex (Mutex::eMutexTypeRecursive), + m_delegates (), + m_breakpoint_list (), + m_watchpoint_list (), + m_terminal_fd (-1), + m_stop_id (0) +{ +} + +lldb_private::Error +NativeProcessProtocol::Interrupt () +{ + Error error; +#if !defined (SIGSTOP) + error.SetErrorString ("local host does not support signaling"); + return error; +#else + return Signal (SIGSTOP); +#endif +} + +lldb_private::Error +NativeProcessProtocol::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) +{ + // Default: not implemented. + return Error ("not implemented"); +} + +bool +NativeProcessProtocol::GetExitStatus (ExitType *exit_type, int *status, std::string &exit_description) +{ + if (m_state == lldb::eStateExited) + { + *exit_type = m_exit_type; + *status = m_exit_status; + exit_description = m_exit_description; + return true; + } + + *status = 0; + return false; +} + +bool +NativeProcessProtocol::SetExitStatus (ExitType exit_type, int status, const char *exit_description, bool bNotifyStateChange) +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessProtocol::%s(%d, %d, %s, %s) called", + __FUNCTION__, + exit_type, + status, + exit_description ? exit_description : "nullptr", + bNotifyStateChange ? "true" : "false"); + + // Exit status already set + if (m_state == lldb::eStateExited) + { + if (log) + log->Printf ("NativeProcessProtocol::%s exit status already set to %d, ignoring new set to %d", __FUNCTION__, m_exit_status, status); + return false; + } + + m_state = lldb::eStateExited; + + m_exit_type = exit_type; + m_exit_status = status; + if (exit_description && exit_description[0]) + m_exit_description = exit_description; + else + m_exit_description.clear(); + + if (bNotifyStateChange) + SynchronouslyNotifyProcessStateChanged (lldb::eStateExited); + + return true; +} + +NativeThreadProtocolSP +NativeProcessProtocol::GetThreadAtIndex (uint32_t idx) +{ + Mutex::Locker locker (m_threads_mutex); + if (idx < m_threads.size ()) + return m_threads[idx]; + return NativeThreadProtocolSP (); +} + +NativeThreadProtocolSP +NativeProcessProtocol::GetThreadByIDUnlocked (lldb::tid_t tid) +{ + for (auto thread_sp : m_threads) + { + if (thread_sp->GetID() == tid) + return thread_sp; + } + return NativeThreadProtocolSP (); +} + +NativeThreadProtocolSP +NativeProcessProtocol::GetThreadByID (lldb::tid_t tid) +{ + Mutex::Locker locker (m_threads_mutex); + return GetThreadByIDUnlocked (tid); +} + +bool +NativeProcessProtocol::IsAlive () const +{ + return m_state != eStateDetached + && m_state != eStateExited + && m_state != eStateInvalid + && m_state != eStateUnloaded; +} + +bool +NativeProcessProtocol::GetByteOrder (lldb::ByteOrder &byte_order) const +{ + ArchSpec process_arch; + if (!GetArchitecture (process_arch)) + return false; + byte_order = process_arch.GetByteOrder (); + return true; +} + +const NativeWatchpointList::WatchpointMap& +NativeProcessProtocol::GetWatchpointMap () const +{ + return m_watchpoint_list.GetWatchpointMap(); +} + +uint32_t +NativeProcessProtocol::GetMaxWatchpoints () const +{ + // This default implementation will return the number of + // *hardware* breakpoints available. MacOSX and other OS + // implementations that support software breakpoints will want to + // override this correctly for their implementation. + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // get any thread + NativeThreadProtocolSP thread_sp (const_cast<NativeProcessProtocol*> (this)->GetThreadAtIndex (0)); + if (!thread_sp) + { + if (log) + log->Warning ("NativeProcessProtocol::%s (): failed to find a thread to grab a NativeRegisterContext!", __FUNCTION__); + return 0; + } + + NativeRegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext ()); + if (!reg_ctx_sp) + { + if (log) + log->Warning ("NativeProcessProtocol::%s (): failed to get a RegisterContextNativeProcess from the first thread!", __FUNCTION__); + return 0; + } + + return reg_ctx_sp->NumSupportedHardwareWatchpoints (); +} + +Error +NativeProcessProtocol::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + // This default implementation assumes setting the watchpoint for + // the process will require setting the watchpoint for each of the + // threads. Furthermore, it will track watchpoints set for the + // process and will add them to each thread that is attached to + // via the (FIXME implement) OnThreadAttached () method. + + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Update the thread list + UpdateThreads (); + + // Keep track of the threads we successfully set the watchpoint + // for. If one of the thread watchpoint setting operations fails, + // back off and remove the watchpoint for all the threads that + // were successfully set so we get back to a consistent state. + std::vector<NativeThreadProtocolSP> watchpoint_established_threads; + + // Tell each thread to set a watchpoint. In the event that + // hardware watchpoints are requested but the SetWatchpoint fails, + // try to set a software watchpoint as a fallback. It's + // conceivable that if there are more threads than hardware + // watchpoints available, some of the threads will fail to set + // hardware watchpoints while software ones may be available. + Mutex::Locker locker (m_threads_mutex); + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not have a NULL thread!"); + if (!thread_sp) + continue; + + Error thread_error = thread_sp->SetWatchpoint (addr, size, watch_flags, hardware); + if (thread_error.Fail () && hardware) + { + // Try software watchpoints since we failed on hardware watchpoint setting + // and we may have just run out of hardware watchpoints. + thread_error = thread_sp->SetWatchpoint (addr, size, watch_flags, false); + if (thread_error.Success ()) + { + if (log) + log->Warning ("hardware watchpoint requested but software watchpoint set"); + } + } + + if (thread_error.Success ()) + { + // Remember that we set this watchpoint successfully in + // case we need to clear it later. + watchpoint_established_threads.push_back (thread_sp); + } + else + { + // Unset the watchpoint for each thread we successfully + // set so that we get back to a consistent state of "not + // set" for the watchpoint. + for (auto unwatch_thread_sp : watchpoint_established_threads) + { + Error remove_error = unwatch_thread_sp->RemoveWatchpoint (addr); + if (remove_error.Fail () && log) + { + log->Warning ("NativeProcessProtocol::%s (): RemoveWatchpoint failed for pid=%" PRIu64 ", tid=%" PRIu64 ": %s", + __FUNCTION__, GetID (), unwatch_thread_sp->GetID (), remove_error.AsCString ()); + } + } + + return thread_error; + } + } + return m_watchpoint_list.Add (addr, size, watch_flags, hardware); +} + +Error +NativeProcessProtocol::RemoveWatchpoint (lldb::addr_t addr) +{ + // Update the thread list + UpdateThreads (); + + Error overall_error; + + Mutex::Locker locker (m_threads_mutex); + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not have a NULL thread!"); + if (!thread_sp) + continue; + + const Error thread_error = thread_sp->RemoveWatchpoint (addr); + if (thread_error.Fail ()) + { + // Keep track of the first thread error if any threads + // fail. We want to try to remove the watchpoint from + // every thread, though, even if one or more have errors. + if (!overall_error.Fail ()) + overall_error = thread_error; + } + } + const Error error = m_watchpoint_list.Remove(addr); + return overall_error.Fail() ? overall_error : error; +} + +bool +NativeProcessProtocol::RegisterNativeDelegate (NativeDelegate &native_delegate) +{ + Mutex::Locker locker (m_delegates_mutex); + if (std::find (m_delegates.begin (), m_delegates.end (), &native_delegate) != m_delegates.end ()) + return false; + + m_delegates.push_back (&native_delegate); + native_delegate.InitializeDelegate (this); + return true; +} + +bool +NativeProcessProtocol::UnregisterNativeDelegate (NativeDelegate &native_delegate) +{ + Mutex::Locker locker (m_delegates_mutex); + + const auto initial_size = m_delegates.size (); + m_delegates.erase (remove (m_delegates.begin (), m_delegates.end (), &native_delegate), m_delegates.end ()); + + // We removed the delegate if the count of delegates shrank after + // removing all copies of the given native_delegate from the vector. + return m_delegates.size () < initial_size; +} + +void +NativeProcessProtocol::SynchronouslyNotifyProcessStateChanged (lldb::StateType state) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + + Mutex::Locker locker (m_delegates_mutex); + for (auto native_delegate: m_delegates) + native_delegate->ProcessStateChanged (this, state); + + if (log) + { + if (!m_delegates.empty ()) + { + log->Printf ("NativeProcessProtocol::%s: sent state notification [%s] from process %" PRIu64, + __FUNCTION__, lldb_private::StateAsCString (state), GetID ()); + } + else + { + log->Printf ("NativeProcessProtocol::%s: would send state notification [%s] from process %" PRIu64 ", but no delegates", + __FUNCTION__, lldb_private::StateAsCString (state), GetID ()); + } + } +} + +void +NativeProcessProtocol::NotifyDidExec () +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessProtocol::%s - preparing to call delegates", __FUNCTION__); + + { + Mutex::Locker locker (m_delegates_mutex); + for (auto native_delegate: m_delegates) + native_delegate->DidExec (this); + } +} + + +Error +NativeProcessProtocol::SetSoftwareBreakpoint (lldb::addr_t addr, uint32_t size_hint) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("NativeProcessProtocol::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + return m_breakpoint_list.AddRef (addr, size_hint, false, + [this] (lldb::addr_t addr, size_t size_hint, bool /* hardware */, NativeBreakpointSP &breakpoint_sp)->Error + { return SoftwareBreakpoint::CreateSoftwareBreakpoint (*this, addr, size_hint, breakpoint_sp); }); +} + +Error +NativeProcessProtocol::RemoveBreakpoint (lldb::addr_t addr) +{ + return m_breakpoint_list.DecRef (addr); +} + +Error +NativeProcessProtocol::EnableBreakpoint (lldb::addr_t addr) +{ + return m_breakpoint_list.EnableBreakpoint (addr); +} + +Error +NativeProcessProtocol::DisableBreakpoint (lldb::addr_t addr) +{ + return m_breakpoint_list.DisableBreakpoint (addr); +} + +lldb::StateType +NativeProcessProtocol::GetState () const +{ + Mutex::Locker locker (m_state_mutex); + return m_state; +} + +void +NativeProcessProtocol::SetState (lldb::StateType state, bool notify_delegates) +{ + Mutex::Locker locker (m_state_mutex); + + if (state == m_state) + return; + + m_state = state; + + if (StateIsStoppedState (state, false)) + { + ++m_stop_id; + + // Give process a chance to do any stop id bump processing, such as + // clearing cached data that is invalidated each time the process runs. + // Note if/when we support some threads running, we'll end up needing + // to manage this per thread and per process. + DoStopIDBumped (m_stop_id); + } + + // Optionally notify delegates of the state change. + if (notify_delegates) + SynchronouslyNotifyProcessStateChanged (state); +} + +uint32_t NativeProcessProtocol::GetStopID () const +{ + Mutex::Locker locker (m_state_mutex); + return m_stop_id; +} + +void +NativeProcessProtocol::DoStopIDBumped (uint32_t /* newBumpId */) +{ + // Default implementation does nothing. +} + +#ifndef __linux__ +// These need to be implemented to support lldb-gdb-server on a given platform. Stubs are +// provided to make the rest of the code link on non-supported platforms. + +Error +NativeProcessProtocol::Launch (ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &process_sp) +{ + llvm_unreachable("Platform has no NativeProcessProtocol support"); +} + +Error +NativeProcessProtocol::Attach (lldb::pid_t pid, + NativeDelegate &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &process_sp) +{ + llvm_unreachable("Platform has no NativeProcessProtocol support"); +} + +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/common/NativeRegisterContext.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeRegisterContext.cpp new file mode 100644 index 0000000..e67c079 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeRegisterContext.cpp @@ -0,0 +1,516 @@ +//===-- NativeRegisterContext.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/common/NativeRegisterContext.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +using namespace lldb; +using namespace lldb_private; + +NativeRegisterContext::NativeRegisterContext (NativeThreadProtocol &thread, uint32_t concrete_frame_idx) : + m_thread (thread), + m_concrete_frame_idx (concrete_frame_idx) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +NativeRegisterContext::~NativeRegisterContext() +{ +} + +// FIXME revisit invalidation, process stop ids, etc. Right now we don't +// support caching in NativeRegisterContext. We can do this later by +// utilizing NativeProcessProtocol::GetStopID () and adding a stop id to +// NativeRegisterContext. + +// void +// NativeRegisterContext::InvalidateIfNeeded (bool force) +// { +// ProcessSP process_sp (m_thread.GetProcess()); +// bool invalidate = force; +// uint32_t process_stop_id = UINT32_MAX; + +// if (process_sp) +// process_stop_id = process_sp->GetStopID(); +// else +// invalidate = true; + +// if (!invalidate) +// invalidate = process_stop_id != GetStopID(); + +// if (invalidate) +// { +// InvalidateAllRegisters (); +// SetStopID (process_stop_id); +// } +// } + + +const RegisterInfo * +NativeRegisterContext::GetRegisterInfoByName (const char *reg_name, uint32_t start_idx) +{ + if (reg_name && reg_name[0]) + { + const uint32_t num_registers = GetRegisterCount(); + for (uint32_t reg = start_idx; reg < num_registers; ++reg) + { + const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); + + if ((reg_info->name != nullptr && ::strcasecmp (reg_info->name, reg_name) == 0) || + (reg_info->alt_name != nullptr && ::strcasecmp (reg_info->alt_name, reg_name) == 0)) + { + return reg_info; + } + } + } + return nullptr; +} + +const RegisterInfo * +NativeRegisterContext::GetRegisterInfo (uint32_t kind, uint32_t num) +{ + const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num); + if (reg_num == LLDB_INVALID_REGNUM) + return nullptr; + return GetRegisterInfoAtIndex (reg_num); +} + +const char * +NativeRegisterContext::GetRegisterName (uint32_t reg) +{ + const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info) + return reg_info->name; + return nullptr; +} + +const char* +NativeRegisterContext::GetRegisterSetNameForRegisterAtIndex (uint32_t reg_index) const +{ + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return nullptr; + + for (uint32_t set_index = 0; set_index < GetRegisterSetCount (); ++set_index) + { + const RegisterSet *const reg_set = GetRegisterSet (set_index); + if (!reg_set) + continue; + + for (uint32_t reg_num_index = 0; reg_num_index < reg_set->num_registers; ++reg_num_index) + { + const uint32_t reg_num = reg_set->registers[reg_num_index]; + // FIXME double check we're checking the right register kind here. + if (reg_info->kinds[RegisterKind::eRegisterKindLLDB] == reg_num) + { + // The given register is a member of this register set. Return the register set name. + return reg_set->name; + } + } + } + + // Didn't find it. + return nullptr; +} + +lldb::addr_t +NativeRegisterContext::GetPC (lldb::addr_t fail_value) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + if (log) + log->Printf ("NativeRegisterContext::%s using reg index %" PRIu32 " (default %" PRIu64 ")", __FUNCTION__, reg, fail_value); + + const uint64_t retval = ReadRegisterAsUnsigned (reg, fail_value); + + if (log) + log->Printf ("NativeRegisterContext::%s " PRIu32 " retval %" PRIu64, __FUNCTION__, retval); + + return retval; +} + +lldb::addr_t +NativeRegisterContext::GetPCfromBreakpointLocation (lldb::addr_t fail_value) +{ + return GetPC (fail_value); +} + +Error +NativeRegisterContext::SetPC (lldb::addr_t pc) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + return WriteRegisterFromUnsigned (reg, pc); +} + +lldb::addr_t +NativeRegisterContext::GetSP (lldb::addr_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +Error +NativeRegisterContext::SetSP (lldb::addr_t sp) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + return WriteRegisterFromUnsigned (reg, sp); +} + +lldb::addr_t +NativeRegisterContext::GetFP (lldb::addr_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +Error +NativeRegisterContext::SetFP (lldb::addr_t fp) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + return WriteRegisterFromUnsigned (reg, fp); +} + +lldb::addr_t +NativeRegisterContext::GetReturnAddress (lldb::addr_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +lldb::addr_t +NativeRegisterContext::GetFlags (lldb::addr_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return ReadRegisterAsUnsigned (reg, fail_value); +} + + +lldb::addr_t +NativeRegisterContext::ReadRegisterAsUnsigned (uint32_t reg, lldb::addr_t fail_value) +{ + if (reg != LLDB_INVALID_REGNUM) + return ReadRegisterAsUnsigned (GetRegisterInfoAtIndex (reg), fail_value); + return fail_value; +} + +uint64_t +NativeRegisterContext::ReadRegisterAsUnsigned (const RegisterInfo *reg_info, lldb::addr_t fail_value) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + if (reg_info) + { + RegisterValue value; + Error error = ReadRegister (reg_info, value); + if (error.Success ()) + { + if (log) + log->Printf ("NativeRegisterContext::%s ReadRegister() succeeded, value %" PRIu64, __FUNCTION__, value.GetAsUInt64()); + return value.GetAsUInt64(); + } + else + { + if (log) + log->Printf ("NativeRegisterContext::%s ReadRegister() failed, error %s", __FUNCTION__, error.AsCString ()); + } + } + else + { + if (log) + log->Printf ("NativeRegisterContext::%s ReadRegister() null reg_info", __FUNCTION__); + } + return fail_value; +} + +Error +NativeRegisterContext::WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval) +{ + if (reg == LLDB_INVALID_REGNUM) + return Error ("NativeRegisterContext::%s (): reg is invalid", __FUNCTION__); + return WriteRegisterFromUnsigned (GetRegisterInfoAtIndex (reg), uval); +} + +Error +NativeRegisterContext::WriteRegisterFromUnsigned (const RegisterInfo *reg_info, uint64_t uval) +{ + assert (reg_info); + if (!reg_info) + return Error ("reg_info is nullptr"); + + RegisterValue value; + if (!value.SetUInt(uval, reg_info->byte_size)) + return Error ("RegisterValue::SetUInt () failed"); + + return WriteRegister (reg_info, value); +} + +lldb::tid_t +NativeRegisterContext::GetThreadID() const +{ + return m_thread.GetID(); +} + +uint32_t +NativeRegisterContext::NumSupportedHardwareBreakpoints () +{ + return 0; +} + +uint32_t +NativeRegisterContext::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + return LLDB_INVALID_INDEX32; +} + +bool +NativeRegisterContext::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + return false; +} + + +uint32_t +NativeRegisterContext::NumSupportedHardwareWatchpoints () +{ + return 0; +} + +uint32_t +NativeRegisterContext::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + return LLDB_INVALID_INDEX32; +} + +bool +NativeRegisterContext::ClearHardwareWatchpoint (uint32_t hw_index) +{ + return false; +} + +Error +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; +} + +lldb::addr_t +NativeRegisterContext::GetWatchpointHitAddress (uint32_t wp_index) +{ + return LLDB_INVALID_ADDRESS; +} + +bool +NativeRegisterContext::HardwareSingleStep (bool enable) +{ + return false; +} + +Error +NativeRegisterContext::ReadRegisterValueFromMemory ( + const RegisterInfo *reg_info, + lldb::addr_t src_addr, + size_t src_len, + RegisterValue ®_value) +{ + Error error; + if (reg_info == nullptr) + { + error.SetErrorString ("invalid register info argument."); + return error; + } + + + // Moving from addr into a register + // + // Case 1: src_len == dst_len + // + // |AABBCCDD| Address contents + // |AABBCCDD| Register contents + // + // Case 2: src_len > dst_len + // + // Error! (The register should always be big enough to hold the data) + // + // Case 3: src_len < dst_len + // + // |AABB| Address contents + // |AABB0000| Register contents [on little-endian hardware] + // |0000AABB| Register contents [on big-endian hardware] + if (src_len > RegisterValue::kMaxRegisterByteSize) + { + error.SetErrorString ("register too small to receive memory data"); + return error; + } + + 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)", + static_cast<uint64_t>(src_len), reg_info->name, static_cast<uint64_t>(dst_len)); + return error; + } + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + { + error.SetErrorString("invalid process"); + return error; + } + + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Read the memory + size_t bytes_read; + error = process_sp->ReadMemory (src_addr, src, src_len, bytes_read); + if (error.Fail ()) + return error; + + // Make sure the memory read succeeded... + if (bytes_read != src_len) + { + // This might happen if we read _some_ bytes but not all + error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes", + static_cast<uint64_t>(bytes_read), static_cast<uint64_t>(src_len)); + return error; + } + + // We now have a memory buffer that contains the part or all of the register + // value. Set the register value using this memory data. + // TODO: we might need to add a parameter to this function in case the byte + // order of the memory data doesn't match the process. For now we are assuming + // they are the same. + lldb::ByteOrder byte_order; + if (!process_sp->GetByteOrder (byte_order)) + { + error.SetErrorString ( "NativeProcessProtocol::GetByteOrder () failed"); + return error; + } + + reg_value.SetFromMemoryData ( + reg_info, + src, + src_len, + byte_order, + error); + + return error; +} + +Error +NativeRegisterContext::WriteRegisterValueToMemory ( + const RegisterInfo *reg_info, + lldb::addr_t dst_addr, + size_t dst_len, + const RegisterValue ®_value) +{ + + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + Error error; + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (process_sp) + { + + // TODO: we might need to add a parameter to this function in case the byte + // order of the memory data doesn't match the process. For now we are assuming + // they are the same. + lldb::ByteOrder byte_order; + if (!process_sp->GetByteOrder (byte_order)) + return Error ("NativeProcessProtocol::GetByteOrder () failed"); + + const size_t bytes_copied = reg_value.GetAsMemoryData ( + reg_info, + dst, + dst_len, + byte_order, + error); + + if (error.Success()) + { + if (bytes_copied == 0) + { + error.SetErrorString("byte copy failed."); + } + else + { + 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", + static_cast<uint64_t>(bytes_written), static_cast<uint64_t>(bytes_copied)); + } + } + } + } + else + error.SetErrorString("invalid process"); + + return error; +} + +uint32_t +NativeRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const +{ + const uint32_t num_regs = GetRegisterCount(); + + assert (kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + + diff --git a/contrib/llvm/tools/lldb/source/Host/common/NativeRegisterContextRegisterInfo.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeRegisterContextRegisterInfo.cpp new file mode 100644 index 0000000..0d7c6ec --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeRegisterContextRegisterInfo.cpp @@ -0,0 +1,50 @@ +//===-- NativeRegisterContex.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/lldb-types.h" +#include "lldb/lldb-private-forward.h" +#include "lldb/Host/common/NativeRegisterContextRegisterInfo.h" + +using namespace lldb_private; + +NativeRegisterContextRegisterInfo::NativeRegisterContextRegisterInfo (NativeThreadProtocol &thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info_interface) : + NativeRegisterContext (thread, concrete_frame_idx), + m_register_info_interface_up (register_info_interface) +{ + assert (register_info_interface && "null register_info_interface"); +} + +uint32_t +NativeRegisterContextRegisterInfo::GetRegisterCount () const +{ + return m_register_info_interface_up->GetRegisterCount (); +} + +uint32_t +NativeRegisterContextRegisterInfo::GetUserRegisterCount () const +{ + return m_register_info_interface_up->GetUserRegisterCount (); +} + +const RegisterInfo * +NativeRegisterContextRegisterInfo::GetRegisterInfoAtIndex (uint32_t reg_index) const +{ + if (reg_index <= GetRegisterCount ()) + return m_register_info_interface_up->GetRegisterInfo () + reg_index; + else + return nullptr; +} + +const RegisterInfoInterface& +NativeRegisterContextRegisterInfo::GetRegisterInfoInterface () const +{ + return *m_register_info_interface_up; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/NativeThreadProtocol.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeThreadProtocol.cpp new file mode 100644 index 0000000..ea406e9 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeThreadProtocol.cpp @@ -0,0 +1,75 @@ +//===-- NativeThreadProtocol.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/common/NativeThreadProtocol.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/common/SoftwareBreakpoint.h" + +using namespace lldb; +using namespace lldb_private; + +NativeThreadProtocol::NativeThreadProtocol (NativeProcessProtocol *process, lldb::tid_t tid) : + m_process_wp (process->shared_from_this ()), + m_tid (tid) +{ +} + +Error +NativeThreadProtocol::ReadRegister (uint32_t reg, RegisterValue ®_value) +{ + NativeRegisterContextSP register_context_sp = GetRegisterContext (); + if (!register_context_sp) + return Error ("no register context"); + + const RegisterInfo *const reg_info = register_context_sp->GetRegisterInfoAtIndex (reg); + if (!reg_info) + return Error ("no register info for reg num %" PRIu32, reg); + + return register_context_sp->ReadRegister (reg_info, reg_value);; +} + +Error +NativeThreadProtocol::WriteRegister (uint32_t reg, const RegisterValue ®_value) +{ + NativeRegisterContextSP register_context_sp = GetRegisterContext (); + if (!register_context_sp) + return Error ("no register context"); + + const RegisterInfo *const reg_info = register_context_sp->GetRegisterInfoAtIndex (reg); + if (!reg_info) + return Error ("no register info for reg num %" PRIu32, reg); + + return register_context_sp->WriteRegister (reg_info, reg_value); +} + +Error +NativeThreadProtocol::SaveAllRegisters (lldb::DataBufferSP &data_sp) +{ + NativeRegisterContextSP register_context_sp = GetRegisterContext (); + if (!register_context_sp) + return Error ("no register context"); + return register_context_sp->WriteAllRegisterValues (data_sp); +} + +Error +NativeThreadProtocol::RestoreAllRegisters (lldb::DataBufferSP &data_sp) +{ + NativeRegisterContextSP register_context_sp = GetRegisterContext (); + if (!register_context_sp) + return Error ("no register context"); + return register_context_sp->ReadAllRegisterValues (data_sp); +} + +NativeProcessProtocolSP +NativeThreadProtocol::GetProcess () +{ + return m_process_wp.lock (); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/NativeWatchpointList.cpp b/contrib/llvm/tools/lldb/source/Host/common/NativeWatchpointList.cpp new file mode 100644 index 0000000..209d213 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/NativeWatchpointList.cpp @@ -0,0 +1,35 @@ +//===-- NativeWatchpointList.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/common/NativeWatchpointList.h" + +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +Error +NativeWatchpointList::Add (addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + m_watchpoints[addr] = {addr, size, watch_flags, hardware}; + return Error (); +} + +Error +NativeWatchpointList::Remove (addr_t addr) +{ + m_watchpoints.erase(addr); + return Error (); +} + +const NativeWatchpointList::WatchpointMap& +NativeWatchpointList::GetWatchpointMap () const +{ + return m_watchpoints; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/OptionParser.cpp b/contrib/llvm/tools/lldb/source/Host/common/OptionParser.cpp new file mode 100644 index 0000000..a978459 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/OptionParser.cpp @@ -0,0 +1,116 @@ +//===-- source/Host/common/OptionParser.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/OptionParser.h" +#include "lldb/Host/HostGetOpt.h" +#include "lldb/lldb-private-types.h" + +#include <vector> + +using namespace lldb_private; + +void +OptionParser::Prepare(Mutex::Locker &locker) +{ + static Mutex g_mutex(Mutex::eMutexTypeNormal); + locker.Lock(g_mutex); +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif +} + +void +OptionParser::EnableError(bool error) +{ + opterr = error ? 1 : 0; +} + +int +OptionParser::Parse (int argc, + char * const argv [], + const char *optstring, + const Option *longopts, + int *longindex) +{ + std::vector<option> opts; + while (longopts->definition != nullptr) + { + option opt; + opt.flag = longopts->flag; + opt.val = longopts->val; + opt.name = longopts->definition->long_option; + opt.has_arg = longopts->definition->option_has_arg; + opts.push_back(opt); + ++longopts; + } + opts.push_back(option()); + return getopt_long_only(argc, argv, optstring, &opts[0], longindex); +} + +char* +OptionParser::GetOptionArgument() +{ + return optarg; +} + +int +OptionParser::GetOptionIndex() +{ + return optind; +} + +int +OptionParser::GetOptionErrorCause() +{ + return optopt; +} + +std::string +OptionParser::GetShortOptionString(struct option *long_options) +{ + std::string s; + int i=0; + bool done = false; + while (!done) + { + if (long_options[i].name == 0 && + long_options[i].has_arg == 0 && + long_options[i].flag == 0 && + long_options[i].val == 0) + { + done = true; + } + else + { + if (long_options[i].flag == NULL && + isalpha(long_options[i].val)) + { + s.append(1, (char)long_options[i].val); + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + + case optional_argument: + s.append(2, ':'); + break; + case required_argument: + s.append(1, ':'); + break; + } + } + ++i; + } + } + return s; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/PipeBase.cpp b/contrib/llvm/tools/lldb/source/Host/common/PipeBase.cpp new file mode 100644 index 0000000..a9d6e6f --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/PipeBase.cpp @@ -0,0 +1,27 @@ +//===-- source/Host/common/PipeBase.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/PipeBase.h" + +using namespace lldb_private; + + +PipeBase::~PipeBase() = default; + +Error +PipeBase::OpenAsWriter(llvm::StringRef name, bool child_process_inherit) +{ + return OpenAsWriterWithTimeout(name, child_process_inherit, std::chrono::microseconds::zero()); +} + +Error +PipeBase::Read(void *buf, size_t size, size_t &bytes_read) +{ + return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(), bytes_read); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/ProcessRunLock.cpp b/contrib/llvm/tools/lldb/source/Host/common/ProcessRunLock.cpp new file mode 100644 index 0000000..669a96d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/ProcessRunLock.cpp @@ -0,0 +1,71 @@ +#ifndef _WIN32 + +#include "lldb/Host/ProcessRunLock.h" + +namespace lldb_private { + + ProcessRunLock::ProcessRunLock() + : m_running(false) + { + int err = ::pthread_rwlock_init(&m_rwlock, NULL); (void) err; + //#if LLDB_CONFIGURATION_DEBUG + // assert(err == 0); + //#endif + } + + ProcessRunLock::~ProcessRunLock() + { + int err = ::pthread_rwlock_destroy(&m_rwlock); (void) err; + //#if LLDB_CONFIGURATION_DEBUG + // assert(err == 0); + //#endif + } + + bool ProcessRunLock::ReadTryLock() + { + ::pthread_rwlock_rdlock(&m_rwlock); + if (m_running == false) + { + return true; + } + ::pthread_rwlock_unlock(&m_rwlock); + return false; + } + + bool ProcessRunLock::ReadUnlock() + { + return ::pthread_rwlock_unlock(&m_rwlock) == 0; + } + + bool ProcessRunLock::SetRunning() + { + ::pthread_rwlock_wrlock(&m_rwlock); + m_running = true; + ::pthread_rwlock_unlock(&m_rwlock); + return true; + } + + bool ProcessRunLock::TrySetRunning() + { + bool r; + + if (::pthread_rwlock_trywrlock(&m_rwlock) == 0) + { + r = !m_running; + m_running = true; + ::pthread_rwlock_unlock(&m_rwlock); + return r; + } + return false; + } + + bool ProcessRunLock::SetStopped() + { + ::pthread_rwlock_wrlock(&m_rwlock); + m_running = false; + ::pthread_rwlock_unlock(&m_rwlock); + return true; + } +} + +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/common/Socket.cpp b/contrib/llvm/tools/lldb/source/Host/common/Socket.cpp new file mode 100644 index 0000000..91a5e37 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Socket.cpp @@ -0,0 +1,489 @@ +//===-- Socket.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/Socket.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/SocketAddress.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/common/UDPSocket.h" + +#ifndef LLDB_DISABLE_POSIX +#include "lldb/Host/posix/DomainSocket.h" + +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#include <sys/un.h> +#endif + +#ifdef __linux__ +#include "lldb/Host/linux/AbstractSocket.h" +#endif + +#ifdef __ANDROID_NDK__ +#include <linux/tcp.h> +#include <bits/error_constants.h> +#include <asm-generic/errno-base.h> +#include <errno.h> +#include <arpa/inet.h> +#if defined(ANDROID_ARM_BUILD_STATIC) || defined(ANDROID_MIPS_BUILD_STATIC) +#include <unistd.h> +#include <sys/syscall.h> +#include <fcntl.h> +#endif // ANDROID_ARM_BUILD_STATIC || ANDROID_MIPS_BUILD_STATIC +#endif // __ANDROID_NDK__ + +using namespace lldb; +using namespace lldb_private; + +#if defined(_WIN32) +typedef const char * set_socket_option_arg_type; +typedef char * get_socket_option_arg_type; +const NativeSocket Socket::kInvalidSocketValue = INVALID_SOCKET; +#else // #if defined(_WIN32) +typedef const void * set_socket_option_arg_type; +typedef void * get_socket_option_arg_type; +const NativeSocket Socket::kInvalidSocketValue = -1; +#endif // #if defined(_WIN32) + +namespace { + +bool IsInterrupted() +{ +#if defined(_WIN32) + return ::WSAGetLastError() == WSAEINTR; +#else + return errno == EINTR; +#endif +} + +} + +Socket::Socket(NativeSocket socket, SocketProtocol protocol, bool should_close) + : IOObject(eFDTypeSocket, should_close) + , m_protocol(protocol) + , m_socket(socket) +{ + +} + +Socket::~Socket() +{ + Close(); +} + +std::unique_ptr<Socket> Socket::Create(const SocketProtocol protocol, bool child_processes_inherit, Error &error) +{ + error.Clear(); + + std::unique_ptr<Socket> socket_up; + switch (protocol) + { + case ProtocolTcp: + socket_up.reset(new TCPSocket(child_processes_inherit, error)); + break; + case ProtocolUdp: + socket_up.reset(new UDPSocket(child_processes_inherit, error)); + break; + case ProtocolUnixDomain: +#ifndef LLDB_DISABLE_POSIX + socket_up.reset(new DomainSocket(child_processes_inherit, error)); +#else + error.SetErrorString("Unix domain sockets are not supported on this platform."); +#endif + break; + case ProtocolUnixAbstract: +#ifdef __linux__ + socket_up.reset(new AbstractSocket(child_processes_inherit, error)); +#else + error.SetErrorString("Abstract domain sockets are not supported on this platform."); +#endif + break; + } + + if (error.Fail()) + socket_up.reset(); + + return socket_up; +} + +Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); + if (log) + log->Printf ("Socket::%s (host/port = %s)", __FUNCTION__, host_and_port.data()); + + Error error; + std::unique_ptr<Socket> connect_socket(Create(ProtocolTcp, child_processes_inherit, error)); + if (error.Fail()) + return error; + + error = connect_socket->Connect(host_and_port); + if (error.Success()) + socket = connect_socket.release(); + + return error; +} + +Error +Socket::TcpListen (llvm::StringRef host_and_port, + bool child_processes_inherit, + Socket *&socket, + Predicate<uint16_t>* predicate, + int backlog) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("Socket::%s (%s)", __FUNCTION__, host_and_port.data()); + + Error error; + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, &error)) + return error; + + std::unique_ptr<TCPSocket> listen_socket(new TCPSocket(child_processes_inherit, error)); + if (error.Fail()) + return error; + + error = listen_socket->Listen(host_and_port, backlog); + if (error.Success()) + { + // We were asked to listen on port zero which means we + // must now read the actual port that was given to us + // as port zero is a special code for "find an open port + // for me". + if (port == 0) + port = listen_socket->GetLocalPortNumber(); + + // Set the port predicate since when doing a listen://<host>:<port> + // it often needs to accept the incoming connection which is a blocking + // system call. Allowing access to the bound port using a predicate allows + // us to wait for the port predicate to be set to a non-zero value from + // another thread in an efficient manor. + if (predicate) + predicate->SetValue (port, eBroadcastAlways); + socket = listen_socket.release(); + } + + return error; +} + +Error Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&send_socket, Socket *&recv_socket) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("Socket::%s (host/port = %s)", __FUNCTION__, host_and_port.data()); + + return UDPSocket::Connect(host_and_port, child_processes_inherit, send_socket, recv_socket); +} + +Error Socket::UnixDomainConnect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) +{ + Error error; + std::unique_ptr<Socket> connect_socket(Create(ProtocolUnixDomain, child_processes_inherit, error)); + if (error.Fail()) + return error; + + error = connect_socket->Connect(name); + if (error.Success()) + socket = connect_socket.release(); + + return error; +} + +Error Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) +{ + Error error; + std::unique_ptr<Socket> listen_socket(Create(ProtocolUnixDomain, child_processes_inherit, error)); + if (error.Fail()) + return error; + + error = listen_socket->Listen(name, 5); + if (error.Fail()) + return error; + + error = listen_socket->Accept(name, child_processes_inherit, socket); + return error; +} + +Error +Socket::UnixAbstractConnect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) +{ + Error error; + std::unique_ptr<Socket> connect_socket(Create(ProtocolUnixAbstract, child_processes_inherit, error)); + if (error.Fail()) + return error; + + error = connect_socket->Connect(name); + if (error.Success()) + socket = connect_socket.release(); + return error; +} + +Error +Socket::UnixAbstractAccept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) +{ + Error error; + std::unique_ptr<Socket> listen_socket(Create(ProtocolUnixAbstract,child_processes_inherit, error)); + if (error.Fail()) + return error; + + error = listen_socket->Listen(name, 5); + if (error.Fail()) + return error; + + error = listen_socket->Accept(name, child_processes_inherit, socket); + return error; +} + +bool +Socket::DecodeHostAndPort(llvm::StringRef host_and_port, + std::string &host_str, + std::string &port_str, + int32_t& port, + Error *error_ptr) +{ + static RegularExpression g_regex ("([^:]+):([0-9]+)"); + RegularExpression::Match regex_match(2); + if (g_regex.Execute (host_and_port.data(), ®ex_match)) + { + if (regex_match.GetMatchAtIndex (host_and_port.data(), 1, host_str) && + regex_match.GetMatchAtIndex (host_and_port.data(), 2, port_str)) + { + bool ok = false; + port = StringConvert::ToUInt32 (port_str.c_str(), UINT32_MAX, 10, &ok); + if (ok && port < UINT16_MAX) + { + if (error_ptr) + error_ptr->Clear(); + return true; + } + // port is too large + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port.data()); + return false; + } + } + + // If this was unsuccessful, then check if it's simply a signed 32-bit integer, representing + // a port with an empty host. + host_str.clear(); + port_str.clear(); + bool ok = false; + port = StringConvert::ToUInt32 (host_and_port.data(), UINT32_MAX, 10, &ok); + if (ok && port < UINT16_MAX) + { + port_str = host_and_port; + if (error_ptr) + error_ptr->Clear(); + return true; + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port.data()); + return false; +} + +IOObject::WaitableHandle Socket::GetWaitableHandle() +{ + // TODO: On Windows, use WSAEventSelect + return m_socket; +} + +Error Socket::Read (void *buf, size_t &num_bytes) +{ + Error error; + int bytes_received = 0; + do + { + bytes_received = ::recv (m_socket, static_cast<char *>(buf), num_bytes, 0); + } while (bytes_received < 0 && IsInterrupted ()); + + if (bytes_received < 0) + { + SetLastError (error); + num_bytes = 0; + } + else + num_bytes = bytes_received; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); + if (log) + { + log->Printf ("%p Socket::Read() (socket = %" PRIu64 ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + static_cast<void*>(this), + static_cast<uint64_t>(m_socket), + buf, + static_cast<uint64_t>(num_bytes), + static_cast<int64_t>(bytes_received), + error.AsCString()); + } + + return error; +} + +Error Socket::Write (const void *buf, size_t &num_bytes) +{ + Error error; + int bytes_sent = 0; + do + { + bytes_sent = Send(buf, num_bytes); + } while (bytes_sent < 0 && IsInterrupted ()); + + if (bytes_sent < 0) + { + SetLastError (error); + num_bytes = 0; + } + else + num_bytes = bytes_sent; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); + if (log) + { + log->Printf ("%p Socket::Write() (socket = %" PRIu64 ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + static_cast<void*>(this), + static_cast<uint64_t>(m_socket), + buf, + static_cast<uint64_t>(num_bytes), + static_cast<int64_t>(bytes_sent), + error.AsCString()); + } + + return error; +} + +Error Socket::PreDisconnect() +{ + Error error; + return error; +} + +Error Socket::Close() +{ + Error error; + if (!IsValid() || !m_should_close_fd) + return error; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p Socket::Close (fd = %i)", static_cast<void*>(this), m_socket); + +#if defined(_WIN32) + bool success = !!closesocket(m_socket); +#else + bool success = !!::close (m_socket); +#endif + // A reference to a FD was passed in, set it to an invalid value + m_socket = kInvalidSocketValue; + if (!success) + { + SetLastError (error); + } + + return error; +} + + +int Socket::GetOption(int level, int option_name, int &option_value) +{ + get_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value); + socklen_t option_value_size = sizeof(int); + return ::getsockopt(m_socket, level, option_name, option_value_p, &option_value_size); +} + +int Socket::SetOption(int level, int option_name, int option_value) +{ + set_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value); + return ::setsockopt(m_socket, level, option_name, option_value_p, sizeof(option_value)); +} + +size_t Socket::Send(const void *buf, const size_t num_bytes) +{ + return ::send (m_socket, static_cast<const char *>(buf), num_bytes, 0); +} + +void Socket::SetLastError(Error &error) +{ +#if defined(_WIN32) + error.SetError(::WSAGetLastError(), lldb::eErrorTypeWin32); +#else + error.SetErrorToErrno(); +#endif +} + +NativeSocket +Socket::CreateSocket(const int domain, + const int type, + const int protocol, + bool child_processes_inherit, + Error& error) +{ + error.Clear(); + auto socketType = type; +#ifdef SOCK_CLOEXEC + if (!child_processes_inherit) + socketType |= SOCK_CLOEXEC; +#endif + auto sock = ::socket (domain, socketType, protocol); + if (sock == kInvalidSocketValue) + SetLastError(error); + + return sock; +} + +NativeSocket +Socket::AcceptSocket(NativeSocket sockfd, + struct sockaddr *addr, + socklen_t *addrlen, + bool child_processes_inherit, + Error& error) +{ + error.Clear(); +#if defined(ANDROID_ARM_BUILD_STATIC) || defined(ANDROID_MIPS_BUILD_STATIC) + // Temporary workaround for statically linking Android lldb-server with the + // latest API. + int fd = syscall(__NR_accept, sockfd, addr, addrlen); + if (fd >= 0 && !child_processes_inherit) + { + int flags = ::fcntl(fd, F_GETFD); + if (flags != -1 && ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1) + return fd; + SetLastError(error); + close(fd); + } + return fd; +#elif defined(SOCK_CLOEXEC) + int flags = 0; + if (!child_processes_inherit) { + flags |= SOCK_CLOEXEC; + } +#if defined(__NetBSD__) + NativeSocket fd = ::paccept (sockfd, addr, addrlen, nullptr, flags); +#else + NativeSocket fd = ::accept4 (sockfd, addr, addrlen, flags); +#endif +#else + NativeSocket fd = ::accept (sockfd, addr, addrlen); +#endif + if (fd == kInvalidSocketValue) + SetLastError(error); + return fd; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/SocketAddress.cpp b/contrib/llvm/tools/lldb/source/Host/common/SocketAddress.cpp new file mode 100644 index 0000000..c8b1687 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/SocketAddress.cpp @@ -0,0 +1,369 @@ +//===-- SocketAddress.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/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> + +// C++ Includes +// Other libraries and framework includes +// Project includes + +// WindowsXP needs an inet_ntop implementation +#ifdef _WIN32 + +#ifndef INET6_ADDRSTRLEN // might not be defined in older Windows SDKs +#define INET6_ADDRSTRLEN 46 +#endif + +// TODO: implement shortened form "::" for runs of zeros +const char* inet_ntop(int af, const void * src, + char * dst, socklen_t size) +{ + if (size==0) + { + return nullptr; + } + + switch (af) + { + case AF_INET: + { + { + const char* formatted = inet_ntoa(*static_cast<const in_addr*>(src)); + if (formatted && strlen(formatted) < size) + { + return ::strcpy(dst, formatted); + } + } + return nullptr; + case AF_INET6: + { + char tmp[INET6_ADDRSTRLEN] = {0}; + const uint16_t* src16 = static_cast<const uint16_t*>(src); + 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)) + { + return ::strcpy(dst, tmp); + } + return nullptr; + } + } + } + return nullptr; +} +#endif + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SocketAddress constructor +//---------------------------------------------------------------------- +SocketAddress::SocketAddress () +{ + Clear (); +} + +SocketAddress::SocketAddress (const struct sockaddr &s) +{ + m_socket_addr.sa = s; +} + + +SocketAddress::SocketAddress (const struct sockaddr_in &s) +{ + m_socket_addr.sa_ipv4 = s; +} + + +SocketAddress::SocketAddress (const struct sockaddr_in6 &s) +{ + m_socket_addr.sa_ipv6 = s; +} + + +SocketAddress::SocketAddress (const struct sockaddr_storage &s) +{ + m_socket_addr.sa_storage = s; +} + +//---------------------------------------------------------------------- +// SocketAddress copy constructor +//---------------------------------------------------------------------- +SocketAddress::SocketAddress (const SocketAddress& rhs) : + m_socket_addr (rhs.m_socket_addr) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SocketAddress::~SocketAddress() +{ +} + +void +SocketAddress::Clear () +{ + memset (&m_socket_addr, 0, sizeof(m_socket_addr)); +} + +bool +SocketAddress::IsValid () const +{ + return GetLength () != 0; +} + +static socklen_t +GetFamilyLength (sa_family_t family) +{ + switch (family) + { + case AF_INET: return sizeof(struct sockaddr_in); + case AF_INET6: return sizeof(struct sockaddr_in6); + } + assert(0 && "Unsupported address family"); + return 0; +} + +socklen_t +SocketAddress::GetLength () const +{ +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) + return m_socket_addr.sa.sa_len; +#else + return GetFamilyLength (GetFamily()); +#endif +} + +socklen_t +SocketAddress::GetMaxLength () +{ + return sizeof (sockaddr_t); +} + +sa_family_t +SocketAddress::GetFamily () const +{ + return m_socket_addr.sa.sa_family; +} + +void +SocketAddress::SetFamily (sa_family_t family) +{ + m_socket_addr.sa.sa_family = family; +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) + m_socket_addr.sa.sa_len = GetFamilyLength (family); +#endif +} + +std::string +SocketAddress::GetIPAddress () const +{ + char str[INET6_ADDRSTRLEN] = {0}; + switch (GetFamily()) + { + case AF_INET: + if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv4.sin_addr, str, sizeof(str))) + { + return str; + } + case AF_INET6: + if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv6.sin6_addr, str, sizeof(str))) + { + return str; + } + } + return ""; +} + +uint16_t +SocketAddress::GetPort () const +{ + switch (GetFamily()) + { + case AF_INET: return ntohs(m_socket_addr.sa_ipv4.sin_port); + case AF_INET6: return ntohs(m_socket_addr.sa_ipv6.sin6_port); + } + return 0; +} + +bool +SocketAddress::SetPort (uint16_t port) +{ + switch (GetFamily()) + { + case AF_INET: + m_socket_addr.sa_ipv4.sin_port = htons(port); + return true; + + case AF_INET6: + m_socket_addr.sa_ipv6.sin6_port = htons(port); + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// SocketAddress assignment operator +//---------------------------------------------------------------------- +const SocketAddress& +SocketAddress::operator=(const SocketAddress& rhs) +{ + if (this != &rhs) + m_socket_addr = rhs.m_socket_addr; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct addrinfo *addr_info) +{ + Clear(); + if (addr_info && + addr_info->ai_addr && + addr_info->ai_addrlen > 0&& + addr_info->ai_addrlen <= sizeof m_socket_addr) + { + ::memcpy (&m_socket_addr, + addr_info->ai_addr, + addr_info->ai_addrlen); + } + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr &s) +{ + m_socket_addr.sa = s; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr_in &s) +{ + m_socket_addr.sa_ipv4 = s; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr_in6 &s) +{ + m_socket_addr.sa_ipv6 = s; + return *this; +} + +const SocketAddress& +SocketAddress::operator=(const struct sockaddr_storage &s) +{ + m_socket_addr.sa_storage = s; + return *this; +} + +bool +SocketAddress::getaddrinfo (const char *host, + const char *service, + int ai_family, + int ai_socktype, + int ai_protocol, + int ai_flags) +{ + Clear (); + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai_family; + hints.ai_socktype = ai_socktype; + hints.ai_protocol = ai_protocol; + hints.ai_flags = ai_flags; + + bool result = false; + struct addrinfo *service_info_list = NULL; + int err = ::getaddrinfo (host, service, &hints, &service_info_list); + if (err == 0 && service_info_list) + { + *this = service_info_list; + result = IsValid (); + } + + if (service_info_list) + ::freeaddrinfo(service_info_list); + + return result; +} + + +bool +SocketAddress::SetToLocalhost (sa_family_t family, uint16_t port) +{ + switch (family) + { + case AF_INET: + SetFamily (AF_INET); + if (SetPort (port)) + { + m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + return true; + } + break; + + case AF_INET6: + SetFamily (AF_INET6); + if (SetPort (port)) + { + m_socket_addr.sa_ipv6.sin6_addr = in6addr_loopback; + return true; + } + break; + + } + Clear(); + return false; +} + +bool +SocketAddress::SetToAnyAddress (sa_family_t family, uint16_t port) +{ + switch (family) + { + case AF_INET: + SetFamily (AF_INET); + if (SetPort (port)) + { + m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_ANY); + return true; + } + break; + + case AF_INET6: + SetFamily (AF_INET6); + if (SetPort (port)) + { + m_socket_addr.sa_ipv6.sin6_addr = in6addr_any; + return true; + } + break; + + } + Clear(); + return false; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/SoftwareBreakpoint.cpp b/contrib/llvm/tools/lldb/source/Host/common/SoftwareBreakpoint.cpp new file mode 100644 index 0000000..5a6f783 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/SoftwareBreakpoint.cpp @@ -0,0 +1,317 @@ +//===-- SoftwareBreakpoint.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/common/SoftwareBreakpoint.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/Debug.h" +#include "lldb/Host/Mutex.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" + +using namespace lldb_private; + +// ------------------------------------------------------------------- +// static members +// ------------------------------------------------------------------- + +Error +SoftwareBreakpoint::CreateSoftwareBreakpoint (NativeProcessProtocol &process, lldb::addr_t addr, size_t size_hint, NativeBreakpointSP &breakpoint_sp) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + // Validate the address. + if (addr == LLDB_INVALID_ADDRESS) + return Error ("SoftwareBreakpoint::%s invalid load address specified.", __FUNCTION__); + + // Ask the NativeProcessProtocol subclass to fill in the correct software breakpoint + // trap for the breakpoint site. + size_t bp_opcode_size = 0; + const uint8_t *bp_opcode_bytes = NULL; + Error error = process.GetSoftwareBreakpointTrapOpcode (size_hint, bp_opcode_size, bp_opcode_bytes); + + if (error.Fail ()) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s failed to retrieve software breakpoint trap opcode: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + // Validate size of trap opcode. + if (bp_opcode_size == 0) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s failed to retrieve any trap opcodes", __FUNCTION__); + return Error ("SoftwareBreakpoint::GetSoftwareBreakpointTrapOpcode() returned zero, unable to get breakpoint trap for address 0x%" PRIx64, addr); + } + + if (bp_opcode_size > MAX_TRAP_OPCODE_SIZE) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s cannot support %lu trapcode bytes, max size is %lu", __FUNCTION__, bp_opcode_size, MAX_TRAP_OPCODE_SIZE); + return Error ("SoftwareBreakpoint::GetSoftwareBreakpointTrapOpcode() returned too many trap opcode bytes: requires %lu but we only support a max of %lu", bp_opcode_size, MAX_TRAP_OPCODE_SIZE); + } + + // Validate that we received opcodes. + if (!bp_opcode_bytes) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s failed to retrieve trap opcode bytes", __FUNCTION__); + return Error ("SoftwareBreakpoint::GetSoftwareBreakpointTrapOpcode() returned NULL trap opcode bytes, unable to get breakpoint trap for address 0x%" PRIx64, addr); + } + + // Enable the breakpoint. + uint8_t saved_opcode_bytes [MAX_TRAP_OPCODE_SIZE]; + error = EnableSoftwareBreakpoint (process, addr, bp_opcode_size, bp_opcode_bytes, saved_opcode_bytes); + if (error.Fail ()) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s: failed to enable new breakpoint at 0x%" PRIx64 ": %s", __FUNCTION__, addr, error.AsCString ()); + return error; + } + + if (log) + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- SUCCESS", __FUNCTION__, addr); + + // Set the breakpoint and verified it was written properly. Now + // create a breakpoint remover that understands how to undo this + // breakpoint. + breakpoint_sp.reset (new SoftwareBreakpoint (process, addr, saved_opcode_bytes, bp_opcode_bytes, bp_opcode_size)); + return Error (); +} + +Error +SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, lldb::addr_t addr, size_t bp_opcode_size, const uint8_t *bp_opcode_bytes, uint8_t *saved_opcode_bytes) +{ + assert (bp_opcode_size <= MAX_TRAP_OPCODE_SIZE && "bp_opcode_size out of valid range"); + assert (bp_opcode_bytes && "bp_opcode_bytes is NULL"); + assert (saved_opcode_bytes && "saved_opcode_bytes is NULL"); + + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr); + + // Save the original opcodes by reading them so we can restore later. + size_t bytes_read = 0; + + Error error = process.ReadMemory(addr, saved_opcode_bytes, bp_opcode_size, bytes_read); + if (error.Fail ()) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + // Ensure we read as many bytes as we expected. + 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, (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. + if (log) + { + 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%hhx)", + __FUNCTION__, addr, i++, *read_byte); + } + } + + // Write a software breakpoint in place of the original opcode. + size_t bytes_written = 0; + error = process.WriteMemory(addr, bp_opcode_bytes, bp_opcode_size, bytes_written); + if (error.Fail ()) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s failed to write memory while attempting to set breakpoint: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + // Ensure we wrote as many bytes as we expected. + 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, (uint64_t)bytes_written); + if (log) + log->PutCString (error.AsCString ()); + return error; + } + + uint8_t verify_bp_opcode_bytes [MAX_TRAP_OPCODE_SIZE]; + 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) + log->Printf ("SoftwareBreakpoint::%s failed to read memory while attempting to verify the breakpoint set: %s", __FUNCTION__, error.AsCString ()); + return error; + } + + // Ensure we read as many verification bytes as we expected. + 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, (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) + { + if (log) + log->Printf ("SoftwareBreakpoint::%s: verification of software breakpoint writing failed - trap opcodes not successfully read back after writing when setting breakpoint at 0x%" PRIx64, __FUNCTION__, addr); + return Error ("SoftwareBreakpoint::%s: verification of software breakpoint writing failed - trap opcodes not successfully read back after writing when setting breakpoint at 0x%" PRIx64, __FUNCTION__, addr); + } + + if (log) + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- SUCCESS", __FUNCTION__, addr); + + return Error (); +} + +// ------------------------------------------------------------------- +// instance-level members +// ------------------------------------------------------------------- + +SoftwareBreakpoint::SoftwareBreakpoint (NativeProcessProtocol &process, lldb::addr_t addr, const uint8_t *saved_opcodes, const uint8_t *trap_opcodes, size_t opcode_size) : + NativeBreakpoint (addr), + m_process (process), + m_saved_opcodes (), + m_trap_opcodes (), + m_opcode_size (opcode_size) +{ + assert ( opcode_size > 0 && "setting software breakpoint with no trap opcodes"); + assert ( opcode_size <= MAX_TRAP_OPCODE_SIZE && "trap opcode size too large"); + + ::memcpy (m_saved_opcodes, saved_opcodes, opcode_size); + ::memcpy (m_trap_opcodes, trap_opcodes, opcode_size); +} + +Error +SoftwareBreakpoint::DoEnable () +{ + return EnableSoftwareBreakpoint (m_process, m_addr, m_opcode_size, m_trap_opcodes, m_saved_opcodes); +} + +Error +SoftwareBreakpoint::DoDisable () +{ + Error error; + assert (m_addr && (m_addr != LLDB_INVALID_ADDRESS) && "can't remove a software breakpoint for an invalid address"); + + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, m_addr); + + assert ( (m_opcode_size > 0) && "cannot restore opcodes when there are no opcodes"); + + if (m_opcode_size > 0) + { + // Clear a software breakpoint instruction + uint8_t curr_break_op [MAX_TRAP_OPCODE_SIZE]; + bool break_op_found = false; + assert (m_opcode_size <= sizeof (curr_break_op)); + + // Read the breakpoint opcode + size_t bytes_read = 0; + error = m_process.ReadMemory (m_addr, curr_break_op, m_opcode_size, bytes_read); + 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, (uint64_t)bytes_read); + } + if (error.Success ()) + { + bool verify = false; + // 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. + size_t bytes_written = 0; + error = m_process.WriteMemory (m_addr, m_saved_opcodes, m_opcode_size, bytes_written); + 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, (uint64_t)bytes_written); + } + if (error.Success ()) + { + verify = true; + } + } + else + { + error.SetErrorString("Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has already been restored + verify = true; + } + + if (verify) + { + uint8_t verify_opcode [MAX_TRAP_OPCODE_SIZE]; + assert (m_opcode_size <= sizeof (verify_opcode)); + // Verify that our original opcode made it back to the inferior + + 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 < 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, (uint64_t)verify_bytes_read); + } + if (error.Success ()) + { + // compare the memory we just read with the original opcode + if (::memcmp (m_saved_opcodes, verify_opcode, m_opcode_size) == 0) + { + // SUCCESS + if (log) + { + 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%hhx", + __FUNCTION__, m_addr, i++, *verify_byte); + } + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- SUCCESS", __FUNCTION__, m_addr); + } + return error; + } + else + { + if (break_op_found) + error.SetErrorString("Failed to restore original opcode."); + } + } + else + error.SetErrorString("Failed to read memory to verify that breakpoint trap was restored."); + } + } + } + + if (log && error.Fail ()) + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- FAILED: %s", + __FUNCTION__, + m_addr, + error.AsCString()); + return error; +} + +bool +SoftwareBreakpoint::IsSoftwareBreakpoint () const +{ + return true; +} + diff --git a/contrib/llvm/tools/lldb/source/Host/common/StringConvert.cpp b/contrib/llvm/tools/lldb/source/Host/common/StringConvert.cpp new file mode 100644 index 0000000..c4ff675 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/StringConvert.cpp @@ -0,0 +1,117 @@ +//===-- StringConvert.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdlib.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/StringConvert.h" + +namespace lldb_private +{ + namespace StringConvert + { + + int32_t + ToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr) + { + if (s && s[0]) + { + char *end = nullptr; + const long sval = ::strtol (s, &end, base); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = ((sval <= INT32_MAX) && (sval >= INT32_MIN)); + return (int32_t)sval; // All characters were used, return the result + } + } + if (success_ptr) + *success_ptr = false; + return fail_value; + } + + uint32_t + ToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) + { + if (s && s[0]) + { + char *end = nullptr; + const unsigned long uval = ::strtoul (s, &end, base); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = (uval <= UINT32_MAX); + return (uint32_t)uval; // All characters were used, return the result + } + } + if (success_ptr) + *success_ptr = false; + return fail_value; + } + + int64_t + ToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr) + { + if (s && s[0]) + { + char *end = nullptr; + int64_t uval = ::strtoll (s, &end, base); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) + *success_ptr = false; + return fail_value; + } + + uint64_t + ToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr) + { + if (s && s[0]) + { + char *end = nullptr; + uint64_t uval = ::strtoull (s, &end, base); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; + } + + double + ToDouble (const char *s, double fail_value, bool *success_ptr) + { + if (s && s[0]) + { + char *end = nullptr; + double val = strtod (s, &end); + if (*end == '\0') + { + if (success_ptr) + *success_ptr = true; + return val; // All characters were used, return the result + } + } + if (success_ptr) + *success_ptr = false; + return fail_value; + } + } +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Symbols.cpp b/contrib/llvm/tools/lldb/source/Host/common/Symbols.cpp new file mode 100644 index 0000000..60e1dc6 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Symbols.cpp @@ -0,0 +1,327 @@ +//===-- Symbols.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/Symbols.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#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(__APPLE__) + +// Forward declaration of method defined in source/Host/macosx/Symbols.cpp +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + ModuleSpec &return_module_spec +); + +#else + +int +LocateMacOSXFilesUsingDebugSymbols +( + const ModuleSpec &module_spec, + ModuleSpec &return_module_spec +) { + // Cannot find MacOSX files using debug symbols on non MacOSX. + return 0; +} + +#endif + +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)) + { + 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; +} + +static bool +LocateDSYMInVincinityOfExecutable (const ModuleSpec &module_spec, FileSpec &dsym_fspec) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + if (exec_fspec) + { + char path[PATH_MAX]; + if (exec_fspec->GetPath(path, sizeof(path))) + { + // Make sure the module isn't already just a dSYM file... + if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) + { + if (log) + { + if (module_spec.GetUUIDPtr() && module_spec.GetUUIDPtr()->IsValid()) + { + log->Printf ("Searching for dSYM bundle next to executable %s, UUID %s", path, module_spec.GetUUIDPtr()->GetAsString().c_str()); + } + else + { + log->Printf ("Searching for dSYM bundle next to executable %s", path); + } + } + 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); + + dsym_fspec.SetFile(path, false); + + ModuleSpecList module_specs; + ModuleSpec matched_module_spec; + if (dsym_fspec.Exists() && + FileAtPathContainsArchAndUUID(dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) + { + if (log) + { + log->Printf ("dSYM with matching UUID & arch found at %s", path); + } + return true; + } + else + { + path[obj_file_path_length] = '\0'; + + char *last_dot = strrchr(path, '.'); + while (last_dot != NULL && last_dot[0]) + { + 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())) + { + if (log) + { + log->Printf ("dSYM with matching UUID & arch found at %s", path); + } + return true; + } + else + { + *last_dot = '\0'; + char *prev_slash = strrchr(path, '/'); + if (prev_slash != NULL) + *prev_slash = '\0'; + else + break; + } + } + else + { + break; + } + } + } + } + } + } + dsym_fspec.Clear(); + return false; +} + +FileSpec +LocateExecutableSymbolFileDsym (const ModuleSpec &module_spec) +{ + const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + const UUID *uuid = module_spec.GetUUIDPtr(); + + Timer scoped_timer (__PRETTY_FUNCTION__, + "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)", + exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", + arch ? arch->GetArchitectureName() : "<NULL>", + (const void*)uuid); + + FileSpec symbol_fspec; + ModuleSpec dsym_module_spec; + // 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, dsym_module_spec); + } + else + { + dsym_module_spec.GetSymbolFileSpec() = symbol_fspec; + } + return dsym_module_spec.GetSymbolFileSpec(); +} + +ModuleSpec +Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) +{ + ModuleSpec result; + 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>", + (const void*)uuid); + + 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)) + { + result.GetFileSpec() = exec_fspec; + } + else + { + LocateMacOSXFilesUsingDebugSymbols (module_spec, result); + } + return result; +} + +FileSpec +Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) +{ + FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec(); + if (symbol_file_spec.IsAbsolute() && symbol_file_spec.Exists()) + return symbol_file_spec; + + const char *symbol_filename = symbol_file_spec.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)); + +#ifndef LLVM_ON_WIN32 + // Add /usr/lib/debug directory. + debug_file_search_paths.AppendIfUnique (FileSpec("/usr/lib/debug", true)); +#endif // LLVM_ON_WIN32 + + 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"; + } + + 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); + + // Some debug files may stored in the module directory like this: + // /usr/lib/debug/usr/lib/library.so.debug + if (!file_dir.IsEmpty()) + files.push_back (dirname + file_dir.AsCString() + "/" + 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(); +} + +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; +} + +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/common/TCPSocket.cpp b/contrib/llvm/tools/lldb/source/Host/common/TCPSocket.cpp new file mode 100644 index 0000000..b23055e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/TCPSocket.cpp @@ -0,0 +1,288 @@ +//===-- TcpSocket.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/common/TCPSocket.h" + +#include "lldb/Core/Log.h" +#include "lldb/Host/Config.h" + +#ifndef LLDB_DISABLE_POSIX +#include <arpa/inet.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +namespace { + +const int kDomain = AF_INET; +const int kType = SOCK_STREAM; + +} + +TCPSocket::TCPSocket(NativeSocket socket, bool should_close) + : Socket(socket, ProtocolTcp, should_close) +{ + +} + +TCPSocket::TCPSocket(bool child_processes_inherit, Error &error) + : TCPSocket(CreateSocket(kDomain, kType, IPPROTO_TCP, child_processes_inherit, error), true) +{ +} + + +// Return the port number that is being used by the socket. +uint16_t +TCPSocket::GetLocalPortNumber() const +{ + if (m_socket != kInvalidSocketValue) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (m_socket, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + } + return 0; +} + +std::string +TCPSocket::GetLocalIPAddress() const +{ + // We bound to port zero, so we need to figure out which port we actually bound to + if (m_socket != kInvalidSocketValue) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (m_socket, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetIPAddress (); + } + return ""; +} + +uint16_t +TCPSocket::GetRemotePortNumber() const +{ + if (m_socket != kInvalidSocketValue) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getpeername (m_socket, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + } + return 0; +} + +std::string +TCPSocket::GetRemoteIPAddress () const +{ + // We bound to port zero, so we need to figure out which port we actually bound to + if (m_socket != kInvalidSocketValue) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getpeername (m_socket, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetIPAddress (); + } + return ""; +} + +Error +TCPSocket::Connect(llvm::StringRef name) +{ + if (m_socket == kInvalidSocketValue) + return Error("Invalid socket"); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); + if (log) + log->Printf ("TCPSocket::%s (host/port = %s)", __FUNCTION__, name.data()); + + Error error; + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (name, host_str, port_str, port, &error)) + return error; + + // Enable local address reuse + SetOptionReuseAddress(); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof (sa)); + sa.sin_family = kDomain; + sa.sin_port = htons (port); + + int inet_pton_result = ::inet_pton (kDomain, host_str.c_str(), &sa.sin_addr); + + if (inet_pton_result <= 0) + { + struct hostent *host_entry = gethostbyname (host_str.c_str()); + if (host_entry) + host_str = ::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); + inet_pton_result = ::inet_pton (kDomain, host_str.c_str(), &sa.sin_addr); + if (inet_pton_result <= 0) + { + if (inet_pton_result == -1) + SetLastError(error); + else + error.SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str()); + + return error; + } + } + + if (-1 == ::connect (GetNativeSocket(), (const struct sockaddr *)&sa, sizeof(sa))) + { + SetLastError (error); + return error; + } + + // Keep our TCP packets coming without any delays. + SetOptionNoDelay(); + error.Clear(); + return error; +} + +Error +TCPSocket::Listen(llvm::StringRef name, int backlog) +{ + Error error; + + // enable local address reuse + SetOptionReuseAddress(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("TCPSocket::%s (%s)", __FUNCTION__, name.data()); + + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (name, host_str, port_str, port, &error)) + return error; + + SocketAddress bind_addr; + + // Only bind to the loopback address if we are expecting a connection from + // localhost to avoid any firewall issues. + const bool bind_addr_success = (host_str == "127.0.0.1") ? + bind_addr.SetToLocalhost (kDomain, port) : + bind_addr.SetToAnyAddress (kDomain, port); + + if (!bind_addr_success) + { + error.SetErrorString("Failed to bind port"); + return error; + } + + int err = ::bind (GetNativeSocket(), bind_addr, bind_addr.GetLength()); + if (err != -1) + err = ::listen (GetNativeSocket(), backlog); + + if (err == -1) + SetLastError (error); + + return error; +} + +Error +TCPSocket::Accept(llvm::StringRef name, bool child_processes_inherit, Socket *&conn_socket) +{ + Error error; + std::string host_str; + std::string port_str; + int32_t port; + if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) + return error; + + const sa_family_t family = kDomain; + const int socktype = kType; + const int protocol = IPPROTO_TCP; + SocketAddress listen_addr; + if (host_str.empty()) + listen_addr.SetToLocalhost(family, port); + else if (host_str.compare("*") == 0) + listen_addr.SetToAnyAddress(family, port); + else + { + if (!listen_addr.getaddrinfo(host_str.c_str(), port_str.c_str(), family, socktype, protocol)) + { + error.SetErrorStringWithFormat("unable to resolve hostname '%s'", host_str.c_str()); + return error; + } + } + + bool accept_connection = false; + std::unique_ptr<TCPSocket> accepted_socket; + + // Loop until we are happy with our connection + while (!accept_connection) + { + struct sockaddr_in accept_addr; + ::memset (&accept_addr, 0, sizeof accept_addr); +#if !(defined (__linux__) || defined(_WIN32)) + accept_addr.sin_len = sizeof accept_addr; +#endif + socklen_t accept_addr_len = sizeof accept_addr; + + int sock = AcceptSocket (GetNativeSocket(), + (struct sockaddr *)&accept_addr, + &accept_addr_len, + child_processes_inherit, + error); + + if (error.Fail()) + break; + + bool is_same_addr = true; +#if !(defined(__linux__) || (defined(_WIN32))) + is_same_addr = (accept_addr_len == listen_addr.sockaddr_in().sin_len); +#endif + if (is_same_addr) + is_same_addr = (accept_addr.sin_addr.s_addr == listen_addr.sockaddr_in().sin_addr.s_addr); + + if (is_same_addr || (listen_addr.sockaddr_in().sin_addr.s_addr == INADDR_ANY)) + { + accept_connection = true; + accepted_socket.reset(new TCPSocket(sock, true)); + } + else + { + const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; + const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sockaddr_in().sin_addr.s_addr; + ::fprintf (stderr, "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n", + accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], + listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); + accepted_socket.reset(); + } + } + + if (!accepted_socket) + return error; + + // Keep our TCP packets coming without any delays. + accepted_socket->SetOptionNoDelay(); + error.Clear(); + conn_socket = accepted_socket.release(); + return error; +} + +int +TCPSocket::SetOptionNoDelay() +{ + return SetOption (IPPROTO_TCP, TCP_NODELAY, 1); +} + +int +TCPSocket::SetOptionReuseAddress() +{ + return SetOption(SOL_SOCKET, SO_REUSEADDR, 1); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/Terminal.cpp b/contrib/llvm/tools/lldb/source/Host/common/Terminal.cpp new file mode 100644 index 0000000..9f3abb7 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/Terminal.cpp @@ -0,0 +1,323 @@ +//===-- Terminal.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/Terminal.h" +#include "llvm/ADT/STLExtras.h" + +#include <fcntl.h> +#include <signal.h> + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +#include <termios.h> +#endif + + +using namespace lldb_private; + +bool +Terminal::IsATerminal () const +{ + + return m_fd >= 0 && ::isatty (m_fd); +} + + +bool +Terminal::SetEcho (bool enabled) +{ + if (FileDescriptorIsValid()) + { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (IsATerminal ()) + { + struct termios fd_termios; + if (::tcgetattr(m_fd, &fd_termios) == 0) + { + bool set_corectly = false; + if (enabled) + { + if (fd_termios.c_lflag & ECHO) + set_corectly = true; + else + fd_termios.c_lflag |= ECHO; + } + else + { + if (fd_termios.c_lflag & ECHO) + fd_termios.c_lflag &= ~ECHO; + else + set_corectly = true; + } + + if (set_corectly) + return true; + return ::tcsetattr (m_fd, TCSANOW, &fd_termios) == 0; + } + } +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + } + return false; +} + +bool +Terminal::SetCanonical (bool enabled) +{ + if (FileDescriptorIsValid()) + { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (IsATerminal ()) + { + struct termios fd_termios; + if (::tcgetattr(m_fd, &fd_termios) == 0) + { + bool set_corectly = false; + if (enabled) + { + if (fd_termios.c_lflag & ICANON) + set_corectly = true; + else + fd_termios.c_lflag |= ICANON; + } + else + { + if (fd_termios.c_lflag & ICANON) + fd_termios.c_lflag &= ~ICANON; + else + set_corectly = true; + } + + if (set_corectly) + return true; + return ::tcsetattr (m_fd, TCSANOW, &fd_termios) == 0; + } + } +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + } + return false; +} + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +TerminalState::TerminalState() : + m_tty(), + m_tflags(-1), +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + m_termios_ap(), +#endif + m_process_group(-1) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TerminalState::~TerminalState() +{ +} + +void +TerminalState::Clear () +{ + m_tty.Clear(); + m_tflags = -1; +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + m_termios_ap.reset(); +#endif + m_process_group = -1; +} + +//---------------------------------------------------------------------- +// Save the current state of the TTY for the file descriptor "fd" +// and if "save_process_group" is true, attempt to save the process +// group info for the TTY. +//---------------------------------------------------------------------- +bool +TerminalState::Save (int fd, bool save_process_group) +{ + m_tty.SetFileDescriptor(fd); + if (m_tty.IsATerminal()) + { +#ifndef LLDB_DISABLE_POSIX + m_tflags = ::fcntl (fd, F_GETFL, 0); +#endif +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (m_termios_ap.get() == NULL) + m_termios_ap.reset (new struct termios); + int err = ::tcgetattr (fd, m_termios_ap.get()); + if (err != 0) + m_termios_ap.reset(); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +#ifndef LLDB_DISABLE_POSIX + if (save_process_group) + m_process_group = ::tcgetpgrp (0); + else + m_process_group = -1; +#endif + } + else + { + m_tty.Clear(); + m_tflags = -1; +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + m_termios_ap.reset(); +#endif + m_process_group = -1; + } + return IsValid(); +} + +//---------------------------------------------------------------------- +// Restore the state of the TTY using the cached values from a +// previous call to Save(). +//---------------------------------------------------------------------- +bool +TerminalState::Restore () const +{ +#ifndef LLDB_DISABLE_POSIX + if (IsValid()) + { + const int fd = m_tty.GetFileDescriptor(); + if (TFlagsIsValid()) + fcntl (fd, F_SETFL, m_tflags); + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (TTYStateIsValid()) + tcsetattr (fd, TCSANOW, m_termios_ap.get()); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + + if (ProcessGroupIsValid()) + { + // Save the original signal handler. + void (*saved_sigttou_callback) (int) = NULL; + saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN); + // Set the process group + tcsetpgrp (fd, m_process_group); + // Restore the original signal handler. + signal (SIGTTOU, saved_sigttou_callback); + } + return true; + } +#endif + return false; +} + + + + +//---------------------------------------------------------------------- +// Returns true if this object has valid saved TTY state settings +// that can be used to restore a previous state. +//---------------------------------------------------------------------- +bool +TerminalState::IsValid() const +{ + return m_tty.FileDescriptorIsValid () && (TFlagsIsValid() || TTYStateIsValid()); +} + +//---------------------------------------------------------------------- +// Returns true if m_tflags is valid +//---------------------------------------------------------------------- +bool +TerminalState::TFlagsIsValid() const +{ + return m_tflags != -1; +} + +//---------------------------------------------------------------------- +// Returns true if m_ttystate is valid +//---------------------------------------------------------------------- +bool +TerminalState::TTYStateIsValid() const +{ +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + return m_termios_ap.get() != 0; +#else + return false; +#endif +} + +//---------------------------------------------------------------------- +// Returns true if m_process_group is valid +//---------------------------------------------------------------------- +bool +TerminalState::ProcessGroupIsValid() const +{ + return static_cast<int32_t>(m_process_group) != -1; +} + +//------------------------------------------------------------------ +// Constructor +//------------------------------------------------------------------ +TerminalStateSwitcher::TerminalStateSwitcher () : + m_currentState(UINT32_MAX) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +TerminalStateSwitcher::~TerminalStateSwitcher () +{ +} + +//------------------------------------------------------------------ +// Returns the number of states that this switcher contains +//------------------------------------------------------------------ +uint32_t +TerminalStateSwitcher::GetNumberOfStates() const +{ + return llvm::array_lengthof(m_ttystates); +} + +//------------------------------------------------------------------ +// Restore the state at index "idx". +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TerminalStateSwitcher::Restore (uint32_t idx) const +{ + const uint32_t num_states = GetNumberOfStates(); + if (idx >= num_states) + return false; + + // See if we already are in this state? + if (m_currentState < num_states && (idx == m_currentState) && m_ttystates[idx].IsValid()) + return true; + + // Set the state to match the index passed in and only update the + // current state if there are no errors. + if (m_ttystates[idx].Restore()) + { + m_currentState = idx; + return true; + } + + // We failed to set the state. The tty state was invalid or not + // initialized. + return false; +} + +//------------------------------------------------------------------ +// Save the state at index "idx" for file descriptor "fd" and +// save the process group if requested. +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TerminalStateSwitcher::Save (uint32_t idx, int fd, bool save_process_group) +{ + const uint32_t num_states = GetNumberOfStates(); + if (idx < num_states) + return m_ttystates[idx].Save(fd, save_process_group); + return false; +} + + diff --git a/contrib/llvm/tools/lldb/source/Host/common/ThisThread.cpp b/contrib/llvm/tools/lldb/source/Host/common/ThisThread.cpp new file mode 100644 index 0000000..7637014 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/ThisThread.cpp @@ -0,0 +1,52 @@ +//===-- ThisThread.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/Core/Error.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/STLExtras.h" + +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name, int max_length) +{ + std::string truncated_name(name.data()); + + // Thread names are coming in like '<lldb.comm.debugger.edit>' and + // '<lldb.comm.debugger.editline>'. So just chopping the end of the string + // off leads to a lot of similar named threads. Go through the thread name + // and search for the last dot and use that. + + if (max_length > 0 && truncated_name.length() > static_cast<size_t>(max_length)) + { + // First see if we can get lucky by removing any initial or final braces. + std::string::size_type begin = truncated_name.find_first_not_of("(<"); + std::string::size_type end = truncated_name.find_last_not_of(")>."); + if (end - begin > static_cast<size_t>(max_length)) + { + // We're still too long. Since this is a dotted component, use everything after the last + // dot, up to a maximum of |length| characters. + std::string::size_type last_dot = truncated_name.rfind('.'); + if (last_dot != std::string::npos) + begin = last_dot + 1; + + end = std::min(end, begin + max_length); + } + + std::string::size_type count = end - begin + 1; + truncated_name = truncated_name.substr(begin, count); + } + + SetName(truncated_name.c_str()); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/ThreadLauncher.cpp b/contrib/llvm/tools/lldb/source/Host/common/ThreadLauncher.cpp new file mode 100644 index 0000000..c19a233 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/ThreadLauncher.cpp @@ -0,0 +1,84 @@ +//===-- ThreadLauncher.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// lldb Includes +#include "lldb/Core/Log.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" + +#if defined(_WIN32) +#include "lldb/Host/windows/windows.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +HostThread +ThreadLauncher::LaunchThread(llvm::StringRef name, lldb::thread_func_t thread_function, lldb::thread_arg_t thread_arg, Error *error_ptr, size_t min_stack_byte_size) +{ + Error error; + if (error_ptr) + error_ptr->Clear(); + + // Host::ThreadCreateTrampoline will delete this pointer for us. + HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo(name.data(), thread_function, thread_arg); + lldb::thread_t thread; +#ifdef _WIN32 + thread = + (lldb::thread_t)::_beginthreadex(0, (unsigned)min_stack_byte_size, HostNativeThread::ThreadCreateTrampoline, info_ptr, 0, NULL); + if (thread == (lldb::thread_t)(-1L)) + error.SetError(::GetLastError(), eErrorTypeWin32); +#else + + + // ASAN instrumentation adds a lot of bookkeeping overhead on stack frames. +#if __has_feature(address_sanitizer) + const size_t eight_megabytes = 8 * 1024 * 1024; + if (min_stack_byte_size < eight_megabytes) + { + min_stack_byte_size += eight_megabytes; + } +#endif + + pthread_attr_t *thread_attr_ptr = NULL; + pthread_attr_t thread_attr; + bool destroy_attr = false; + if (min_stack_byte_size > 0) + { + if (::pthread_attr_init (&thread_attr) == 0) + { + destroy_attr = true; + size_t default_min_stack_byte_size = 0; + if (::pthread_attr_getstacksize(&thread_attr, &default_min_stack_byte_size) == 0) + { + if (default_min_stack_byte_size < min_stack_byte_size) + { + if (::pthread_attr_setstacksize (&thread_attr, min_stack_byte_size) == 0) + thread_attr_ptr = &thread_attr; + } + } + + } + } + int err = ::pthread_create(&thread, thread_attr_ptr, HostNativeThread::ThreadCreateTrampoline, info_ptr); + + if (destroy_attr) + ::pthread_attr_destroy(&thread_attr); + + error.SetError(err, eErrorTypePOSIX); +#endif + if (error_ptr) + *error_ptr = error; + if (!error.Success()) + thread = LLDB_INVALID_HOST_THREAD; + + return HostThread(thread); +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/TimeValue.cpp b/contrib/llvm/tools/lldb/source/Host/common/TimeValue.cpp new file mode 100644 index 0000000..b471a3d --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/TimeValue.cpp @@ -0,0 +1,222 @@ +//===-- TimeValue.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/TimeValue.h" +#include "lldb/Host/Config.h" + +// C Includes +#include <stddef.h> +#include <time.h> +#include <cstring> + +#ifdef _MSC_VER +#include "lldb/Host/windows/windows.h" +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// TimeValue constructor +//---------------------------------------------------------------------- +TimeValue::TimeValue() : + m_nano_seconds (0) +{ +} + +//---------------------------------------------------------------------- +// TimeValue copy constructor +//---------------------------------------------------------------------- +TimeValue::TimeValue(const TimeValue& rhs) : + m_nano_seconds (rhs.m_nano_seconds) +{ +} + +TimeValue::TimeValue(const struct timespec& ts) : + m_nano_seconds ((uint64_t) ts.tv_sec * NanoSecPerSec + ts.tv_nsec) +{ +} + +TimeValue::TimeValue(uint32_t seconds, uint32_t nanos) : + m_nano_seconds((uint64_t) seconds * NanoSecPerSec + nanos) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TimeValue::~TimeValue() +{ +} + + +uint64_t +TimeValue::GetAsNanoSecondsSinceJan1_1970() const +{ + return m_nano_seconds; +} + +uint64_t +TimeValue::GetAsMicroSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NanoSecPerMicroSec; +} + +uint64_t +TimeValue::GetAsSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NanoSecPerSec; +} + + + +struct timespec +TimeValue::GetAsTimeSpec () const +{ + struct timespec ts; + ts.tv_sec = m_nano_seconds / NanoSecPerSec; + ts.tv_nsec = m_nano_seconds % NanoSecPerSec; + return ts; +} + +void +TimeValue::Clear () +{ + m_nano_seconds = 0; +} + +bool +TimeValue::IsValid () const +{ + return m_nano_seconds != 0; +} + +void +TimeValue::OffsetWithSeconds (uint64_t sec) +{ + m_nano_seconds += sec * NanoSecPerSec; +} + +void +TimeValue::OffsetWithMicroSeconds (uint64_t usec) +{ + m_nano_seconds += usec * NanoSecPerMicroSec; +} + +void +TimeValue::OffsetWithNanoSeconds (uint64_t nsec) +{ + m_nano_seconds += nsec; +} + +TimeValue +TimeValue::Now() +{ + uint32_t seconds, nanoseconds; +#if _MSC_VER + SYSTEMTIME st; + GetSystemTime(&st); + nanoseconds = st.wMilliseconds * 1000000; + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + + seconds = ((((uint64_t)ft.dwHighDateTime) << 32 | ft.dwLowDateTime) / 10000000) - 11644473600ULL; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + seconds = tv.tv_sec; + nanoseconds = tv.tv_usec * NanoSecPerMicroSec; +#endif + TimeValue now(seconds, nanoseconds); + return now; +} + +//---------------------------------------------------------------------- +// TimeValue assignment operator +//---------------------------------------------------------------------- +const TimeValue& +TimeValue::operator=(const TimeValue& rhs) +{ + m_nano_seconds = rhs.m_nano_seconds; + return *this; +} + +void +TimeValue::Dump (Stream *s, uint32_t width) const +{ + if (s == NULL) + return; + +#ifndef LLDB_DISABLE_POSIX + char time_buf[32]; + time_t time = GetAsSecondsSinceJan1_1970(); + char *time_cstr = ::ctime_r(&time, time_buf); + if (time_cstr) + { + char *newline = ::strpbrk(time_cstr, "\n\r"); + if (newline) + *newline = '\0'; + if (width > 0) + s->Printf("%-*s", width, time_cstr); + else + s->PutCString(time_cstr); + } + else if (width > 0) + s->Printf("%-*s", width, ""); +#endif +} + +bool +lldb_private::operator == (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() == rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator != (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() != rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator < (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() < rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator <= (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() <= rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator > (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() > rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator >= (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() >= rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +uint64_t +lldb_private::operator - (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() - rhs.GetAsNanoSecondsSinceJan1_1970(); +} + + diff --git a/contrib/llvm/tools/lldb/source/Host/common/UDPSocket.cpp b/contrib/llvm/tools/lldb/source/Host/common/UDPSocket.cpp new file mode 100644 index 0000000..8297232 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/UDPSocket.cpp @@ -0,0 +1,158 @@ +//===-- UdpSocket.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/common/UDPSocket.h" + +#include "lldb/Core/Log.h" +#include "lldb/Host/Config.h" + +#ifndef LLDB_DISABLE_POSIX +#include <arpa/inet.h> +#include <sys/socket.h> +#endif + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +const int kDomain = AF_INET; +const int kType = SOCK_DGRAM; + +const Error kNotSupported("Not supported"); + +} + +UDPSocket::UDPSocket(NativeSocket socket) + : Socket(socket, ProtocolUdp, true) +{ +} + +UDPSocket::UDPSocket(bool child_processes_inherit, Error &error) + : UDPSocket(CreateSocket(kDomain, kType, 0, child_processes_inherit, error)) +{ +} + +size_t +UDPSocket::Send(const void *buf, const size_t num_bytes) +{ + return ::sendto (m_socket, + static_cast<const char*>(buf), + num_bytes, + 0, + m_send_sockaddr, + m_send_sockaddr.GetLength()); +} + +Error +UDPSocket::Connect(llvm::StringRef name) +{ + return kNotSupported; +} + +Error +UDPSocket::Listen(llvm::StringRef name, int backlog) +{ + return kNotSupported; +} + +Error +UDPSocket::Accept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) +{ + return kNotSupported; +} + +Error +UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit, Socket *&send_socket, Socket *&recv_socket) +{ + std::unique_ptr<UDPSocket> final_send_socket; + std::unique_ptr<UDPSocket> final_recv_socket; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("UDPSocket::%s (host/port = %s)", __FUNCTION__, name.data()); + + Error error; + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (name, host_str, port_str, port, &error)) + return error; + + // Setup the receiving end of the UDP connection on this localhost + // on port zero. After we bind to port zero we can read the port. + final_recv_socket.reset(new UDPSocket(child_processes_inherit, error)); + if (error.Success()) + { + // Socket was created, now lets bind to the requested port + SocketAddress addr; + addr.SetToAnyAddress (AF_INET, 0); + + if (::bind (final_recv_socket->GetNativeSocket(), addr, addr.GetLength()) == -1) + { + // Bind failed... + SetLastError (error); + } + } + + assert(error.Fail() == !(final_recv_socket && final_recv_socket->IsValid())); + if (error.Fail()) + return error; + + // At this point we have setup the receive port, now we need to + // setup the UDP send socket + + struct addrinfo hints; + struct addrinfo *service_info_list = nullptr; + + ::memset (&hints, 0, sizeof(hints)); + hints.ai_family = kDomain; + hints.ai_socktype = kType; + int err = ::getaddrinfo (host_str.c_str(), port_str.c_str(), &hints, &service_info_list); + if (err != 0) + { + error.SetErrorStringWithFormat("getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)", + host_str.c_str(), + port_str.c_str(), + err, + gai_strerror(err)); + return error; + } + + for (struct addrinfo *service_info_ptr = service_info_list; + service_info_ptr != nullptr; + service_info_ptr = service_info_ptr->ai_next) + { + auto send_fd = CreateSocket (service_info_ptr->ai_family, + service_info_ptr->ai_socktype, + service_info_ptr->ai_protocol, + child_processes_inherit, + error); + if (error.Success()) + { + final_send_socket.reset(new UDPSocket(send_fd)); + final_send_socket->m_send_sockaddr = service_info_ptr; + break; + } + else + continue; + } + + :: freeaddrinfo (service_info_list); + + if (!final_send_socket) + return error; + + send_socket = final_send_socket.release(); + recv_socket = final_recv_socket.release(); + error.Clear(); + return error; +} diff --git a/contrib/llvm/tools/lldb/source/Host/common/XML.cpp b/contrib/llvm/tools/lldb/source/Host/common/XML.cpp new file mode 100644 index 0000000..dc9cb0b --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/common/XML.cpp @@ -0,0 +1,691 @@ +//===-- 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 element 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 = 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/contrib/llvm/tools/lldb/source/Host/freebsd/Host.cpp b/contrib/llvm/tools/lldb/source/Host/freebsd/Host.cpp new file mode 100644 index 0000000..cf53cda --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/freebsd/Host.cpp @@ -0,0 +1,283 @@ +//===-- source/Host/freebsd/Host.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdio.h> +#include <dlfcn.h> +#include <execinfo.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/sysctl.h> +#include <sys/proc.h> + +#include <sys/ptrace.h> +#include <sys/exec.h> +#include <machine/elf.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Platform.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" + +#include "llvm/Support/Host.h" + +extern "C" { + extern char **environ; +} + +using namespace lldb; +using namespace lldb_private; + +size_t +Host::GetEnvironment (StringList &env) +{ + char *v; + char **var = environ; + for (; var != NULL && *var != NULL; ++var) + { + v = strchr(*var, (int)'-'); + if (v == NULL) + continue; + env.AppendString(v); + } + return env.GetSize(); +} + +static bool +GetFreeBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, (int)process_info.GetProcessID() }; + + char arg_data[8192]; + size_t arg_data_size = sizeof(arg_data); + if (::sysctl (mib, 4, arg_data, &arg_data_size , NULL, 0) == 0) + { + DataExtractor data (arg_data, arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + const char *cstr; + + cstr = data.GetCStr (&offset); + if (cstr) + { + process_info.GetExecutableFile().SetFile(cstr, false); + + if (!(match_info_ptr == NULL || + NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName()))) + return false; + + Args &proc_args = process_info.GetArguments(); + while (1) + { + const uint8_t *p = data.PeekData(offset, 1); + while ((p != NULL) && (*p == '\0') && offset < arg_data_size) + { + ++offset; + p = data.PeekData(offset, 1); + } + if (p == NULL || offset >= arg_data_size) + return true; + + cstr = data.GetCStr(&offset); + if (cstr) + proc_args.AppendArgument(cstr); + else + return true; + } + } + } + } + return false; +} + +static bool +GetFreeBSDProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + process_info.GetArchitecture() = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + return true; + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) +{ + struct kinfo_proc proc_kinfo; + size_t proc_kinfo_size; + + if (process_info.ProcessIDIsValid()) + { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, + (int)process_info.GetProcessID() }; + proc_kinfo_size = sizeof(struct kinfo_proc); + + if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) + { + if (proc_kinfo_size > 0) + { + process_info.SetParentProcessID (proc_kinfo.ki_ppid); + process_info.SetUserID (proc_kinfo.ki_ruid); + process_info.SetGroupID (proc_kinfo.ki_rgid); + process_info.SetEffectiveUserID (proc_kinfo.ki_uid); + if (proc_kinfo.ki_ngroups > 0) + process_info.SetEffectiveGroupID (proc_kinfo.ki_groups[0]); + else + process_info.SetEffectiveGroupID (UINT32_MAX); + return true; + } + } + } + process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); + process_info.SetUserID (UINT32_MAX); + process_info.SetGroupID (UINT32_MAX); + process_info.SetEffectiveUserID (UINT32_MAX); + process_info.SetEffectiveGroupID (UINT32_MAX); + return false; +} + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + std::vector<struct kinfo_proc> kinfos; + + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + + size_t pid_data_size = 0; + if (::sysctl (mib, 3, NULL, &pid_data_size, NULL, 0) != 0) + return 0; + + // Add a few extra in case a few more show up + const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; + + kinfos.resize (estimated_pid_count); + pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); + + if (::sysctl (mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0) + return 0; + + const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + + bool all_users = match_info.GetMatchAllUsers(); + const ::pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + for (size_t i = 0; i < actual_pid_count; i++) + { + const struct kinfo_proc &kinfo = kinfos[i]; + const bool kinfo_user_matches = (all_users || + (kinfo.ki_ruid == our_uid) || + // Special case, if lldb is being run as root we can attach to anything. + (our_uid == 0) + ); + + if (kinfo_user_matches == false || // Make sure the user is acceptable + kinfo.ki_pid == our_pid || // Skip this process + kinfo.ki_pid == 0 || // Skip kernel (kernel pid is zero) + kinfo.ki_stat == SZOMB || // Zombies are bad, they like brains... + kinfo.ki_flag & P_TRACED || // Being debugged? + kinfo.ki_flag & P_WEXIT) // Working on exiting + continue; + + // Every thread is a process in FreeBSD, but all the threads of a single process + // have the same pid. Do not store the process info in the result list if a process + // with given identifier is already registered there. + bool already_registered = false; + for (uint32_t pi = 0; + !already_registered && + (const int)kinfo.ki_numthreads > 1 && + pi < (const uint32_t)process_infos.GetSize(); pi++) + already_registered = (process_infos.GetProcessIDAtIndex(pi) == (uint32_t)kinfo.ki_pid); + + if (already_registered) + continue; + + ProcessInstanceInfo process_info; + process_info.SetProcessID (kinfo.ki_pid); + process_info.SetParentProcessID (kinfo.ki_ppid); + process_info.SetUserID (kinfo.ki_ruid); + process_info.SetGroupID (kinfo.ki_rgid); + process_info.SetEffectiveUserID (kinfo.ki_svuid); + process_info.SetEffectiveGroupID (kinfo.ki_svgid); + + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches (process_info) && + GetFreeBSDProcessArgs (&match_info, process_info)) + { + GetFreeBSDProcessCPUType (process_info); + if (match_info.Matches (process_info)) + process_infos.Append (process_info); + } + } + + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.SetProcessID(pid); + + if (GetFreeBSDProcessArgs(NULL, process_info)) + { + // should use libprocstat instead of going right into sysctl? + GetFreeBSDProcessCPUType(process_info); + GetFreeBSDProcessUserAndGroup(process_info); + return true; + } + + process_info.Clear(); + return false; +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_AUXV, 0 }; + size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo); + DataBufferSP buf_sp; + + std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(auxv_size, 0)); + + mib[3] = process->GetID(); + if (::sysctl(mib, 4, buf_ap->GetBytes(), &auxv_size, NULL, 0) == 0) { + buf_sp.reset(buf_ap.release()); + } else { + perror("sysctl failed on auxv"); + } + + return buf_sp; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + return Error("unimplemented"); +} + diff --git a/contrib/llvm/tools/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp b/contrib/llvm/tools/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp new file mode 100644 index 0000000..d511093 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp @@ -0,0 +1,91 @@ +//===-- HostInfoFreeBSD.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/freebsd/HostInfoFreeBSD.h" + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/utsname.h> + +using namespace lldb_private; + +uint32_t +HostInfoFreeBSD::GetMaxThreadNameLength() +{ + return 16; +} + +bool +HostInfoFreeBSD::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + struct utsname un; + + ::memset(&un, 0, sizeof(utsname)); + if (uname(&un) < 0) + return false; + + int status = sscanf(un.release, "%u.%u", &major, &minor); + return status == 2; +} + +bool +HostInfoFreeBSD::GetOSBuildString(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_OSREV}; + char osrev_str[12]; + uint32_t osrev = 0; + size_t osrev_len = sizeof(osrev); + + if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0) + { + ::snprintf(osrev_str, sizeof(osrev_str), "%-8.8u", osrev); + s.assign(osrev_str); + return true; + } + + s.clear(); + return false; +} + +bool +HostInfoFreeBSD::GetOSKernelDescription(std::string &s) +{ + struct utsname un; + + ::memset(&un, 0, sizeof(utsname)); + s.clear(); + + if (uname(&un) < 0) + return false; + + s.assign(un.version); + + return true; +} + +FileSpec +HostInfoFreeBSD::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + if (!g_program_filespec) + { + int exe_path_mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()}; + size_t exe_path_size; + if (sysctl(exe_path_mib, 4, NULL, &exe_path_size, NULL, 0) == 0) + { + char *exe_path = new char[exe_path_size]; + if (sysctl(exe_path_mib, 4, exe_path, &exe_path_size, NULL, 0) == 0) + g_program_filespec.SetFile(exe_path, false); + delete[] exe_path; + } + } + return g_program_filespec; +}
\ No newline at end of file diff --git a/contrib/llvm/tools/lldb/source/Host/freebsd/HostThreadFreeBSD.cpp b/contrib/llvm/tools/lldb/source/Host/freebsd/HostThreadFreeBSD.cpp new file mode 100644 index 0000000..a4302a9 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/freebsd/HostThreadFreeBSD.cpp @@ -0,0 +1,79 @@ +//===-- HostThreadFreeBSD.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// lldb Includes +#include "lldb/Host/freebsd/HostThreadFreeBSD.h" +#include "lldb/Host/Host.h" + +// 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> + +// C++ includes +#include <string> + +using namespace lldb_private; + +HostThreadFreeBSD::HostThreadFreeBSD() +{ +} + +HostThreadFreeBSD::HostThreadFreeBSD(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +void +HostThreadFreeBSD::GetName(lldb::tid_t tid, llvm::SmallVectorImpl<char> &name) +{ + name.clear(); + int pid = Host::GetCurrentProcessID(); + + struct kinfo_proc *kp = nullptr, *nkp; + size_t len = 0; + int error; + int ctl[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, (int)pid}; + + while (1) + { + error = sysctl(ctl, 4, kp, &len, nullptr, 0); + if (kp == nullptr || (error != 0 && errno == ENOMEM)) + { + // Add extra space in case threads are added before next call. + len += sizeof(*kp) + len / 10; + nkp = (struct kinfo_proc *)realloc(kp, len); + if (nkp == nullptr) + { + free(kp); + return; + } + kp = nkp; + continue; + } + if (error != 0) + len = 0; + break; + } + + for (size_t i = 0; i < len / sizeof(*kp); i++) + { + if (kp[i].ki_tid == (lwpid_t)tid) + { + name.append(kp[i].ki_tdname, kp[i].ki_tdname + strlen(kp[i].ki_tdname)); + break; + } + } + free(kp); +} diff --git a/contrib/llvm/tools/lldb/source/Host/freebsd/ThisThread.cpp b/contrib/llvm/tools/lldb/source/Host/freebsd/ThisThread.cpp new file mode 100644 index 0000000..e524fd4 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/freebsd/ThisThread.cpp @@ -0,0 +1,39 @@ +//===-- ThisThread.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/HostNativeThread.h" +#include "lldb/Host/ThisThread.h" + +#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/contrib/llvm/tools/lldb/source/Host/netbsd/Host.cpp b/contrib/llvm/tools/lldb/source/Host/netbsd/Host.cpp new file mode 100644 index 0000000..8742850 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/netbsd/Host.cpp @@ -0,0 +1,287 @@ +//===-- source/Host/netbsd/Host.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdio.h> +#include <dlfcn.h> +#include <execinfo.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/sysctl.h> +#include <sys/proc.h> + +#include <limits.h> + +#include <sys/ptrace.h> +#include <sys/exec.h> +#include <elf.h> +#include <kvm.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Platform.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" + +#include "llvm/Support/Host.h" + +extern "C" { + extern char **environ; +} + +using namespace lldb; +using namespace lldb_private; + +size_t +Host::GetEnvironment (StringList &env) +{ + char *v; + char **var = environ; + for (; var != NULL && *var != NULL; ++var) + { + v = ::strchr(*var, (int)'-'); + if (v == NULL) + continue; + env.AppendString(v); + } + return env.GetSize(); +} + +static bool +GetNetBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, + ProcessInstanceInfo &process_info) +{ + if (!process_info.ProcessIDIsValid()) + return false; + + int pid = process_info.GetProcessID(); + + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV }; + + char arg_data[8192]; + size_t arg_data_size = sizeof(arg_data); + if (::sysctl (mib, 4, arg_data, &arg_data_size , NULL, 0) != 0) + return false; + + DataExtractor data (arg_data, arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + const char *cstr; + + cstr = data.GetCStr (&offset); + if (!cstr) + return false; + + process_info.GetExecutableFile().SetFile(cstr, false); + + if (!(match_info_ptr == NULL || + NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), + match_info_ptr->GetNameMatchType(), + match_info_ptr->GetProcessInfo().GetName()))) + return false; + + Args &proc_args = process_info.GetArguments(); + while (1) + { + const uint8_t *p = data.PeekData(offset, 1); + while ((p != NULL) && (*p == '\0') && offset < arg_data_size) + { + ++offset; + p = data.PeekData(offset, 1); + } + if (p == NULL || offset >= arg_data_size) + break; + + cstr = data.GetCStr(&offset); + if (!cstr) + break; + + proc_args.AppendArgument(cstr); + } + + return true; +} + +static bool +GetNetBSDProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) + { + process_info.GetArchitecture() = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + return true; + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetNetBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) +{ + ::kvm_t *kdp; + char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */ + + struct ::kinfo_proc2 *proc_kinfo; + const int pid = process_info.GetProcessID(); + int nproc; + + if (!process_info.ProcessIDIsValid()) + goto error; + + if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) + goto error; + + if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_PID, pid, + sizeof(struct ::kinfo_proc2), + &nproc)) == NULL) { + ::kvm_close(kdp); + goto error; + } + + if (nproc < 1) { + ::kvm_close(kdp); /* XXX: we don't check for error here */ + goto error; + } + + process_info.SetParentProcessID (proc_kinfo->p_ppid); + process_info.SetUserID (proc_kinfo->p_ruid); + process_info.SetGroupID (proc_kinfo->p_rgid); + process_info.SetEffectiveUserID (proc_kinfo->p_uid); + process_info.SetEffectiveGroupID (proc_kinfo->p_gid); + + ::kvm_close(kdp); /* XXX: we don't check for error here */ + + return true; + +error: + process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); + process_info.SetUserID (UINT32_MAX); + process_info.SetGroupID (UINT32_MAX); + process_info.SetEffectiveUserID (UINT32_MAX); + process_info.SetEffectiveGroupID (UINT32_MAX); + return false; +} + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + const ::pid_t our_pid = ::getpid(); + const ::uid_t our_uid = ::getuid(); + + const bool all_users = match_info.GetMatchAllUsers() || + // Special case, if lldb is being run as root we can attach to anything + (our_uid == 0); + + kvm_t *kdp; + char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */ + if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) + return 0; + + struct ::kinfo_proc2 *proc_kinfo; + int nproc; + if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_ALL, 0, + sizeof(struct ::kinfo_proc2), + &nproc)) == NULL) { + ::kvm_close(kdp); + return 0; + } + + for (int i = 0; i < nproc; i++) { + if (proc_kinfo[i].p_pid < 1) + continue; /* not valid */ + /* Make sure the user is acceptable */ + if (!all_users && proc_kinfo[i].p_ruid != our_uid) + continue; + + if (proc_kinfo[i].p_pid == our_pid || // Skip this process + proc_kinfo[i].p_pid == 0 || // Skip kernel (kernel pid is 0) + proc_kinfo[i].p_stat == LSZOMB || // Zombies are bad + proc_kinfo[i].p_flag & P_TRACED || // Being debugged? + proc_kinfo[i].p_flag & P_WEXIT) // Working on exiting + continue; + + + // Every thread is a process in NetBSD, but all the threads of a single + // process have the same pid. Do not store the process info in the + // result list if a process with given identifier is already registered + // there. + if (proc_kinfo[i].p_nlwps > 1) { + bool already_registered = false; + for (size_t pi = 0; pi < process_infos.GetSize(); pi++) { + if (process_infos.GetProcessIDAtIndex(pi) == + proc_kinfo[i].p_pid) { + already_registered = true; + break; + } + } + + if (already_registered) + continue; + } + ProcessInstanceInfo process_info; + process_info.SetProcessID (proc_kinfo[i].p_pid); + process_info.SetParentProcessID (proc_kinfo[i].p_ppid); + process_info.SetUserID (proc_kinfo[i].p_ruid); + process_info.SetGroupID (proc_kinfo[i].p_rgid); + process_info.SetEffectiveUserID (proc_kinfo[i].p_uid); + process_info.SetEffectiveGroupID (proc_kinfo[i].p_gid); + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches (process_info) && + GetNetBSDProcessArgs (&match_info, process_info)) + { + GetNetBSDProcessCPUType (process_info); + if (match_info.Matches (process_info)) + process_infos.Append (process_info); + } + } + + kvm_close(kdp); /* XXX: we don't check for error here */ + + return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.SetProcessID(pid); + + if (GetNetBSDProcessArgs(NULL, process_info)) + { + GetNetBSDProcessCPUType(process_info); + GetNetBSDProcessUserAndGroup(process_info); + return true; + } + + process_info.Clear(); + return false; +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + return lldb::DataBufferSP(); +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ + return Error("unimplemented"); +} diff --git a/contrib/llvm/tools/lldb/source/Host/netbsd/HostInfoNetBSD.cpp b/contrib/llvm/tools/lldb/source/Host/netbsd/HostInfoNetBSD.cpp new file mode 100644 index 0000000..aadda76 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/netbsd/HostInfoNetBSD.cpp @@ -0,0 +1,112 @@ +//===-- HostInfoNetBSD.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/netbsd/HostInfoNetBSD.h" + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <pthread.h> +#include <inttypes.h> + + +using namespace lldb_private; + +uint32_t +HostInfoNetBSD::GetMaxThreadNameLength() +{ + return PTHREAD_MAX_NAMELEN_NP; +} + +bool +HostInfoNetBSD::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ + struct utsname un; + + ::memset(&un, 0, sizeof(un)); + if (::uname(&un) < 0) + return false; + + /* Accept versions like 7.99.21 and 6.1_STABLE */ + int status = ::sscanf(un.release, "%" PRIu32 ".%" PRIu32 ".%" PRIu32, &major, &minor, &update); + switch(status) { + case 0: + return false; + case 1: + minor = 0; + /* FALLTHROUGH */ + case 2: + update = 0; + /* FALLTHROUGH */ + case 3: + default: + return true; + } +} + +bool +HostInfoNetBSD::GetOSBuildString(std::string &s) +{ + int mib[2] = {CTL_KERN, KERN_OSREV}; + char osrev_str[12]; + int osrev = 0; + size_t osrev_len = sizeof(osrev); + + if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0) + { + ::snprintf(osrev_str, sizeof(osrev_str), "%-10.10d", osrev); + s.assign(osrev_str); + return true; + } + + s.clear(); + return false; +} + +bool +HostInfoNetBSD::GetOSKernelDescription(std::string &s) +{ + struct utsname un; + + ::memset(&un, 0, sizeof(un)); + s.clear(); + + if (::uname(&un) < 0) + return false; + + s.assign(un.version); + + return true; +} + +FileSpec +HostInfoNetBSD::GetProgramFileSpec() +{ + static FileSpec g_program_filespec; + + if (!g_program_filespec) + { + ssize_t len; + static char buf[PATH_MAX]; + char name[PATH_MAX]; + + ::snprintf(name, PATH_MAX, "/proc/%d/exe", ::getpid()); + len = ::readlink(name, buf, PATH_MAX - 1); + if (len != -1) + { + buf[len] = '\0'; + g_program_filespec.SetFile(buf, false); + } + } + return g_program_filespec; +} diff --git a/contrib/llvm/tools/lldb/source/Host/netbsd/HostThreadNetBSD.cpp b/contrib/llvm/tools/lldb/source/Host/netbsd/HostThreadNetBSD.cpp new file mode 100644 index 0000000..06bc502 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/netbsd/HostThreadNetBSD.cpp @@ -0,0 +1,50 @@ +//===-- HostThreadNetBSD.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// lldb Includes +#include "lldb/Host/netbsd/HostThreadNetBSD.h" +#include "lldb/Host/Host.h" + +// C includes +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <sys/sysctl.h> +#include <sys/user.h> + +// C++ includes +#include <string> + +using namespace lldb_private; + +HostThreadNetBSD::HostThreadNetBSD() +{ +} + +HostThreadNetBSD::HostThreadNetBSD(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +void +HostThreadNetBSD::SetName(lldb::thread_t thread, llvm::StringRef &name) +{ + ::pthread_setname_np(thread, "%s", const_cast<char*>(name.data())); +} + +void +HostThreadNetBSD::GetName(lldb::thread_t thread, llvm::SmallVectorImpl<char> &name) +{ + char buf[PTHREAD_MAX_NAMELEN_NP]; + ::pthread_getname_np(thread, buf, PTHREAD_MAX_NAMELEN_NP); + + name.clear(); + name.append(buf, buf + strlen(buf)); +} diff --git a/contrib/llvm/tools/lldb/source/Host/netbsd/ThisThread.cpp b/contrib/llvm/tools/lldb/source/Host/netbsd/ThisThread.cpp new file mode 100644 index 0000000..dff5d9e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/netbsd/ThisThread.cpp @@ -0,0 +1,30 @@ +//===-- ThisThread.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/HostNativeThread.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/SmallVector.h" + +#include <pthread.h> +#include <string.h> + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ + HostNativeThread::SetName(::pthread_self(), name); +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ + HostNativeThread::GetName(::pthread_self(), name); +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp new file mode 100644 index 0000000..dbbd5a1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -0,0 +1,914 @@ +//===-- ConnectionFileDescriptorPosix.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/IOObject.h" +#include "lldb/Host/SocketAddress.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/StringConvert.h" + +// C Includes +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#ifndef LLDB_DISABLE_POSIX +#include <termios.h> +#endif + +// C++ Includes +#include <sstream> + +// Other libraries and framework includes +#include "llvm/Support/ErrorHandling.h" +#if defined(__APPLE__) +#include "llvm/ADT/SmallVector.h" +#endif +// Project includes +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +const char* ConnectionFileDescriptor::LISTEN_SCHEME = "listen"; +const char* ConnectionFileDescriptor::ACCEPT_SCHEME = "accept"; +const char* ConnectionFileDescriptor::UNIX_ACCEPT_SCHEME = "unix-accept"; +const char* ConnectionFileDescriptor::CONNECT_SCHEME = "connect"; +const char* ConnectionFileDescriptor::TCP_CONNECT_SCHEME = "tcp-connect"; +const char* ConnectionFileDescriptor::UDP_SCHEME = "udp"; +const char* ConnectionFileDescriptor::UNIX_CONNECT_SCHEME = "unix-connect"; +const char* ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME = "unix-abstract-connect"; +const char* ConnectionFileDescriptor::FD_SCHEME = "fd"; +const char* ConnectionFileDescriptor::FILE_SCHEME = "file"; + +namespace { + +const char* +GetURLAddress(const char *url, const char *scheme) +{ + const auto prefix = std::string(scheme) + "://"; + if (strstr(url, prefix.c_str()) != url) + return nullptr; + + return url + prefix.size(); +} + +} + +ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit) + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) + , m_child_processes_inherit(child_processes_inherit) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", static_cast<void *>(this)); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) + , m_child_processes_inherit(false) +{ + m_write_sp.reset(new File(fd, owns_fd)); + m_read_sp.reset(new File(fd, false)); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", static_cast<void *>(this), fd, + 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)); + if (log) + log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", static_cast<void *>(this)); + Disconnect(NULL); + CloseCommandPipe(); +} + +void +ConnectionFileDescriptor::OpenCommandPipe() +{ + CloseCommandPipe(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + // Make the command file descriptor here: + Error result = m_pipe.CreateNew(m_child_processes_inherit); + if (!result.Success()) + { + if (log) + log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", static_cast<void *>(this), + result.AsCString()); + } + else + { + if (log) + log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", static_cast<void *>(this), + m_pipe.GetReadFileDescriptor(), m_pipe.GetWriteFileDescriptor()); + } +} + +void +ConnectionFileDescriptor::CloseCommandPipe() +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()", static_cast<void *>(this)); + + m_pipe.Close(); +} + +bool +ConnectionFileDescriptor::IsConnected() const +{ + return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid()); +} + +ConnectionStatus +ConnectionFileDescriptor::Connect(const char *s, Error *error_ptr) +{ + Mutex::Locker locker(m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')", static_cast<void *>(this), s); + + OpenCommandPipe(); + + if (s && s[0]) + { + const char *addr = nullptr; + if ((addr = GetURLAddress(s, LISTEN_SCHEME))) + { + // listen://HOST:PORT + return SocketListenAndAccept(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, ACCEPT_SCHEME))) + { + // unix://SOCKNAME + return NamedSocketAccept(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, UNIX_ACCEPT_SCHEME))) + { + // unix://SOCKNAME + return NamedSocketAccept(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, CONNECT_SCHEME))) + { + return ConnectTCP(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, TCP_CONNECT_SCHEME))) + { + return ConnectTCP(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, UDP_SCHEME))) + { + return ConnectUDP(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, UNIX_CONNECT_SCHEME))) + { + // unix-connect://SOCKNAME + return NamedSocketConnect(addr, error_ptr); + } + else if ((addr = GetURLAddress(s, UNIX_ABSTRACT_CONNECT_SCHEME))) + { + // unix-abstract-connect://SOCKNAME + return UnixAbstractSocketConnect(addr, error_ptr); + } +#ifndef LLDB_DISABLE_POSIX + else if ((addr = GetURLAddress(s, FD_SCHEME))) + { + // Just passing a native file descriptor within this current process + // that is already opened (possibly from a service or other source). + bool success = false; + int fd = StringConvert::ToSInt32(addr, -1, 0, &success); + + if (success) + { + // We have what looks to be a valid file descriptor, but we + // should make sure it is. We currently are doing this by trying to + // get the flags from the file descriptor and making sure it + // isn't a bad fd. + errno = 0; + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags == -1 || errno == EBADF) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", s); + m_read_sp.reset(); + m_write_sp.reset(); + return eConnectionStatusError; + } + else + { + // Don't take ownership of a file descriptor that gets passed + // to us since someone else opened the file descriptor and + // handed it to us. + // TODO: Since are using a URL to open connection we should + // eventually parse options using the web standard where we + // have "fd://123?opt1=value;opt2=value" and we can have an + // option be "owns=1" or "owns=0" or something like this to + // allow us to specify this. For now, we assume we must + // assume we don't own it. + + std::unique_ptr<TCPSocket> tcp_socket; + tcp_socket.reset(new TCPSocket(fd, false)); + // Try and get a socket option from this file descriptor to + // see if this is a socket and set m_is_socket accordingly. + int resuse; + bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); + if (is_socket) + { + m_read_sp = std::move(tcp_socket); + m_write_sp = m_read_sp; + } + else + { + m_read_sp.reset(new File(fd, false)); + m_write_sp.reset(new File(fd, false)); + } + m_uri.assign(addr); + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"", s); + m_read_sp.reset(); + m_write_sp.reset(); + return eConnectionStatusError; + } + else if ((addr = GetURLAddress(s, FILE_SCHEME))) + { + // file:///PATH + const char *path = addr; + int fd = -1; + do + { + fd = ::open(path, O_RDWR); + } while (fd == -1 && errno == EINTR); + + if (fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + if (::isatty(fd)) + { + // Set up serial terminal emulation + struct termios options; + ::tcgetattr(fd, &options); + + // Set port speed to maximum + ::cfsetospeed(&options, B115200); + ::cfsetispeed(&options, B115200); + + // Raw input, disable echo and signals + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + // Make sure only one character is needed to return from a read + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + + ::tcsetattr(fd, TCSANOW, &options); + } + + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl(fd, F_SETFL, flags); + } + } + m_read_sp.reset(new File(fd, true)); + m_write_sp.reset(new File(fd, false)); + return eConnectionStatusSuccess; + } +#endif + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +bool +ConnectionFileDescriptor::InterruptRead() +{ + size_t bytes_written = 0; + Error result = m_pipe.Write("i", 1, bytes_written); + return result.Success(); +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect(Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::Disconnect ()", static_cast<void *>(this)); + + ConnectionStatus status = eConnectionStatusSuccess; + + if (!IsConnected()) + { + if (log) + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", static_cast<void *>(this)); + return eConnectionStatusSuccess; + } + + if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + static_cast<Socket &>(*m_read_sp).PreDisconnect(); + + // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely + // because somebody is doing a blocking read on our file descriptor. If that's the case, + // then send the "q" char to the command file channel so the read will wake up and the connection + // will then know to shut down. + + m_shutting_down = true; + + Mutex::Locker locker; + bool got_lock = locker.TryLock(m_mutex); + + if (!got_lock) + { + if (m_pipe.CanWrite()) + { + size_t bytes_written = 0; + Error result = m_pipe.Write("q", 1, bytes_written); + if (log) + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, sent 'q' to %d, error = '%s'.", + static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(), result.AsCString()); + } + else if (log) + { + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.", + static_cast<void *>(this)); + } + locker.Lock(m_mutex); + } + + Error error = m_read_sp->Close(); + Error error2 = m_write_sp->Close(); + if (error.Fail() || error2.Fail()) + status = eConnectionStatusError; + 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; +} + +size_t +ConnectionFileDescriptor::Read(void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + + Mutex::Locker locker; + bool got_lock = locker.TryLock(m_mutex); + if (!got_lock) + { + if (log) + log->Printf("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", static_cast<void *>(this)); + if (error_ptr) + error_ptr->SetErrorString("failed to get the connection lock for read."); + + status = eConnectionStatusTimedOut; + return 0; + } + + if (m_shutting_down) + { + status = eConnectionStatusError; + return 0; + } + + status = BytesAvailable(timeout_usec, error_ptr); + if (status != eConnectionStatusSuccess) + return 0; + + Error error; + size_t bytes_read = dst_len; + error = m_read_sp->Read(dst, bytes_read); + + if (log) + { + log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", + static_cast<void *>(this), static_cast<uint64_t>(m_read_sp->GetWaitableHandle()), static_cast<void *>(dst), + static_cast<uint64_t>(dst_len), static_cast<uint64_t>(bytes_read), error.AsCString()); + } + + if (bytes_read == 0) + { + error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. + status = eConnectionStatusEndOfFile; + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + uint32_t error_value = error.GetError(); + switch (error_value) + { + case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. + if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + status = eConnectionStatusTimedOut; + else + status = eConnectionStatusSuccess; + return 0; + + case EFAULT: // Buf points outside the allocated address space. + case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. + case EINVAL: // The pointer associated with fildes was negative. + case EIO: // An I/O error occurred while reading from the file system. + // The process group is orphaned. + // The file is a regular file, nbyte is greater than 0, + // the starting position is before the end-of-file, and + // the starting position is greater than or equal to the + // offset maximum established for the open file + // descriptor associated with fildes. + case EISDIR: // An attempt is made to read a directory. + case ENOBUFS: // An attempt to allocate a memory buffer fails. + case ENOMEM: // Insufficient memory is available. + status = eConnectionStatusError; + break; // Break to close.... + + case ENOENT: // no such file or directory + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + case ENXIO: // An action is requested of a device that does not exist.. + // A requested action cannot be performed by the device. + case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. + status = eConnectionStatusTimedOut; + return 0; + + default: + if (log) + log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", static_cast<void *>(this), + strerror(error_value)); + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + return bytes_read; +} + +size_t +ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast<void *>(this), + static_cast<const void *>(src), static_cast<uint64_t>(src_len)); + + if (!IsConnected()) + { + if (error_ptr) + error_ptr->SetErrorString("not connected"); + status = eConnectionStatusNoConnection; + return 0; + } + + Error error; + + size_t bytes_sent = src_len; + error = m_write_sp->Write(src, bytes_sent); + + if (log) + { + log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", + static_cast<void *>(this), static_cast<uint64_t>(m_write_sp->GetWaitableHandle()), static_cast<const void *>(src), + static_cast<uint64_t>(src_len), static_cast<uint64_t>(bytes_sent), error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + +std::string +ConnectionFileDescriptor::GetURI() +{ + return m_uri; +} + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// - The Apple specific version allows for unlimited fds in the fd_sets by +// setting the _DARWIN_UNLIMITED_SELECT define prior to including the +// required header files. +// CONS: +// - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. +// This implementation will assert if it runs into that hard limit to let +// users know that another ConnectionFileDescriptor::BytesAvailable() should +// be used or a new version of ConnectionFileDescriptor::BytesAvailable() +// should be written for the system that is running into the limitations. + +#if defined(__APPLE__) +#define FD_SET_DATA(fds) fds.data() +#else +#define FD_SET_DATA(fds) &fds +#endif + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast<void *>(this), timeout_usec); + + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Inifinite wait... + tv_ptr = nullptr; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds(timeout_usec); + tv.tv_sec = time_value.seconds(); + tv.tv_usec = time_value.microseconds(); + tv_ptr = &tv; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); + const int pipe_fd = m_pipe.GetReadFileDescriptor(); + + if (handle != IOObject::kInvalidHandleValue) + { +#if defined(_MSC_VER) + // select() won't accept pipes on Windows. The entire Windows codepath needs to be + // converted over to using WaitForMultipleObjects and event HANDLEs, but for now at least + // this will allow ::select() to not return an error. + const bool have_pipe_fd = false; +#else + const bool have_pipe_fd = pipe_fd >= 0; +#if !defined(__APPLE__) + assert(handle < FD_SETSIZE); + if (have_pipe_fd) + assert(pipe_fd < FD_SETSIZE); +#endif +#endif + while (handle == m_read_sp->GetWaitableHandle()) + { + const int nfds = std::max<int>(handle, pipe_fd) + 1; +#if defined(__APPLE__) + llvm::SmallVector<fd_set, 1> read_fds; + read_fds.resize((nfds / FD_SETSIZE) + 1); + for (size_t i = 0; i < read_fds.size(); ++i) + FD_ZERO(&read_fds[i]); +// FD_SET doesn't bounds check, it just happily walks off the end +// but we have taken care of making the extra storage with our +// SmallVector of fd_set objects +#else + fd_set read_fds; + FD_ZERO(&read_fds); +#endif + FD_SET(handle, FD_SET_DATA(read_fds)); + if (have_pipe_fd) + FD_SET(pipe_fd, FD_SET_DATA(read_fds)); + + Error error; + + if (log) + { + if (have_pipe_fd) + log->Printf( + "%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr)); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr)); + } + + const int num_set_fds = ::select(nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) " + "=> %d, error = %s", + static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr), num_set_fds, + error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => " + "%d, error = %s", + static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr), num_set_fds, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (FD_ISSET(handle, FD_SET_DATA(read_fds))) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) + { + // 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 + { + bytes_read = ::read(pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + + switch (buffer[0]) + { + case 'q': + if (log) + 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 + return eConnectionStatusInterrupted; + } + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketAccept(const char *socket_name, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) + { + return eConnectionStatusError; + } + m_uri.assign(socket_name); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketConnect(const char *socket_name, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) + { + return eConnectionStatusError; + } + m_uri.assign(socket_name); + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionFileDescriptor::UnixAbstractSocketConnect(const char *socket_name, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::UnixAbstractConnect(socket_name, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) + { + return eConnectionStatusError; + } + m_uri.assign(socket_name); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListenAndAccept(const char *s, Error *error_ptr) +{ + m_port_predicate.SetValue(0, eBroadcastNever); + + Socket *socket = nullptr; + m_waiting_for_accept = true; + Error error = Socket::TcpListen(s, m_child_processes_inherit, socket, &m_port_predicate); + if (error_ptr) + *error_ptr = error; + if (error.Fail()) + return eConnectionStatusError; + + std::unique_ptr<Socket> listening_socket_up; + + listening_socket_up.reset(socket); + socket = nullptr; + error = listening_socket_up->Accept(s, m_child_processes_inherit, socket); + listening_socket_up.reset(); + if (error_ptr) + *error_ptr = error; + if (error.Fail()) + return eConnectionStatusError; + + InitializeSocket(socket); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::TcpConnect(s, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) + { + return eConnectionStatusError; + } + m_uri.assign(s); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr) +{ + Socket *send_socket = nullptr; + Socket *recv_socket = nullptr; + Error error = Socket::UdpConnect(s, m_child_processes_inherit, send_socket, recv_socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(send_socket); + m_read_sp.reset(recv_socket); + if (error.Fail()) + { + return eConnectionStatusError; + } + m_uri.assign(s); + return eConnectionStatusSuccess; +} + +uint16_t +ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) +{ + uint16_t bound_port = 0; + if (timeout_sec == UINT32_MAX) + m_port_predicate.WaitForValueNotEqualTo(0, bound_port); + else + { + TimeValue timeout = TimeValue::Now(); + timeout.OffsetWithSeconds(timeout_sec); + m_port_predicate.WaitForValueNotEqualTo(0, bound_port, &timeout); + } + return bound_port; +} + +bool +ConnectionFileDescriptor::GetChildProcessesInherit() const +{ + return m_child_processes_inherit; +} + +void +ConnectionFileDescriptor::SetChildProcessesInherit(bool child_processes_inherit) +{ + m_child_processes_inherit = child_processes_inherit; +} + +void +ConnectionFileDescriptor::InitializeSocket(Socket* socket) +{ + assert(socket->GetSocketProtocol() == Socket::ProtocolTcp); + TCPSocket* tcp_socket = static_cast<TCPSocket*>(socket); + + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + StreamString strm; + strm.Printf("connect://%s:%u",tcp_socket->GetRemoteIPAddress().c_str(), tcp_socket->GetRemotePortNumber()); + m_uri.swap(strm.GetString()); +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/DomainSocket.cpp b/contrib/llvm/tools/lldb/source/Host/posix/DomainSocket.cpp new file mode 100644 index 0000000..b4427e3 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/DomainSocket.cpp @@ -0,0 +1,133 @@ +//===-- DomainSocket.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/DomainSocket.h" + +#include "lldb/Host/FileSystem.h" + +#include <stddef.h> +#include <sys/socket.h> +#include <sys/un.h> + +using namespace lldb; +using namespace lldb_private; + +#ifdef __ANDROID__ +// Android does not have SUN_LEN +#ifndef SUN_LEN +#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path)) +#endif +#endif // #ifdef __ANDROID__ + +namespace { + +const int kDomain = AF_UNIX; +const int kType = SOCK_STREAM; + +bool SetSockAddr(llvm::StringRef name, + const size_t name_offset, + sockaddr_un* saddr_un, + socklen_t& saddr_un_len) +{ + if (name.size() + name_offset > sizeof(saddr_un->sun_path)) + return false; + + memset(saddr_un, 0, sizeof(*saddr_un)); + saddr_un->sun_family = kDomain; + + memcpy(saddr_un->sun_path + name_offset, name.data(), name.size()); + + // For domain sockets we can use SUN_LEN in order to calculate size of + // sockaddr_un, but for abstract sockets we have to calculate size manually + // because of leading null symbol. + if (name_offset == 0) + saddr_un_len = SUN_LEN(saddr_un); + else + saddr_un_len = offsetof(struct sockaddr_un, sun_path) + name_offset + name.size(); + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) + saddr_un->sun_len = saddr_un_len; +#endif + + return true; +} + +} + +DomainSocket::DomainSocket(NativeSocket socket) + : Socket(socket, ProtocolUnixDomain, true) +{ +} + +DomainSocket::DomainSocket(bool child_processes_inherit, Error &error) + : DomainSocket(CreateSocket(kDomain, kType, 0, child_processes_inherit, error)) +{ +} + +DomainSocket::DomainSocket(SocketProtocol protocol, bool child_processes_inherit, Error &error) + : Socket(CreateSocket(kDomain, kType, 0, child_processes_inherit, error), protocol, true) +{ +} + +Error +DomainSocket::Connect(llvm::StringRef name) +{ + sockaddr_un saddr_un; + socklen_t saddr_un_len; + if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) + return Error("Failed to set socket address"); + + Error error; + if (::connect(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) < 0) + SetLastError (error); + + return error; +} + +Error +DomainSocket::Listen(llvm::StringRef name, int backlog) +{ + sockaddr_un saddr_un; + socklen_t saddr_un_len; + if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) + return Error("Failed to set socket address"); + + DeleteSocketFile(name); + + Error error; + if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) == 0) + if (::listen(GetNativeSocket(), backlog) == 0) + return error; + + SetLastError(error); + return error; +} + +Error +DomainSocket::Accept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) +{ + Error error; + auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, child_processes_inherit, error); + if (error.Success()) + socket = new DomainSocket(conn_fd); + + return error; +} + +size_t +DomainSocket::GetNameOffset() const +{ + return 0; +} + +void +DomainSocket::DeleteSocketFile(llvm::StringRef name) +{ + FileSystem::Unlink(FileSpec{name, true}); +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/FileSystem.cpp b/contrib/llvm/tools/lldb/source/Host/posix/FileSystem.cpp new file mode 100644 index 0000000..1f2e7db --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/FileSystem.cpp @@ -0,0 +1,299 @@ +//===-- 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" + +// 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 +#if defined(__NetBSD__) +#include <sys/statvfs.h> +#endif + +// lldb Includes +#include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +FileSystem::DEV_NULL = "/dev/null"; + +FileSpec::PathSyntax +FileSystem::GetNativePathSyntax() +{ + return FileSpec::ePathSyntaxPosix; +} + +Error +FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) +{ + if (file_spec) + { + 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 + // 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) + { + error.SetErrorToErrno(); + return error; + } + } + case EEXIST: + { + if (file_spec.IsDirectory()) + return Error{}; // It is a directory and it already exists + } + } + } + return error; + } + return Error{"empty path"}; +} + +Error +FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) +{ + Error error; + if (file_spec) + { + if (recurse) + { + // 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; + } + } + } + + if (error.Success()) + { + if (::rmdir(file_spec.GetCString()) != 0) + error.SetErrorToErrno(); + } + } + else + { + error.SetErrorString("empty path"); + } + return error; +} + +Error +FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) +{ + Error error; + struct stat file_stats; + if (::stat(file_spec.GetCString(), &file_stats) == 0) + { + // The bits in "st_mode" currently match the definitions + // for the file mode bits in unix. + file_permissions = file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + } + else + { + error.SetErrorToErrno(); + } + return error; +} + +Error +FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) +{ + Error error; + if (::chmod(file_spec.GetCString(), file_permissions) != 0) + error.SetErrorToErrno(); + return error; +} + +lldb::user_id_t +FileSystem::GetFileSize(const FileSpec &file_spec) +{ + return file_spec.GetByteSize(); +} + +bool +FileSystem::GetFileExists(const FileSpec &file_spec) +{ + return file_spec.Exists(); +} + +Error +FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) +{ + Error error; + if (::link(dst.GetCString(), src.GetCString()) == -1) + error.SetErrorToErrno(); + return error; +} + +int +FileSystem::GetHardlinkCount(const FileSpec &file_spec) +{ + struct stat file_stat; + if (::stat(file_spec.GetCString(), &file_stat) == 0) + return file_stat.st_nlink; + + return -1; +} + +Error +FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) +{ + Error error; + if (::symlink(dst.GetCString(), src.GetCString()) == -1) + error.SetErrorToErrno(); + return error; +} + +Error +FileSystem::Unlink(const FileSpec &file_spec) +{ + Error error; + 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 + { + buf[count] = '\0'; // Success + dst.SetFile(buf, false); + } + return error; +} + +Error +FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) +{ + char resolved_path[PATH_MAX]; + if (!src.GetPath (resolved_path, sizeof (resolved_path))) + { + return Error("Couldn't get the canonical path for %s", src.GetCString()); + } + + char real_path[PATH_MAX + 1]; + if (realpath(resolved_path, real_path) == nullptr) + { + Error err; + err.SetErrorToErrno(); + return err; + } + + dst = FileSpec(real_path, false); + + return Error(); +} + +#if defined(__NetBSD__) +static bool IsLocal(const struct statvfs& info) +{ + return (info.f_flag & MNT_LOCAL) != 0; +} +#else +static bool IsLocal(const struct statfs& info) +{ +#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; + default: + return true; + } +#else + return (info.f_flags & MNT_LOCAL) != 0; +#endif +} +#endif + +#if defined(__NetBSD__) +bool +FileSystem::IsLocal(const FileSpec &spec) +{ + struct statvfs statfs_info; + std::string path (spec.GetPath()); + if (statvfs(path.c_str(), &statfs_info) == 0) + return ::IsLocal(statfs_info); + return false; +} +#else +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; +} +#endif diff --git a/contrib/llvm/tools/lldb/source/Host/posix/HostInfoPosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/HostInfoPosix.cpp new file mode 100644 index 0000000..cfdbf56 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/HostInfoPosix.cpp @@ -0,0 +1,244 @@ +//===-- HostInfoPosix.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if !defined(LLDB_DISABLE_PYTHON) +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#endif + +#include "lldb/Core/Log.h" +#include "lldb/Host/posix/HostInfoPosix.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +#include <grp.h> +#include <limits.h> +#include <mutex> +#include <netdb.h> +#include <pwd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> + +using namespace lldb_private; + +size_t +HostInfoPosix::GetPageSize() +{ + return ::getpagesize(); +} + +bool +HostInfoPosix::GetHostname(std::string &s) +{ + char hostname[PATH_MAX]; + hostname[sizeof(hostname) - 1] = '\0'; + if (::gethostname(hostname, sizeof(hostname) - 1) == 0) + { + struct hostent *h = ::gethostbyname(hostname); + if (h) + s.assign(h->h_name); + else + s.assign(hostname); + return true; + } + 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]; + size_t user_buffer_size = sizeof(user_buffer); + if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size, &user_info_ptr) == 0) + { + if (user_info_ptr) + { + user_name.assign(user_info_ptr->pw_name); + return user_name.c_str(); + } + } +#endif + user_name.clear(); + return nullptr; +} + +const char * +HostInfoPosix::LookupGroupName(uint32_t gid, std::string &group_name) +{ +#ifndef __ANDROID__ + char group_buffer[PATH_MAX]; + size_t group_buffer_size = sizeof(group_buffer); + struct group group_info; + struct group *group_info_ptr = &group_info; + // Try the threadsafe version first + if (::getgrgid_r(gid, &group_info, group_buffer, group_buffer_size, &group_info_ptr) == 0) + { + if (group_info_ptr) + { + group_name.assign(group_info_ptr->gr_name); + return group_name.c_str(); + } + } + else + { + // The threadsafe version isn't currently working for me on darwin, but the non-threadsafe version + // is, so I am calling it below. + group_info_ptr = ::getgrgid(gid); + if (group_info_ptr) + { + group_name.assign(group_info_ptr->gr_name); + return group_name.c_str(); + } + } + group_name.clear(); +#else + assert(false && "getgrgid_r() not supported on Android"); +#endif + return NULL; +} + +uint32_t +HostInfoPosix::GetUserID() +{ + return getuid(); +} + +uint32_t +HostInfoPosix::GetGroupID() +{ + return getgid(); +} + +uint32_t +HostInfoPosix::GetEffectiveUserID() +{ + return geteuid(); +} + +uint32_t +HostInfoPosix::GetEffectiveGroupID() +{ + return getegid(); +} + +FileSpec +HostInfoPosix::GetDefaultShell() +{ + return FileSpec("/bin/sh", false); +} + +bool +HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + char raw_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + + // Most Posix systems (e.g. Linux/*BSD) will attempt to replace a */lib with */bin as the base + // directory for helper exe programs. This will fail if the /lib and /bin directories are + // rooted in entirely different trees. + if (log) + log->Printf("HostInfoPosix::ComputeSupportExeDirectory() attempting to derive the bin path (ePathTypeSupportExecutableDir) from " + "this path: %s", + raw_path); + char *lib_pos = ::strstr(raw_path, "/lib"); + if (lib_pos != nullptr) + { + // Now write in bin in place of lib. + ::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); + } + else + { + if (log) + log->Printf("Host::%s() failed to find /lib/liblldb within the shared lib path, bailing on bin path construction", + __FUNCTION__); + } + file_spec.GetDirectory().SetCString(raw_path); + return (bool)file_spec.GetDirectory(); +} + +bool +HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) +{ + FileSpec temp_file("/opt/local/include/lldb", false); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +bool +HostInfoPosix::ComputePythonDirectory(FileSpec &file_spec) +{ +#ifndef LLDB_DISABLE_PYTHON + FileSpec lldb_file_spec; + if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) + return false; + + char raw_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + +#if defined(LLDB_PYTHON_RELATIVE_LIBDIR) + // Build the path by backing out of the lib dir, then building + // with whatever the real python interpreter uses. (e.g. lib + // for most, lib64 on RHEL x86_64). + char python_path[PATH_MAX]; + ::snprintf(python_path, sizeof(python_path), "%s/../%s", raw_path, LLDB_PYTHON_RELATIVE_LIBDIR); + + char final_path[PATH_MAX]; + realpath(python_path, final_path); + file_spec.GetDirectory().SetCString(final_path); + + return true; +#else + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + + // We may get our string truncated. Should we protect this with an assert? + ::strncat(raw_path, python_version_dir.c_str(), sizeof(raw_path) - strlen(raw_path) - 1); + + file_spec.GetDirectory().SetCString(raw_path); + return true; +#endif +#else + return false; +#endif +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/HostProcessPosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/HostProcessPosix.cpp new file mode 100644 index 0000000..5761a79 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/HostProcessPosix.cpp @@ -0,0 +1,113 @@ +//===-- HostProcessPosix.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/Host.h" +#include "lldb/Host/posix/HostProcessPosix.h" +#include "lldb/Host/FileSystem.h" + +#include "llvm/ADT/STLExtras.h" + +#include <limits.h> + +using namespace lldb_private; + +namespace +{ + const int kInvalidPosixProcess = 0; +} + +HostProcessPosix::HostProcessPosix() + : HostNativeProcessBase(kInvalidPosixProcess) +{ +} + +HostProcessPosix::HostProcessPosix(lldb::process_t process) + : HostNativeProcessBase(process) +{ +} + +HostProcessPosix::~HostProcessPosix() +{ +} + +Error HostProcessPosix::Signal(int signo) const +{ + if (m_process == kInvalidPosixProcess) + { + Error error; + error.SetErrorString("HostProcessPosix refers to an invalid process"); + return error; + } + + return HostProcessPosix::Signal(m_process, signo); +} + +Error HostProcessPosix::Signal(lldb::process_t process, int signo) +{ + Error error; + + if (-1 == ::kill(process, signo)) + error.SetErrorToErrno(); + + return error; +} + +Error HostProcessPosix::Terminate() +{ + return Signal(SIGKILL); +} + +Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const +{ + Error error; + + // Use special code here because proc/[pid]/exe is a symbolic link. + char link_path[PATH_MAX]; + 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(FileSpec{link_path, false}, file_spec); + if (!error.Success()) + return error; + + // If the binary has been deleted, the link name has " (deleted)" appended. + // Remove if there. + if (file_spec.GetFilename().GetStringRef().endswith(" (deleted)")) + { + 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); + } + return error; +} + +lldb::pid_t HostProcessPosix::GetProcessId() const +{ + return m_process; +} + +bool HostProcessPosix::IsRunning() const +{ + if (m_process == kInvalidPosixProcess) + return false; + + // Send this process the null signal. If it succeeds the process is running. + Error error = Signal(0); + return error.Success(); +} + +HostThread +HostProcessPosix::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +{ + return Host::StartMonitoringChildProcess(callback, callback_baton, m_process, monitor_signals); +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/HostThreadPosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/HostThreadPosix.cpp new file mode 100644 index 0000000..9c48924 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/HostThreadPosix.cpp @@ -0,0 +1,74 @@ +//===-- HostThreadPosix.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/Core/Error.h" +#include "lldb/Host/posix/HostThreadPosix.h" + +#include <errno.h> +#include <pthread.h> + +using namespace lldb; +using namespace lldb_private; + +HostThreadPosix::HostThreadPosix() +{ +} + +HostThreadPosix::HostThreadPosix(lldb::thread_t thread) + : HostNativeThreadBase(thread) +{ +} + +HostThreadPosix::~HostThreadPosix() +{ +} + +Error +HostThreadPosix::Join(lldb::thread_result_t *result) +{ + Error error; + if (IsJoinable()) + { + int err = ::pthread_join(m_thread, result); + error.SetError(err, lldb::eErrorTypePOSIX); + } + else + { + if (result) + *result = NULL; + error.SetError(EINVAL, eErrorTypePOSIX); + } + + Reset(); + return error; +} + +Error +HostThreadPosix::Cancel() +{ + Error error; +#ifndef __ANDROID__ + int err = ::pthread_cancel(m_thread); + error.SetError(err, eErrorTypePOSIX); +#else + error.SetErrorString("HostThreadPosix::Cancel() not supported on Android"); +#endif + + return error; +} + +Error +HostThreadPosix::Detach() +{ + Error error; + int err = ::pthread_detach(m_thread); + error.SetError(err, eErrorTypePOSIX); + Reset(); + return error; +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/LockFilePosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/LockFilePosix.cpp new file mode 100644 index 0000000..e52b648 --- /dev/null +++ b/contrib/llvm/tools/lldb/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/contrib/llvm/tools/lldb/source/Host/posix/MainLoopPosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/MainLoopPosix.cpp new file mode 100644 index 0000000..897f2d1 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/MainLoopPosix.cpp @@ -0,0 +1,193 @@ +//===-- MainLoopPosix.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/MainLoopPosix.h" + +#include <vector> + +#include "lldb/Core/Error.h" + +using namespace lldb; +using namespace lldb_private; + +static sig_atomic_t g_signal_flags[NSIG]; + +static void +SignalHandler(int signo, siginfo_t *info, void *) +{ + assert(signo < NSIG); + g_signal_flags[signo] = 1; +} + + +MainLoopPosix::~MainLoopPosix() +{ + assert(m_read_fds.size() == 0); + assert(m_signals.size() == 0); +} + +MainLoopPosix::ReadHandleUP +MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp, const Callback &callback, Error &error) +{ + if (!object_sp || !object_sp->IsValid()) + { + error.SetErrorString("IO object is not valid."); + return nullptr; + } + + const bool inserted = m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second; + if (! inserted) + { + error.SetErrorStringWithFormat("File descriptor %d already monitored.", + object_sp->GetWaitableHandle()); + return nullptr; + } + + return CreateReadHandle(object_sp); +} + +// We shall block the signal, then install the signal handler. The signal will be unblocked in +// the Run() function to check for signal delivery. +MainLoopPosix::SignalHandleUP +MainLoopPosix::RegisterSignal(int signo, const Callback &callback, Error &error) +{ + if (m_signals.find(signo) != m_signals.end()) + { + error.SetErrorStringWithFormat("Signal %d already monitored.", signo); + return nullptr; + } + + SignalInfo info; + info.callback = callback; + struct sigaction new_action; + new_action.sa_sigaction = &SignalHandler; + new_action.sa_flags = SA_SIGINFO; + sigemptyset(&new_action.sa_mask); + sigaddset(&new_action.sa_mask, signo); + + sigset_t old_set; + if (int ret = pthread_sigmask(SIG_BLOCK, &new_action.sa_mask, &old_set)) + { + error.SetErrorStringWithFormat("pthread_sigmask failed with error %d\n", ret); + return nullptr; + } + + info.was_blocked = sigismember(&old_set, signo); + if (sigaction(signo, &new_action, &info.old_action) == -1) + { + error.SetErrorToErrno(); + if (!info.was_blocked) + pthread_sigmask(SIG_UNBLOCK, &new_action.sa_mask, nullptr); + return nullptr; + } + + m_signals.insert({signo, info}); + g_signal_flags[signo] = 0; + + return SignalHandleUP(new SignalHandle(*this, signo)); +} + +void +MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle) +{ + bool erased = m_read_fds.erase(handle); + UNUSED_IF_ASSERT_DISABLED(erased); + assert(erased); +} + +void +MainLoopPosix::UnregisterSignal(int signo) +{ + // We undo the actions of RegisterSignal on a best-effort basis. + auto it = m_signals.find(signo); + assert(it != m_signals.end()); + + sigaction(signo, &it->second.old_action, nullptr); + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, signo); + pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK, &set, nullptr); + + m_signals.erase(it); +} + +Error +MainLoopPosix::Run() +{ + std::vector<int> signals; + sigset_t sigmask; + std::vector<int> read_fds; + fd_set read_fd_set; + m_terminate_request = false; + + // run until termination or until we run out of things to listen to + while (! m_terminate_request && (!m_read_fds.empty() || !m_signals.empty())) + { + // To avoid problems with callbacks changing the things we're supposed to listen to, we + // will store the *real* list of events separately. + signals.clear(); + read_fds.clear(); + FD_ZERO(&read_fd_set); + int nfds = 0; + + if (int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask)) + return Error("pthread_sigmask failed with error %d\n", ret); + + for (const auto &fd: m_read_fds) + { + read_fds.push_back(fd.first); + FD_SET(fd.first, &read_fd_set); + nfds = std::max(nfds, fd.first+1); + } + + for (const auto &sig: m_signals) + { + signals.push_back(sig.first); + sigdelset(&sigmask, sig.first); + } + + if (pselect(nfds, &read_fd_set, nullptr, nullptr, nullptr, &sigmask) == -1 && errno != EINTR) + return Error(errno, eErrorTypePOSIX); + + for (int sig: signals) + { + if (g_signal_flags[sig] == 0) + continue; // No signal + g_signal_flags[sig] = 0; + + auto it = m_signals.find(sig); + if (it == m_signals.end()) + continue; // Signal must have gotten unregistered in the meantime + + it->second.callback(*this); // Do the work + + if (m_terminate_request) + return Error(); + } + + for (int fd: read_fds) + { + if (!FD_ISSET(fd, &read_fd_set)) + continue; // Not ready + + auto it = m_read_fds.find(fd); + if (it == m_read_fds.end()) + continue; // File descriptor must have gotten unregistered in the meantime + + it->second(*this); // Do the work + + if (m_terminate_request) + return Error(); + } + } + return Error(); +} + + diff --git a/contrib/llvm/tools/lldb/source/Host/posix/PipePosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/PipePosix.cpp new file mode 100644 index 0000000..353faae --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/PipePosix.cpp @@ -0,0 +1,437 @@ +//===-- PipePosix.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/PipePosix.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" + +#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) +#ifndef _GLIBCXX_USE_NANOSLEEP +#define _GLIBCXX_USE_NANOSLEEP +#endif +#endif + +#include <functional> +#include <thread> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +using namespace lldb; +using namespace lldb_private; + +int PipePosix::kInvalidDescriptor = -1; + +enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE + +// pipe2 is supported by a limited set of platforms +// TODO: Add more platforms that support pipe2. +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) || defined(__NetBSD__) +#define PIPE2_SUPPORTED 1 +#else +#define PIPE2_SUPPORTED 0 +#endif + +namespace +{ + +constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; + +#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED +bool SetCloexecFlag(int fd) +{ + int flags = ::fcntl(fd, F_GETFD); + if (flags == -1) + return false; + return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); +} +#endif + +std::chrono::time_point<std::chrono::steady_clock> +Now() +{ + return std::chrono::steady_clock::now(); +} + +Error +SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout) +{ + Error error; + fd_set fds; + bool done = false; + + using namespace std::chrono; + + const auto finish_time = Now() + timeout; + + while (!done) + { + struct timeval tv = {0, 0}; + if (timeout != microseconds::zero()) + { + const auto remaining_dur = duration_cast<microseconds>(finish_time - Now()); + if (remaining_dur.count() <= 0) + { + error.SetErrorString("timeout exceeded"); + break; + } + const auto dur_secs = duration_cast<seconds>(remaining_dur); + const auto dur_usecs = remaining_dur % seconds(1); + + tv.tv_sec = dur_secs.count(); + tv.tv_usec = dur_usecs.count(); + } + else + tv.tv_sec = 1; + + FD_ZERO(&fds); + FD_SET(handle, &fds); + + const auto retval = ::select(handle + 1, + (is_read) ? &fds : nullptr, + (is_read) ? nullptr : &fds, + nullptr, &tv); + if (retval == -1) + { + if (errno == EINTR) + continue; + error.SetErrorToErrno(); + break; + } + if (retval == 0) + { + error.SetErrorString("timeout exceeded"); + break; + } + if (!FD_ISSET(handle, &fds)) + { + error.SetErrorString("invalid state"); + break; + } + + error = io_handler(done); + if (error.Fail()) + { + if (error.GetError() == EINTR) + continue; + break; + } + } + return error; +} + +} + +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) +{ + PipeBase::operator=(std::move(pipe_posix)); + m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor(); + m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor(); + return *this; +} + +PipePosix::~PipePosix() +{ + Close(); +} + +Error +PipePosix::CreateNew(bool child_processes_inherit) +{ + if (CanRead() || CanWrite()) + return Error(EINVAL, eErrorTypePOSIX); + + Error error; +#if PIPE2_SUPPORTED + if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) + return error; +#else + if (::pipe(m_fds) == 0) + { +#ifdef FD_CLOEXEC + if (!child_processes_inherit) + { + if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) + { + error.SetErrorToErrno(); + Close(); + return error; + } + } +#endif + return error; + } +#endif + + error.SetErrorToErrno(); + m_fds[READ] = PipePosix::kInvalidDescriptor; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + return error; +} + +Error +PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error("Pipe is already opened"); + + Error error; + if (::mkfifo(name.data(), 0660) != 0) + error.SetErrorToErrno(); + + return error; +} + +Error +PipePosix::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name) +{ + llvm::SmallString<PATH_MAX> named_pipe_path; + llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str()); + FileSpec tmpdir_file_spec; + tmpdir_file_spec.Clear(); + if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) + { + tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); + } + else + { + tmpdir_file_spec.AppendPathComponent("/tmp"); + tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); + } + + // It's possible that another process creates the target path after we've + // verified it's available but before we create it, in which case we + // should try again. + Error error; + do { + llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), named_pipe_path); + error = CreateNew(named_pipe_path, child_process_inherit); + } while (error.GetError() == EEXIST); + + if (error.Success()) + name = named_pipe_path; + return error; +} + +Error +PipePosix::OpenAsReader(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error("Pipe is already opened"); + + int flags = O_RDONLY | O_NONBLOCK; + if (!child_process_inherit) + flags |= O_CLOEXEC; + + Error error; + int fd = ::open(name.data(), flags); + if (fd != -1) + m_fds[READ] = fd; + else + error.SetErrorToErrno(); + + return error; +} + +Error +PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) +{ + if (CanRead() || CanWrite()) + return Error("Pipe is already opened"); + + int flags = O_WRONLY | O_NONBLOCK; + if (!child_process_inherit) + flags |= O_CLOEXEC; + + using namespace std::chrono; + const auto finish_time = Now() + timeout; + + while (!CanWrite()) + { + if (timeout != microseconds::zero()) + { + const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); + if (dur <= 0) + return Error("timeout exceeded - reader hasn't opened so far"); + } + + errno = 0; + int fd = ::open(name.data(), flags); + if (fd == -1) + { + const auto errno_copy = errno; + // We may get ENXIO if a reader side of the pipe hasn't opened yet. + if (errno_copy != ENXIO) + return Error(errno_copy, eErrorTypePOSIX); + + std::this_thread::sleep_for(milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); + } + else + { + m_fds[WRITE] = fd; + } + } + + return Error(); +} + +int +PipePosix::GetReadFileDescriptor() const +{ + return m_fds[READ]; +} + +int +PipePosix::GetWriteFileDescriptor() const +{ + return m_fds[WRITE]; +} + +int +PipePosix::ReleaseReadFileDescriptor() +{ + const int fd = m_fds[READ]; + m_fds[READ] = PipePosix::kInvalidDescriptor; + return fd; +} + +int +PipePosix::ReleaseWriteFileDescriptor() +{ + const int fd = m_fds[WRITE]; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + return fd; +} + +void +PipePosix::Close() +{ + CloseReadFileDescriptor(); + CloseWriteFileDescriptor(); +} + +Error +PipePosix::Delete(llvm::StringRef name) +{ + return FileSystem::Unlink(FileSpec{name.data(), true}); +} + +bool +PipePosix::CanRead() const +{ + return m_fds[READ] != PipePosix::kInvalidDescriptor; +} + +bool +PipePosix::CanWrite() const +{ + return m_fds[WRITE] != PipePosix::kInvalidDescriptor; +} + +void +PipePosix::CloseReadFileDescriptor() +{ + if (CanRead()) + { + close(m_fds[READ]); + m_fds[READ] = PipePosix::kInvalidDescriptor; + } +} + +void +PipePosix::CloseWriteFileDescriptor() +{ + if (CanWrite()) + { + close(m_fds[WRITE]); + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + } +} + +Error +PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) +{ + bytes_read = 0; + if (!CanRead()) + return Error(EINVAL, eErrorTypePOSIX); + + auto handle = GetReadFileDescriptor(); + return SelectIO(handle, + true, + [=, &bytes_read](bool &done) + { + Error error; + auto result = ::read(handle, + reinterpret_cast<char*>(buf) + bytes_read, + size - bytes_read); + if (result != -1) + { + bytes_read += result; + if (bytes_read == size || result == 0) + done = true; + } + else + error.SetErrorToErrno(); + + return error; + }, + timeout); +} + +Error +PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) +{ + bytes_written = 0; + if (!CanWrite()) + return Error(EINVAL, eErrorTypePOSIX); + + auto handle = GetWriteFileDescriptor(); + return SelectIO(handle, + false, + [=, &bytes_written](bool &done) + { + Error error; + auto result = ::write(handle, + reinterpret_cast<const char*>(buf) + bytes_written, + size - bytes_written); + if (result != -1) + { + bytes_written += result; + if (bytes_written == size) + done = true; + } + else + error.SetErrorToErrno(); + + return error; + }, + std::chrono::microseconds::zero()); +} diff --git a/contrib/llvm/tools/lldb/source/Host/posix/ProcessLauncherPosix.cpp b/contrib/llvm/tools/lldb/source/Host/posix/ProcessLauncherPosix.cpp new file mode 100644 index 0000000..dd5c512 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Host/posix/ProcessLauncherPosix.cpp @@ -0,0 +1,33 @@ +//===-- ProcessLauncherPosix.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/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/posix/ProcessLauncherPosix.h" + +#include "lldb/Target/ProcessLaunchInfo.h" + +#include <limits.h> + +using namespace lldb; +using namespace lldb_private; + +HostProcess +ProcessLauncherPosix::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + lldb::pid_t pid; + char exe_path[PATH_MAX]; + + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + + // TODO(zturner): Move the code from LaunchProcessPosixSpawn to here, and make MacOSX re-use this + // ProcessLauncher when it wants a posix_spawn launch. + error = Host::LaunchProcessPosixSpawn(exe_path, launch_info, pid); + return HostProcess(pid); +} |