summaryrefslogtreecommitdiff
path: root/source/Host
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host')
-rw-r--r--source/Host/common/Editline.cpp1735
-rw-r--r--source/Host/common/File.cpp2
-rw-r--r--source/Host/common/FileSpec.cpp189
-rw-r--r--source/Host/common/Host.cpp471
-rw-r--r--source/Host/common/HostInfoBase.cpp36
-rw-r--r--source/Host/common/HostNativeThreadBase.cpp82
-rw-r--r--source/Host/common/HostProcess.cpp65
-rw-r--r--source/Host/common/HostThread.cpp78
-rw-r--r--source/Host/common/MonitoringProcessLauncher.cpp102
-rw-r--r--source/Host/common/NativeProcessProtocol.cpp23
-rw-r--r--source/Host/common/NativeProcessProtocol.h23
-rw-r--r--source/Host/common/NativeThreadProtocol.h2
-rw-r--r--source/Host/common/Pipe.cpp171
-rw-r--r--source/Host/common/PipeBase.cpp27
-rw-r--r--source/Host/common/Socket.cpp78
-rw-r--r--source/Host/common/SocketAddress.cpp10
-rw-r--r--source/Host/common/SoftwareBreakpoint.cpp19
-rw-r--r--source/Host/common/ThisThread.cpp52
-rw-r--r--source/Host/common/ThreadLauncher.cpp74
-rw-r--r--source/Host/freebsd/Host.cpp114
-rw-r--r--source/Host/freebsd/HostInfoFreeBSD.cpp6
-rw-r--r--source/Host/freebsd/HostThreadFreeBSD.cpp77
-rw-r--r--source/Host/freebsd/ThisThread.cpp30
-rw-r--r--source/Host/posix/ConnectionFileDescriptorPosix.cpp797
-rw-r--r--source/Host/posix/HostInfoPosix.cpp14
-rw-r--r--source/Host/posix/HostProcessPosix.cpp47
-rw-r--r--source/Host/posix/HostThreadPosix.cpp74
-rw-r--r--source/Host/posix/PipePosix.cpp378
-rw-r--r--source/Host/posix/ProcessLauncherPosix.cpp33
29 files changed, 3466 insertions, 1343 deletions
diff --git a/source/Host/common/Editline.cpp b/source/Host/common/Editline.cpp
index 7af9f39a7863..b82fbea90c6c 100644
--- a/source/Host/common/Editline.cpp
+++ b/source/Host/common/Editline.cpp
@@ -7,527 +7,913 @@
//
//===----------------------------------------------------------------------===//
+#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/StreamString.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 <limits.h>
-
-using namespace lldb;
using namespace lldb_private;
+using namespace lldb_private::line_editor;
-namespace lldb_private {
- typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
-
-
- // EditlineHistory objects are sometimes shared between multiple
- // Editline instances with the same program name. This class allows
- // multiple editline instances to
- //
-
- 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 ()
+// 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)
{
- m_history = ::history_init();
- ::history (m_history, &m_event, H_SETSIZE, size);
- if (unique_entries)
- ::history (m_history, &m_event, H_SETUNIQUE, 1);
+ 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.
- const char *
- GetHistoryFilePath()
+ class EditlineHistory
{
- if (m_path.empty() && m_history && !m_prefix.empty())
+ 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 ()
{
- char history_path[PATH_MAX];
- ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_prefix.c_str());
- m_path = std::move(FileSpec(history_path, true).GetPath());
+ 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())
+ {
+ std::string parent_path = FileSpec ("~/.lldb", true).GetPath();
+ char history_path[PATH_MAX];
+ if (FileSystem::MakeDirectory(parent_path.c_str(), 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 = std::move (FileSpec (history_path, true).GetPath());
+ }
+ if (m_path.empty())
+ return NULL;
+ return m_path.c_str();
}
- if (m_path.empty())
- return NULL;
- return m_path.c_str();
- }
-
- public:
-
- ~EditlineHistory()
- {
- Save ();
- if (m_history)
+ public:
+
+ ~EditlineHistory()
{
- ::history_end (m_history);
- m_history = NULL;
+ 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())
+
+ static EditlineHistorySP
+ GetHistory (const std::string &prefix)
{
- history_sp = pos->second.lock();
- if (history_sp)
- return history_sp;
- g_weak_map.erase(pos);
+ 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;
}
- history_sp.reset(new EditlineHistory(prefix, 800, true));
- g_weak_map[prefix] = history_sp;
- return history_sp;
- }
-
- bool IsValid() const
- {
- return m_history != NULL;
- }
-
- ::History *
- GetHistoryPtr ()
- {
- return m_history;
- }
-
- void
- Enter (const char *line_cstr)
- {
- if (m_history)
- ::history (m_history, &m_event, H_ENTER, line_cstr);
- }
-
- bool
- Load ()
- {
- if (m_history)
+
+ bool IsValid() const
{
- const char *path = GetHistoryFilePath();
- if (path)
+ 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)
{
- ::history (m_history, &m_event, H_LOAD, path);
- return true;
+ const char *path = GetHistoryFilePath();
+ if (path)
+ {
+ history_w (m_history, &m_event, H_LOAD, path);
+ return true;
+ }
}
+ return false;
}
- return false;
- }
-
- bool
- Save ()
- {
- if (m_history)
+
+ bool
+ Save ()
{
- const char *path = GetHistoryFilePath();
- if (path)
+ if (m_history)
{
- ::history (m_history, &m_event, H_SAVE, path);
- return true;
+ const char *path = GetHistoryFilePath();
+ if (path)
+ {
+ history_w (m_history, &m_event, H_SAVE, path);
+ return true;
+ }
}
+ return false;
}
- return false;
- }
-
- protected:
- ::History *m_history; // The history object
- ::HistEvent 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
- };
+
+ 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
+//------------------------------------------------------------------
-static const char k_prompt_escape_char = '\1';
-
-Editline::Editline (const char *prog, // prog can't be NULL
- const char *prompt, // can be NULL for no prompt
- bool configure_for_multiline,
- FILE *fin,
- FILE *fout,
- FILE *ferr) :
- m_editline (NULL),
- m_history_sp (),
- m_prompt (),
- m_lines_prompt (),
- m_getting_char (false),
- m_completion_callback (NULL),
- m_completion_callback_baton (NULL),
- m_line_complete_callback (NULL),
- m_line_complete_callback_baton (NULL),
- m_lines_command (Command::None),
- m_line_offset (0),
- m_lines_curr_line (0),
- m_lines_max_line (0),
- m_file (fileno(fin), false),
- m_prompt_with_line_numbers (false),
- m_getting_line (false),
- m_got_eof (false),
- m_interrupted (false)
-{
- if (prog && prog[0])
- {
- m_editline = ::el_init(prog, fin, fout, ferr);
-
- // Get a shared history instance
- m_history_sp = EditlineHistory::GetHistory(prog);
+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 = ": ";
}
- else
+ std::string continuation_prompt = prompt;
+ if (m_set_continuation_prompt.length() > 0)
{
- m_editline = ::el_init("lldb-tmp", fin, fout, ferr);
+ 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 (prompt && prompt[0])
- SetPrompt (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;
+}
- //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key
- //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key
+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();
+}
- assert (m_editline);
- ::el_set (m_editline, EL_CLIENTDATA, this);
+bool
+Editline::IsEmacs()
+{
+ const char * editor;
+ el_get (m_editline, EL_EDITOR, &editor);
+ return editor[0] == 'e';
+}
- // only defined for newer versions of editline
-#ifdef EL_PROMPT_ESC
- ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char);
-#else
- // fall back on old prompt setting code
- ::el_set (m_editline, EL_PROMPT, GetPromptCallback);
-#endif
- ::el_set (m_editline, EL_EDITOR, "emacs");
- if (m_history_sp && m_history_sp->IsValid())
+bool
+Editline::IsOnlySpaces()
+{
+ const LineInfoW * info = el_wline (m_editline);
+ for (const EditLineCharType * character = info->buffer; character < info->lastchar; character++)
{
- ::el_set (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
+ if (*character != ' ')
+ return false;
}
- ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete);
- // Keep old "lldb_complete" mapping for older clients that used this in their .editrc. editline also
- // has a bad bug where if you have a bind command that tries to bind to a function name that doesn't
- // exist, it will corrupt the heap and probably crash your process later.
- ::el_set (m_editline, EL_ADDFN, "lldb_complete", "Editline completion function", Editline::CallbackComplete);
- ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine);
- ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine);
+ return true;
+}
- ::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 does.
- ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
- ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be auto complete
-
- if (configure_for_multiline)
+int
+Editline::GetLineIndexForLocation (CursorLocation location, int cursor_row)
+{
+ int line = 0;
+ if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd ||
+ location == CursorLocation::EditingCursor)
{
- // Use escape sequences for control characters due to bugs in editline
- // where "-k up" and "-k down" don't always work.
- ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow
- ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow
- // Bindings for next/prev history
- ::el_set (m_editline, EL_BIND, "^P", "ed-prev-history", NULL); // Map up arrow
- ::el_set (m_editline, EL_BIND, "^N", "ed-next-history", NULL); // Map down arrow
+ 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;
+ }
}
- else
+ 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)
{
- // Use escape sequences for control characters due to bugs in editline
- // where "-k up" and "-k down" don't always work.
- ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow
- ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow
+ fprintf (m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs (toLine - fromLine));
}
- // Source $PWD/.editrc then $HOME/.editrc
- ::el_source (m_editline, NULL);
-
- // Always read through our callback function so we don't read
- // stuff we aren't supposed to. This also stops the extra echoing
- // that can happen when you have more input than editline can handle
- // at once.
- SetGetCharCallback(GetCharFromInputFileCallback);
-
- LoadHistory();
+ // 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);
}
-Editline::~Editline()
+void
+Editline::DisplayInput (int firstIndex)
{
- // 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();
+ 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 : "";
- // 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);
+ 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");
+ }
+}
- ::el_end(m_editline);
- m_editline = NULL;
+
+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::SetGetCharCallback (GetCharCallbackType callback)
+Editline::SaveEditedLine()
{
- ::el_set (m_editline, EL_GETCFN, callback);
+ const LineInfoW * info = el_wline (m_editline);
+ m_input_lines[m_current_line_index] = EditLineStringType (info->buffer, info->lastchar - info->buffer);
}
-bool
-Editline::LoadHistory ()
+StringList
+Editline::GetInputAsStringList(int line_count)
{
- if (m_history_sp)
- return m_history_sp->Load();
- return false;
+ 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;
}
-bool
-Editline::SaveHistory ()
+unsigned char
+Editline::RecallHistory (bool earlier)
{
- if (m_history_sp)
- return m_history_sp->Save();
- return false;
-}
+ 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);
-Error
-Editline::PrivateGetLine(std::string &line)
+ // 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)
{
- Error error;
- if (m_interrupted)
+ 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)
{
- error.SetErrorString("interrupted");
- return error;
+ 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;
}
- line.clear();
- if (m_editline != NULL)
+ if (m_multiline_enabled)
{
- int line_len = 0;
- // Call el_gets to prompt the user and read the user's input.
- const char *line_cstr = ::el_gets (m_editline, &line_len);
-
- static int save_errno = (line_len < 0) ? errno : 0;
-
- if (save_errno != 0)
+ // 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)
{
- error.SetError(save_errno, eErrorTypePOSIX);
+ // 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);
}
- else if (line_cstr)
+ m_current_line_rows = new_line_rows;
+ }
+
+ // Read an actual character
+ while (true)
+ {
+ lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
+ char ch = 0;
+ m_editor_getting_char = true;
+ int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
+ m_editor_getting_char = false;
+ if (read_count)
{
- // Decrement the length so we don't have newline characters in "line" for when
- // we assign the cstr into the std::string
- llvm::StringRef line_ref (line_cstr);
- line_ref = line_ref.rtrim("\n\r");
-
- if (!line_ref.empty() && !m_interrupted)
+#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(*c != EOF)
+ return 1;
+#endif
+ }
+ else
+ {
+ switch (status)
{
- // We didn't strip the newlines, we just adjusted the length, and
- // we want to add the history item with the newlines
- if (m_history_sp)
- m_history_sp->Enter(line_cstr);
-
- // Copy the part of the c string that we want (removing the newline chars)
- line = std::move(line_ref.str());
+ case lldb::eConnectionStatusInterrupted:
+ m_editor_status = EditorStatus::Interrupted;
+ printf ("^C\n");
+ return 0;
+
+ case lldb::eConnectionStatusSuccess: // Success
+ break;
+
+ 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;
}
}
}
- else
- {
- error.SetErrorString("the EditLine instance has been deleted");
- }
- return error;
}
+const char *
+Editline::Prompt()
+{
+ if (m_color_prompts)
+ m_needs_prompt_repaint = true;
+ return m_current_prompt.c_str();
+}
-Error
-Editline::GetLine(std::string &line, bool &interrupted)
+unsigned char
+Editline::BreakLineCommand (int ch)
{
- Error error;
- interrupted = false;
- line.clear();
+ // 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("");
- // Set arrow key bindings for up and down arrows for single line
- // mode where up and down arrows do prev/next history
- m_interrupted = false;
+ // Establish the new cursor position at the start of a line when inserting a line break
+ m_revert_cursor_index = 0;
- if (!m_got_eof)
+ // Don't perform end of input detection or automatic formatting when pasting
+ if (!IsInputPending (m_input_file))
{
- if (m_getting_line)
+ // 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)
{
- error.SetErrorString("already getting a line");
- return error;
+ 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;
+ }
}
- if (m_lines_curr_line > 0)
+
+ // Apply smart indentation
+ if (m_fix_indentation_callback)
{
- error.SetErrorString("already getting lines");
- return error;
+ 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);
}
- m_getting_line = true;
- error = PrivateGetLine(line);
- m_getting_line = false;
}
+
+ // 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;
+}
- interrupted = m_interrupted;
-
- if (m_got_eof && line.empty())
+unsigned char
+Editline::DeleteNextCharCommand (int ch)
+{
+ LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+
+ // Just delete the next character normally if possible
+ if (info->cursor < info->lastchar)
{
- // Only set the error if we didn't get an error back from PrivateGetLine()
- if (error.Success())
- error.SetErrorString("end of file");
+ info->cursor++;
+ el_deletestr (m_editline, 1);
+ return CC_REFRESH;
}
- return error;
+ // 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;
}
-size_t
-Editline::Push (const char *bytes, size_t len)
+unsigned char
+Editline::DeletePreviousCharCommand (int ch)
{
- if (m_editline)
+ LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+
+ // Just delete the previous character normally when not at the start of a line
+ if (info->cursor > info->buffer)
{
- // Must NULL terminate the string for el_push() so we stick it
- // into a std::string first
- ::el_push(m_editline,
- const_cast<char*>(std::string (bytes, len).c_str()));
- return len;
+ el_deletestr (m_editline, 1);
+ return CC_REFRESH;
}
- return 0;
+
+ // 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;
}
-
-Error
-Editline::GetLines(const std::string &end_line, StringList &lines, bool &interrupted)
+unsigned char
+Editline::PreviousLineCommand (int ch)
{
- Error error;
- interrupted = false;
- if (m_getting_line)
- {
- error.SetErrorString("already getting a line");
- return error;
+ SaveEditedLine();
+
+ if (m_current_line_index == 0) {
+ return RecallHistory (true);
}
- if (m_lines_curr_line > 0)
+
+ // 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())
{
- error.SetErrorString("already getting lines");
- return error;
+ m_input_lines.erase (m_input_lines.begin() + m_current_line_index);
+ fprintf (m_output_file, ANSI_CLEAR_BELOW);
}
- // Set arrow key bindings for up and down arrows for multiple line
- // mode where up and down arrows do edit prev/next line
- m_interrupted = false;
-
- LineStatus line_status = LineStatus::Success;
+ 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;
+}
- lines.Clear();
+unsigned char
+Editline::NextLineCommand (int ch)
+{
+ SaveEditedLine();
- FILE *out_file = GetOutputFile();
- FILE *err_file = GetErrorFile();
- m_lines_curr_line = 1;
- while (line_status != LineStatus::Done)
+ // Handle attempts to move down from the last line
+ if (m_current_line_index == m_input_lines.size() - 1)
{
- const uint32_t line_idx = m_lines_curr_line-1;
- if (line_idx >= lines.GetSize())
- lines.SetSize(m_lines_curr_line);
- m_lines_max_line = lines.GetSize();
- m_lines_command = Command::None;
- assert(line_idx < m_lines_max_line);
- std::string &line = lines[line_idx];
- error = PrivateGetLine(line);
- if (error.Fail())
+ // Don't add an extra line if the existing last line is blank, move through history instead
+ if (IsOnlySpaces())
{
- line_status = LineStatus::Error;
+ return RecallHistory (false);
}
- else if (m_interrupted)
+
+ // Determine indentation for the new line
+ int indentation = 0;
+ if (m_fix_indentation_callback)
{
- interrupted = true;
- line_status = LineStatus::Done;
+ StringList lines = GetInputAsStringList();
+ lines.AppendString("");
+ indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton);
}
- else
- {
- switch (m_lines_command)
- {
- case Command::None:
- if (m_line_complete_callback)
- {
- line_status = m_line_complete_callback (this,
- lines,
- line_idx,
- error,
- m_line_complete_callback_baton);
- }
- else if (line == end_line)
- {
- line_status = LineStatus::Done;
- }
+ 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;
+}
- if (line_status == LineStatus::Success)
- {
- ++m_lines_curr_line;
- // If we already have content for the next line because
- // we were editing previous lines, then populate the line
- // with the appropriate contents
- if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
- ::el_push (m_editline,
- const_cast<char*>(lines[line_idx+1].c_str()));
- }
- else if (line_status == LineStatus::Error)
- {
- // Clear to end of line ("ESC[K"), then print the error,
- // then go to the next line ("\n") and then move cursor up
- // two lines ("ESC[2A").
- fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString());
- }
- break;
- case Command::EditPrevLine:
- if (m_lines_curr_line > 1)
- {
- //::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line
- ::fprintf (out_file, "\033[1A\033[1000D\033[2K");
- if (!lines[line_idx-1].empty())
- ::el_push (m_editline,
- const_cast<char*>(lines[line_idx-1].c_str()));
- --m_lines_curr_line;
- }
- break;
- case Command::EditNextLine:
- // Allow the down arrow to create a new line
- ++m_lines_curr_line;
- //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size()));
- ::fprintf (out_file, "\033[1B\033[1000D\033[2K");
- if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
- ::el_push (m_editline,
- const_cast<char*>(lines[line_idx+1].c_str()));
- break;
- }
+unsigned char
+Editline::FixIndentationCommand (int ch)
+{
+ if (!m_fix_indentation_callback)
+ return CC_NORM;
+
+ // Insert the character by hand prior to correction
+ EditLineCharType inserted[] = { (EditLineCharType)ch, 0 };
+ el_winsertstr (m_editline, inserted);
+ SaveEditedLine();
+ StringList lines = GetInputAsStringList (m_current_line_index + 1);
+
+ // Determine the cursor position
+ LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+ int cursor_position = info->cursor - info->buffer;
+
+ int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton);
+
+ // Adjust the input buffer to correct indentation
+ if (indent_correction > 0)
+ {
+ info->cursor = info->buffer;
+ el_winsertstr (m_editline, EditLineStringType (indent_correction, EditLineCharType(' ')).c_str());
+ }
+ else if (indent_correction < 0)
+ {
+ info->cursor = info->buffer - indent_correction;
+ el_wdeletestr (m_editline, -indent_correction);
+ }
+ info->cursor = info->buffer + cursor_position + indent_correction;
+ return CC_REFRESH;
+}
+
+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 = (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;
}
- m_lines_curr_line = 0;
- m_lines_command = Command::None;
+ return CC_REFRESH;
+}
- // If we have a callback, call it one more time to let the
- // user know the lines are complete
- if (m_line_complete_callback && !interrupted)
- m_line_complete_callback (this,
- lines,
- UINT32_MAX,
- error,
- m_line_complete_callback_baton);
+unsigned char
+Editline::BufferStartCommand (int ch)
+{
+ SaveEditedLine();
+ MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ SetCurrentLine (0);
+ m_revert_cursor_index = 0;
+ return CC_NEWLINE;
+}
- return error;
+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::HandleCompletion (int ch)
+Editline::TabCommand (int ch)
{
- if (m_completion_callback == NULL)
+ if (m_completion_callback == nullptr)
return CC_ERROR;
-
- const LineInfo *line_info = ::el_line(m_editline);
+
+ 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,
@@ -536,25 +922,25 @@ Editline::HandleCompletion (int ch)
completions,
m_completion_callback_baton);
- FILE *out_file = GetOutputFile();
-
-// if (num_completions == -1)
-// {
-// ::el_insertstr (m_editline, m_completion_key);
-// return CC_REDISPLAY;
-// }
-// else
+ 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));
+ 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 != NULL && *completion_str != '\0')
+ const char *completion_str = completions.GetStringAtIndex (0);
+ if (completion_str != nullptr && *completion_str != '\0')
{
el_insertstr (m_editline, completion_str);
return CC_REDISPLAY;
@@ -563,15 +949,15 @@ Editline::HandleCompletion (int ch)
if (num_completions > 1)
{
int num_elements = num_completions + 1;
- ::fprintf (out_file, "\nAvailable completions:");
+ 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 (out_file, "\n\t%s", completion_str);
+ completion_str = completions.GetStringAtIndex (i);
+ fprintf (m_output_file, "\n\t%s", completion_str);
}
- ::fprintf (out_file, "\n");
+ fprintf (m_output_file, "\n");
}
else
{
@@ -585,17 +971,17 @@ Editline::HandleCompletion (int ch)
endpoint = num_elements;
for (; cur_pos < endpoint; cur_pos++)
{
- completion_str = completions.GetStringAtIndex(cur_pos);
- ::fprintf (out_file, "\n\t%s", completion_str);
+ completion_str = completions.GetStringAtIndex (cur_pos);
+ fprintf (m_output_file, "\n\t%s", completion_str);
}
if (cur_pos >= num_elements)
{
- ::fprintf (out_file, "\n");
+ fprintf (m_output_file, "\n");
break;
}
- ::fprintf (out_file, "\nMore (Y/n/a): ");
+ fprintf (m_output_file, "\nMore (Y/n/a): ");
reply = 'n';
got_char = el_getc(m_editline, &reply);
if (got_char == -1 || reply == 'n')
@@ -604,247 +990,388 @@ Editline::HandleCompletion (int ch)
page_size = num_elements - cur_pos;
}
}
-
+ DisplayInput();
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
}
-
- if (num_completions == 0)
- return CC_REFRESH_BEEP;
- else
- return CC_REDISPLAY;
+ return CC_REDISPLAY;
}
-Editline *
-Editline::GetClientData (::EditLine *e)
+void
+Editline::ConfigureEditor (bool multiline)
{
- Editline *editline = NULL;
- if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0)
- return editline;
- return NULL;
-}
+ 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();
+ }));
-FILE *
-Editline::GetInputFile ()
-{
- return GetFilePointer (m_editline, 0);
+ 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);
+ }
+ }
}
-FILE *
-Editline::GetOutputFile ()
+//------------------------------------------------------------------
+// Editline public methods
+//------------------------------------------------------------------
+
+Editline *
+Editline::InstanceFor (EditLine * editline)
{
- return GetFilePointer (m_editline, 1);
+ Editline * editor;
+ el_get (editline, EL_CLIENTDATA, &editor);
+ return editor;
}
-FILE *
-Editline::GetErrorFile ()
+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)
{
- return GetFilePointer (m_editline, 2);
+ // Get a shared history instance
+ m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
+ m_history_sp = EditlineHistory::GetHistory (m_editor_name);
}
-const char *
-Editline::GetPrompt()
+Editline::~Editline()
{
- if (m_prompt_with_line_numbers && m_lines_curr_line > 0)
+ if (m_editline)
{
- StreamString strm;
- strm.Printf("%3u: ", m_lines_curr_line);
- m_lines_prompt = std::move(strm.GetString());
- return m_lines_prompt.c_str();
- }
- else
- {
- return m_prompt.c_str();
+ // 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 *p)
+Editline::SetPrompt (const char * prompt)
{
- if (p && p[0])
- m_prompt = p;
- else
- m_prompt.clear();
- size_t start_pos = 0;
- size_t escape_pos;
- while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos)
- {
- m_prompt.insert(escape_pos, 1, k_prompt_escape_char);
- start_pos += 2;
- }
+ m_set_prompt = prompt == nullptr ? "" : prompt;
}
-FILE *
-Editline::GetFilePointer (::EditLine *e, int fd)
+void
+Editline::SetContinuationPrompt (const char * continuation_prompt)
{
- FILE *file_ptr = NULL;
- if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0)
- return file_ptr;
- return NULL;
+ m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt;
}
-unsigned char
-Editline::CallbackEditPrevLine (::EditLine *e, int ch)
+void
+Editline::TerminalSizeChanged()
{
- Editline *editline = GetClientData (e);
- if (editline->m_lines_curr_line > 1)
+ if (m_editline != nullptr)
{
- editline->m_lines_command = Command::EditPrevLine;
- return CC_NEWLINE;
+ 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;
+ }
}
- return CC_ERROR;
}
-unsigned char
-Editline::CallbackEditNextLine (::EditLine *e, int ch)
+
+const char *
+Editline::GetPrompt()
{
- Editline *editline = GetClientData (e);
- if (editline->m_lines_curr_line < editline->m_lines_max_line)
- {
- editline->m_lines_command = Command::EditNextLine;
- return CC_NEWLINE;
- }
- return CC_ERROR;
+ return m_set_prompt.c_str();
}
-unsigned char
-Editline::CallbackComplete (::EditLine *e, int ch)
+uint32_t
+Editline::GetCurrentLine()
{
- Editline *editline = GetClientData (e);
- if (editline)
- return editline->HandleCompletion (ch);
- return CC_ERROR;
+ return m_current_line_index;
}
-const char *
-Editline::GetPromptCallback (::EditLine *e)
+void
+Editline::Hide()
{
- Editline *editline = GetClientData (e);
- if (editline)
- return editline->GetPrompt();
- return "";
+ // Make sure we're at a stable location waiting for input
+ while (m_editor_status == EditorStatus::Editing && !m_editor_getting_char)
+ {
+ usleep(100000);
+ }
+
+ // Clear the existing input
+ if (m_editor_status == EditorStatus::Editing)
+ {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ }
}
-int
-Editline::GetCharFromInputFileCallback (EditLine *e, char *c)
+void
+Editline::Refresh()
{
- Editline *editline = GetClientData (e);
- if (editline && editline->m_got_eof == false)
+ if (m_editor_status == EditorStatus::Editing)
{
- FILE *f = editline->GetInputFile();
- if (f == NULL)
- {
- editline->m_got_eof = true;
- return 0;
- }
-
-
- while (1)
- {
- lldb::ConnectionStatus status = eConnectionStatusSuccess;
- char ch = 0;
- // When we start to call el_gets() the editline library needs to
- // output the prompt
- editline->m_getting_char.SetValue(true, eBroadcastAlways);
- const size_t n = editline->m_file.Read(&ch, 1, UINT32_MAX, status, NULL);
- editline->m_getting_char.SetValue(false, eBroadcastAlways);
- if (n)
- {
- if (ch == '\x04')
- {
- // Only turn a CTRL+D into a EOF if we receive the
- // CTRL+D an empty line, otherwise it will forward
- // delete the character at the cursor
- const LineInfo *line_info = ::el_line(e);
- if (line_info != NULL &&
- line_info->buffer == line_info->cursor &&
- line_info->cursor == line_info->lastchar)
- {
- editline->m_got_eof = true;
- break;
- }
- }
-
- if (status == eConnectionStatusEndOfFile)
- {
- editline->m_got_eof = true;
- break;
- }
- else
- {
- *c = ch;
- return 1;
- }
- }
- else
- {
- switch (status)
- {
- case eConnectionStatusInterrupted:
- editline->m_interrupted = true;
- *c = '\n';
- return 1;
-
- case eConnectionStatusSuccess: // Success
- break;
-
- case eConnectionStatusError: // Check GetError() for details
- case eConnectionStatusTimedOut: // Request timed out
- case eConnectionStatusEndOfFile: // End-of-file encountered
- case eConnectionStatusNoConnection: // No connection
- case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection
- editline->m_got_eof = true;
- break;
- }
- }
- }
+ DisplayInput();
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
}
- return 0;
}
-void
-Editline::Hide ()
+bool
+Editline::Interrupt()
{
- if (m_getting_line)
+ if (m_editor_status == EditorStatus::Editing)
{
- // If we are getting a line, we might have started to call el_gets() and
- // it might be printing the prompt. Here we make sure we are actually getting
- // a character. This way we know the entire prompt has been printed.
- TimeValue timeout = TimeValue::Now();
- timeout.OffsetWithSeconds(1);
- if (m_getting_char.WaitForValueEqualTo(true, &timeout))
- {
- FILE *out_file = GetOutputFile();
- if (out_file)
- {
- const LineInfo *line_info = ::el_line(m_editline);
- if (line_info)
- ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer));
- }
- }
+ return m_input_connection.InterruptRead();
}
+ return false; // Interrupt not handled as we weren't getting a line or lines
}
+void
+Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton)
+{
+ m_completion_callback = callback;
+ m_completion_callback_baton = baton;
+}
void
-Editline::Refresh()
+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)
{
- if (m_getting_line)
+ 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(""));
+
+ SetCurrentLine (0);
+ m_in_history = false;
+ m_editor_status = EditorStatus::Editing;
+ m_editor_getting_char = false;
+ m_revert_cursor_index = -1;
+
+ int count;
+ auto input = el_wgets (m_editline, &count);
+
+ interrupted = m_editor_status == EditorStatus::Interrupted;
+ if (!interrupted)
{
- // If we are getting a line, we might have started to call el_gets() and
- // it might be printing the prompt. Here we make sure we are actually getting
- // a character. This way we know the entire prompt has been printed.
- TimeValue timeout = TimeValue::Now();
- timeout.OffsetWithSeconds(1);
- if (m_getting_char.WaitForValueEqualTo(true, &timeout))
+ if (input == nullptr)
{
- ::el_set (m_editline, EL_REFRESH);
+ 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::Interrupt ()
+Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
{
- m_interrupted = true;
- if (m_getting_line || m_lines_curr_line > 0)
- return m_file.InterruptRead();
- return false; // Interrupt not handled as we weren't getting a line or lines
+ 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(""));
+
+ // Begin the line editing loop
+ DisplayInput();
+ SetCurrentLine (0);
+ MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart);
+ m_editor_status = EditorStatus::Editing;
+ m_editor_getting_char = false;
+ m_in_history = false;
+
+ m_revert_cursor_index = -1;
+ while (m_editor_status == EditorStatus::Editing)
+ {
+ 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;
}
diff --git a/source/Host/common/File.cpp b/source/Host/common/File.cpp
index 50513af2dc14..c3c77835ce86 100644
--- a/source/Host/common/File.cpp
+++ b/source/Host/common/File.cpp
@@ -896,7 +896,7 @@ File::CalculateInteractiveAndTerminal ()
{
m_is_interactive = eLazyBoolNo;
m_is_real_terminal = eLazyBoolNo;
-#ifdef _WIN32
+#if (defined(_WIN32) || defined(__ANDROID_NDK__))
if (_isatty(fd))
{
m_is_interactive = eLazyBoolYes;
diff --git a/source/Host/common/FileSpec.cpp b/source/Host/common/FileSpec.cpp
index 8c4014c074f5..0af0556d30c9 100644
--- a/source/Host/common/FileSpec.cpp
+++ b/source/Host/common/FileSpec.cpp
@@ -56,7 +56,8 @@ GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr)
}
// Resolves the username part of a path of the form ~user/other/directories, and
-// writes the result into dst_path.
+// 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)
{
@@ -66,9 +67,9 @@ FileSpec::ResolveUsername (llvm::SmallVectorImpl<char> &path)
llvm::StringRef path_str(path.data());
size_t slash_pos = path_str.find_first_of("/", 1);
- if (slash_pos == 1)
+ if (slash_pos == 1 || path.size() == 1)
{
- // A path of the form ~/ resolves to the current user's home dir
+ // 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;
@@ -166,10 +167,10 @@ FileSpec::Resolve (llvm::SmallVectorImpl<char> &path)
llvm::sys::fs::make_absolute(path);
}
-FileSpec::FileSpec()
- : m_directory()
- , m_filename()
- , m_syntax(FileSystem::GetNativePathSyntax())
+FileSpec::FileSpec() :
+ m_directory(),
+ m_filename(),
+ m_syntax(FileSystem::GetNativePathSyntax())
{
}
@@ -180,7 +181,8 @@ FileSpec::FileSpec()
FileSpec::FileSpec(const char *pathname, bool resolve_path, PathSyntax syntax) :
m_directory(),
m_filename(),
- m_is_resolved(false)
+ m_is_resolved(false),
+ m_syntax(syntax)
{
if (pathname && pathname[0])
SetFile(pathname, resolve_path, syntax);
@@ -266,14 +268,18 @@ FileSpec::SetFile (const char *pathname, bool resolve, PathSyntax syntax)
return;
llvm::SmallString<64> normalized(pathname);
- Normalize(normalized, syntax);
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())
@@ -446,15 +452,129 @@ FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full)
}
bool
-FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full)
+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
+ 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::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)
+ {
+ 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
@@ -502,7 +622,10 @@ FileSpec::ResolveExecutableLocation ()
if (file_cstr)
{
const std::string file_str (file_cstr);
- std::string path = llvm::sys::FindProgramByName (file_str);
+ 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())
{
@@ -946,6 +1069,8 @@ FileSpec::EnumerateDirectory
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)
@@ -990,7 +1115,14 @@ FileSpec::EnumerateDirectory
if (call_callback)
{
char child_path[PATH_MAX];
- const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name);
+
+ // 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
@@ -1208,18 +1340,31 @@ FileSpec::IsSourceImplementationFile () const
bool
FileSpec::IsRelativeToCurrentWorkingDirectory () const
{
- const char *directory = m_directory.GetCString();
- if (directory && directory[0])
+ const char *dir = m_directory.GetCString();
+ llvm::StringRef directory(dir ? dir : "");
+
+ if (directory.size() > 0)
{
- // If the path doesn't start with '/' or '~', return true
- switch (directory[0])
+ if (m_syntax == ePathSyntaxWindows)
{
- case '/':
- case '~':
- return false;
- default:
+ if (directory.size() >= 2 && directory[1] == ':')
+ return false;
+ if (directory[0] == '/')
+ return false;
return true;
}
+ else
+ {
+ // If the path doesn't start with '/' or '~', return true
+ switch (directory[0])
+ {
+ case '/':
+ case '~':
+ return false;
+ default:
+ return true;
+ }
+ }
}
else if (m_filename)
{
diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp
index 00c2fa37b383..c8daa175d1bd 100644
--- a/source/Host/common/Host.cpp
+++ b/source/Host/common/Host.cpp
@@ -14,11 +14,7 @@
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
-#ifdef _WIN32
-#include "lldb/Host/windows/windows.h"
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
+#ifndef _WIN32
#include <unistd.h>
#include <dlfcn.h>
#include <grp.h>
@@ -27,11 +23,6 @@
#include <sys/stat.h>
#endif
-#if !defined (__GNU__) && !defined (_WIN32)
-// Does not exist under GNU/HURD or Windows
-#include <sys/sysctl.h>
-#endif
-
#if defined (__APPLE__)
#include <mach/mach_port.h>
#include <mach/mach_init.h>
@@ -39,7 +30,9 @@
#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
@@ -51,33 +44,30 @@
// 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/ConstString.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
-#include "lldb/Core/StreamString.h"
-#include "lldb/Core/ThreadSafeSTLMap.h"
-#include "lldb/Host/Config.h"
-#include "lldb/Host/Endian.h"
#include "lldb/Host/FileSpec.h"
-#include "lldb/Host/FileSystem.h"
-#include "lldb/Host/Mutex.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/MonitoringProcessLauncher.h"
+#include "lldb/Host/ProcessLauncher.h"
+#include "lldb/Host/ThreadLauncher.h"
#include "lldb/lldb-private-forward.h"
#include "lldb/Target/FileAction.h"
-#include "lldb/Target/Process.h"
#include "lldb/Target/ProcessLaunchInfo.h"
#include "lldb/Target/TargetList.h"
#include "lldb/Utility/CleanUp.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/Host.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/raw_ostream.h"
+#if defined(_WIN32)
+#include "lldb/Host/windows/ProcessLauncherWindows.h"
+#else
+#include "lldb/Host/posix/ProcessLauncherPosix.h"
+#endif
#if defined (__APPLE__)
#ifndef _POSIX_SPAWN_DISABLE_ASLR
@@ -95,13 +85,6 @@ extern "C"
using namespace lldb;
using namespace lldb_private;
-// Define maximum thread name length
-#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__NetBSD__)
-uint32_t const Host::MAX_THREAD_NAME_LENGTH = 16;
-#else
-uint32_t const Host::MAX_THREAD_NAME_LENGTH = std::numeric_limits<uint32_t>::max ();
-#endif
-
#if !defined (__APPLE__) && !defined (_WIN32)
struct MonitorInfo
{
@@ -114,16 +97,9 @@ struct MonitorInfo
static thread_result_t
MonitorChildProcessThreadFunction (void *arg);
-lldb::thread_t
-Host::StartMonitoringChildProcess
-(
- Host::MonitorChildProcessCallback callback,
- void *callback_baton,
- lldb::pid_t pid,
- bool monitor_signals
-)
+HostThread
+Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals)
{
- lldb::thread_t thread = LLDB_INVALID_HOST_THREAD;
MonitorInfo * info_ptr = new MonitorInfo();
info_ptr->pid = pid;
@@ -132,26 +108,11 @@ Host::StartMonitoringChildProcess
info_ptr->monitor_signals = monitor_signals;
char thread_name[256];
-
- if (Host::MAX_THREAD_NAME_LENGTH <= 16)
- {
- // On some platforms, the thread name is limited to 16 characters. We need to
- // abbreviate there or the pid info would get truncated.
- ::snprintf (thread_name, sizeof(thread_name), "wait4(%" PRIu64 ")", pid);
- }
- else
- {
- ::snprintf (thread_name, sizeof(thread_name), "<lldb.host.wait4(pid=%" PRIu64 ")>", pid);
- }
-
- thread = ThreadCreate (thread_name,
- MonitorChildProcessThreadFunction,
- info_ptr,
- NULL);
-
- return thread;
+ ::snprintf(thread_name, sizeof(thread_name), "<lldb.host.wait4(pid=%" PRIu64 ")>", pid);
+ return ThreadLauncher::LaunchThread(thread_name, MonitorChildProcessThreadFunction, info_ptr, NULL);
}
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
//------------------------------------------------------------------
// Scoped class that will disable thread canceling when it is
// constructed, and exception safely restore the previous value it
@@ -166,7 +127,6 @@ public:
int err = ::pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &m_old_state);
if (err != 0)
m_old_state = -1;
-
}
~ScopedPThreadCancelDisabler()
@@ -179,6 +139,7 @@ public:
private:
int m_old_state; // Save the old cancelability state.
};
+#endif // __ANDROID_NDK__
static thread_result_t
MonitorChildProcessThreadFunction (void *arg)
@@ -212,11 +173,14 @@ MonitorChildProcessThreadFunction (void *arg)
log->Printf("%s ::wait_pid (pid = %" PRIi32 ", &status, options = %i)...", function, pid, options);
// Wait for all child processes
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
::pthread_testcancel ();
+#endif
// Get signals from all children with same process group of pid
const ::pid_t wait_pid = ::waitpid (pid, &status, options);
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
::pthread_testcancel ();
-
+#endif
if (wait_pid == -1)
{
if (errno == EINTR)
@@ -261,7 +225,9 @@ MonitorChildProcessThreadFunction (void *arg)
// Scope for pthread_cancel_disabler
{
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
ScopedPThreadCancelDisabler pthread_cancel_disabler;
+#endif
log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS);
if (log)
@@ -349,6 +315,8 @@ Host::GetCurrentThreadID()
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
@@ -429,11 +397,6 @@ Host::WillTerminate ()
#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) // see macosx/Host.mm
void
-Host::ThreadCreated (const char *thread_name)
-{
-}
-
-void
Host::Backtrace (Stream &strm, uint32_t max_frames)
{
// TODO: Is there a way to backtrace the current process on other systems?
@@ -448,101 +411,8 @@ Host::GetEnvironment (StringList &env)
#endif // #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__)
-struct HostThreadCreateInfo
-{
- std::string thread_name;
- thread_func_t thread_fptr;
- thread_arg_t thread_arg;
-
- HostThreadCreateInfo (const char *name, thread_func_t fptr, thread_arg_t arg) :
- thread_name (name ? name : ""),
- thread_fptr (fptr),
- thread_arg (arg)
- {
- }
-};
-
-static thread_result_t
-#ifdef _WIN32
-__stdcall
-#endif
-ThreadCreateTrampoline (thread_arg_t arg)
-{
- HostThreadCreateInfo *info = (HostThreadCreateInfo *)arg;
- Host::ThreadCreated (info->thread_name.c_str());
- 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);
-}
-
-lldb::thread_t
-Host::ThreadCreate
-(
- const char *thread_name,
- thread_func_t thread_fptr,
- thread_arg_t thread_arg,
- Error *error
-)
-{
- lldb::thread_t thread = LLDB_INVALID_HOST_THREAD;
-
- // Host::ThreadCreateTrampoline will delete this pointer for us.
- HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo (thread_name, thread_fptr, thread_arg);
-
-#ifdef _WIN32
- thread = ::_beginthreadex(0, 0, ThreadCreateTrampoline, info_ptr, 0, NULL);
- int err = thread <= 0 ? GetLastError() : 0;
-#else
- int err = ::pthread_create (&thread, NULL, ThreadCreateTrampoline, info_ptr);
-#endif
- if (err == 0)
- {
- if (error)
- error->Clear();
- return thread;
- }
-
- if (error)
- error->SetError (err, eErrorTypePOSIX);
-
- return LLDB_INVALID_HOST_THREAD;
-}
-
#ifndef _WIN32
-bool
-Host::ThreadCancel (lldb::thread_t thread, Error *error)
-{
- int err = ::pthread_cancel (thread);
- if (error)
- error->SetError(err, eErrorTypePOSIX);
- return err == 0;
-}
-
-bool
-Host::ThreadDetach (lldb::thread_t thread, Error *error)
-{
- int err = ::pthread_detach (thread);
- if (error)
- error->SetError(err, eErrorTypePOSIX);
- return err == 0;
-}
-
-bool
-Host::ThreadJoin (lldb::thread_t thread, thread_result_t *thread_result_ptr, Error *error)
-{
- int err = ::pthread_join (thread, thread_result_ptr);
- if (error)
- error->SetError(err, eErrorTypePOSIX);
- return err == 0;
-}
-
lldb::thread_key_t
Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback)
{
@@ -563,99 +433,6 @@ Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value)
::pthread_setspecific (key, value);
}
-bool
-Host::SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name)
-{
-#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
- lldb::pid_t curr_pid = Host::GetCurrentProcessID();
- lldb::tid_t curr_tid = Host::GetCurrentThreadID();
- if (pid == LLDB_INVALID_PROCESS_ID)
- pid = curr_pid;
-
- if (tid == LLDB_INVALID_THREAD_ID)
- tid = curr_tid;
-
- // Set the pthread name if possible
- if (pid == curr_pid && tid == curr_tid)
- {
- if (::pthread_setname_np (name) == 0)
- return true;
- }
- return false;
-#elif defined (__FreeBSD__)
- lldb::pid_t curr_pid = Host::GetCurrentProcessID();
- lldb::tid_t curr_tid = Host::GetCurrentThreadID();
- if (pid == LLDB_INVALID_PROCESS_ID)
- pid = curr_pid;
-
- if (tid == LLDB_INVALID_THREAD_ID)
- tid = curr_tid;
-
- // Set the pthread name if possible
- if (pid == curr_pid && tid == curr_tid)
- {
- ::pthread_set_name_np (::pthread_self(), name);
- return true;
- }
- return false;
-#elif defined (__linux__) || defined (__GLIBC__)
- void *fn = dlsym (RTLD_DEFAULT, "pthread_setname_np");
- if (fn)
- {
- lldb::pid_t curr_pid = Host::GetCurrentProcessID();
- lldb::tid_t curr_tid = Host::GetCurrentThreadID();
- if (pid == LLDB_INVALID_PROCESS_ID)
- pid = curr_pid;
-
- if (tid == LLDB_INVALID_THREAD_ID)
- tid = curr_tid;
-
- if (pid == curr_pid && tid == curr_tid)
- {
- int (*pthread_setname_np_func)(pthread_t thread, const char *name);
- *reinterpret_cast<void **> (&pthread_setname_np_func) = fn;
-
- if (pthread_setname_np_func (::pthread_self(), name) == 0)
- return true;
- }
- }
- return false;
-#else
- return false;
-#endif
-}
-
-bool
-Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid,
- const char *thread_name, size_t len)
-{
- std::unique_ptr<char[]> namebuf(new char[len+1]);
-
- // 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.
- const char *lastdot = ::strrchr (thread_name, '.');
-
- if (lastdot && lastdot != thread_name)
- thread_name = lastdot + 1;
- ::strncpy (namebuf.get(), thread_name, len);
- namebuf[len] = 0;
-
- int namebuflen = strlen(namebuf.get());
- if (namebuflen > 0)
- {
- if (namebuf[namebuflen - 1] == '(' || namebuf[namebuflen - 1] == '>')
- {
- // Trim off trailing '(' and '>' characters for a bit more cleanup.
- namebuflen--;
- namebuf[namebuflen] = 0;
- }
- return Host::SetThreadName (pid, tid, namebuf.get());
- }
- return false;
-}
-
#endif
#if !defined (__APPLE__) // see Host.mm
@@ -680,12 +457,16 @@ 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);
}
+#else
+ assert(false && "dladdr() not supported on Android");
+#endif
return module_filespec;
}
@@ -699,28 +480,6 @@ Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach)
}
#endif
-lldb::TargetSP
-Host::GetDummyTarget (lldb_private::Debugger &debugger)
-{
- static TargetSP g_dummy_target_sp;
-
- // FIXME: Maybe the dummy target should be per-Debugger
- if (!g_dummy_target_sp || !g_dummy_target_sp->IsValid())
- {
- ArchSpec arch(Target::GetDefaultArchitecture());
- if (!arch.IsValid())
- arch = HostInfo::GetArchitecture();
- Error err = debugger.GetTargetList().CreateTarget(debugger,
- NULL,
- arch.GetTriple().getTriple().c_str(),
- false,
- NULL,
- g_dummy_target_sp);
- }
-
- return g_dummy_target_sp;
-}
-
struct ShellInfo
{
ShellInfo () :
@@ -770,14 +529,15 @@ Host::RunShellCommand (const char *command,
int *signo_ptr,
std::string *command_output_ptr,
uint32_t timeout_sec,
- const char *shell)
+ bool run_in_default_shell)
{
Error error;
ProcessLaunchInfo launch_info;
- if (shell && shell[0])
+ launch_info.SetArchitecture(HostInfo::GetArchitecture());
+ if (run_in_default_shell)
{
// Run the command in a shell
- launch_info.SetShell(shell);
+ launch_info.SetShell(HostInfo::GetDefaultShell());
launch_info.GetArguments().AppendArgument(command);
const bool localhost = true;
const bool will_debug = false;
@@ -798,9 +558,8 @@ Host::RunShellCommand (const char *command,
if (working_dir)
launch_info.SetWorkingDirectory(working_dir);
- char output_file_path_buffer[PATH_MAX];
- const char *output_file_path = NULL;
-
+ llvm::SmallString<PATH_MAX> output_file_path;
+
if (command_output_ptr)
{
// Create a temporary file to get the stdout/stderr and redirect the
@@ -809,21 +568,19 @@ Host::RunShellCommand (const char *command,
FileSpec tmpdir_file_spec;
if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
{
- tmpdir_file_spec.AppendPathComponent("lldb-shell-output.XXXXXX");
- strncpy(output_file_path_buffer, tmpdir_file_spec.GetPath().c_str(), sizeof(output_file_path_buffer));
+ tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%");
+ llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), output_file_path);
}
else
{
- strncpy(output_file_path_buffer, "/tmp/lldb-shell-output.XXXXXX", sizeof(output_file_path_buffer));
+ llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "", output_file_path);
}
-
- output_file_path = ::mktemp(output_file_path_buffer);
}
launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
- if (output_file_path)
+ if (!output_file_path.empty())
{
- launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path, false, true);
+ launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path.c_str(), false, true);
launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
}
else
@@ -882,7 +639,7 @@ Host::RunShellCommand (const char *command,
if (command_output_ptr)
{
command_output_ptr->clear();
- FileSpec file_spec(output_file_path, File::eOpenOptionRead);
+ FileSpec file_spec(output_file_path.c_str(), File::eOpenOptionRead);
uint64_t file_size = file_spec.GetByteSize();
if (file_size > 0)
{
@@ -901,8 +658,9 @@ Host::RunShellCommand (const char *command,
shell_info->can_delete.SetValue(true, eBroadcastAlways);
}
- if (output_file_path)
- ::unlink (output_file_path);
+ FileSpec output_file_spec(output_file_path.c_str(), false);
+ if (FileSystem::GetFileExists(output_file_spec))
+ FileSystem::Unlink(output_file_path.c_str());
// 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...
@@ -914,13 +672,13 @@ Host::RunShellCommand (const char *command,
// systems
#if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) || defined(__NetBSD__)
-
// this method needs to be visible to macosx/Host.cpp and
// common/Host.cpp.
short
-Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info)
+Host::GetPosixspawnFlags(const ProcessLaunchInfo &launch_info)
{
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
#if defined (__APPLE__)
@@ -963,12 +721,17 @@ Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info)
#endif
#endif // #if defined (__APPLE__)
return flags;
+#else
+ assert(false && "Host::GetPosixspawnFlags() not supported on Android");
+ return 0;
+#endif
}
Error
-Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid)
+Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid)
{
Error error;
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
posix_spawnattr_t attr;
@@ -1086,6 +849,7 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i
#endif
}
+ ::pid_t result_pid = LLDB_INVALID_PROCESS_ID;
const size_t num_file_actions = launch_info.GetNumFileActions ();
if (num_file_actions > 0)
{
@@ -1110,21 +874,13 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i
}
}
- error.SetError (::posix_spawnp (&pid,
- exe_path,
- &file_actions,
- &attr,
- argv,
- envp),
- eErrorTypePOSIX);
+ 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 )",
- pid, exe_path, static_cast<void*>(&file_actions),
- static_cast<void*>(&attr),
- reinterpret_cast<const void*>(argv),
- reinterpret_cast<const void*>(envp));
+ 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)
@@ -1135,20 +891,13 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i
}
else
{
- error.SetError (::posix_spawnp (&pid,
- exe_path,
- NULL,
- &attr,
- argv,
- envp),
- eErrorTypePOSIX);
+ 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 )",
- pid, exe_path, static_cast<void*>(&attr),
- reinterpret_cast<const void*>(argv),
- reinterpret_cast<const void*>(envp));
+ 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)
@@ -1156,6 +905,7 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i
}
}
}
+ pid = result_pid;
if (working_dir)
{
@@ -1171,6 +921,9 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i
}
#endif
}
+#else
+ error.SetErrorString("Host::LaunchProcessPosixSpawn() not supported on Android");
+#endif
return error;
}
@@ -1178,6 +931,7 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i
bool
Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Error &error)
{
+#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__)
if (info == NULL)
return false;
@@ -1240,97 +994,40 @@ Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *
break;
}
return error.Success();
+#else
+ error.SetErrorString("Host::AddPosixSpawnFileAction() not supported on Android");
+ return false;
+#endif
}
-
#endif // LaunchProcedssPosixSpawn: Apple, Linux, FreeBSD and other GLIBC systems
-
-#if 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)
{
- Error error;
- char exe_path[PATH_MAX];
-
- PlatformSP host_platform_sp (Platform::GetDefaultPlatform ());
-
- const ArchSpec &arch_spec = launch_info.GetArchitecture();
-
- FileSpec exe_spec(launch_info.GetExecutableFile());
-
- FileSpec::FileType file_type = exe_spec.GetFileType();
- if (file_type != FileSpec::eFileTypeRegular)
- {
- lldb::ModuleSP exe_module_sp;
- error = host_platform_sp->ResolveExecutable (exe_spec,
- arch_spec,
- exe_module_sp,
- NULL);
-
- if (error.Fail())
- return error;
-
- if (exe_module_sp)
- exe_spec = exe_module_sp->GetFileSpec();
- }
-
- if (exe_spec.Exists())
- {
- exe_spec.GetPath (exe_path, sizeof(exe_path));
- }
- else
- {
- launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path));
- error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path);
- return error;
- }
-
- assert(!launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY));
-
- ::pid_t pid = LLDB_INVALID_PROCESS_ID;
-
- error = LaunchProcessPosixSpawn(exe_path, launch_info, pid);
+ std::unique_ptr<ProcessLauncher> delegate_launcher;
+#if defined(_WIN32)
+ delegate_launcher.reset(new ProcessLauncherWindows());
+#else
+ delegate_launcher.reset(new ProcessLauncherPosix());
+#endif
+ MonitoringProcessLauncher launcher(std::move(delegate_launcher));
- if (pid != LLDB_INVALID_PROCESS_ID)
- {
- // If all went well, then set the process ID into the launch info
- launch_info.SetProcessID(pid);
+ Error error;
+ HostProcess process = launcher.LaunchProcess(launch_info, error);
- Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ // TODO(zturner): It would be better if the entire HostProcess were returned instead of writing
+ // it into this structure.
+ launch_info.SetProcessID(process.GetProcessId());
- // Make sure we reap any processes we spawn or we will have zombies.
- if (!launch_info.MonitorProcess())
- {
- const bool monitor_signals = false;
- StartMonitoringChildProcess (Process::SetProcessExitStatus,
- NULL,
- pid,
- monitor_signals);
- if (log)
- log->PutCString ("monitored child process with default Process::SetProcessExitStatus.");
- }
- else
- {
- if (log)
- log->PutCString ("monitored child process with user-specified process monitor.");
- }
- }
- else
- {
- // Invalid process ID, something didn't go well
- if (error.Success())
- error.SetErrorString ("process launch failed for unknown reasons");
- }
return error;
}
-
#endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
#ifndef _WIN32
-
void
Host::Kill(lldb::pid_t pid, int signo)
{
diff --git a/source/Host/common/HostInfoBase.cpp b/source/Host/common/HostInfoBase.cpp
index 4eb43bfaf6ff..d65b79698384 100644
--- a/source/Host/common/HostInfoBase.cpp
+++ b/source/Host/common/HostInfoBase.cpp
@@ -18,7 +18,9 @@
#include "lldb/Host/HostInfoBase.h"
#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Host.h"
+#include "llvm/Support/raw_ostream.h"
#include <thread>
@@ -54,6 +56,7 @@ struct HostInfoBaseFields
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_tmp_dir;
@@ -94,6 +97,12 @@ HostInfoBase::GetNumberCPUS()
return g_fields->m_number_cpus;
}
+uint32_t
+HostInfoBase::GetMaxThreadNameLength()
+{
+ return 0;
+}
+
llvm::StringRef
HostInfoBase::GetVendorString()
{
@@ -190,6 +199,11 @@ HostInfoBase::GetLLDBPath(lldb::PathType type, FileSpec &file_spec)
if (log)
log->Printf("HostInfoBase::GetLLDBPath(ePathTypePythonDir) => '%s'", g_fields->m_lldb_python_dir.GetPath().c_str());
break;
+ case lldb::ePathTypeClangDir:
+ COMPUTE_LLDB_PATH(ComputeClangDirectory, g_fields->m_lldb_clang_resource_dir)
+ if (log)
+ log->Printf("HostInfoBase::GetLLDBPath(ePathTypeClangResourceDir) => '%s'", g_fields->m_lldb_clang_resource_dir.GetPath().c_str());
+ break;
case lldb::ePathTypeLLDBSystemPlugins:
COMPUTE_LLDB_PATH(ComputeSystemPluginsDirectory, g_fields->m_lldb_system_plugin_dir)
if (log)
@@ -252,19 +266,23 @@ HostInfoBase::ComputeTempFileDirectory(FileSpec &file_spec)
if (!tmpdir_cstr)
return false;
- StreamString pid_tmpdir;
- pid_tmpdir.Printf("%s/lldb", tmpdir_cstr);
- if (!FileSystem::MakeDirectory(pid_tmpdir.GetString().c_str(), eFilePermissionsDirectoryDefault).Success())
+ FileSpec temp_file_spec(tmpdir_cstr, false);
+ temp_file_spec.AppendPathComponent("lldb");
+ if (!FileSystem::MakeDirectory(temp_file_spec.GetPath().c_str(), eFilePermissionsDirectoryDefault).Success())
return false;
- pid_tmpdir.Printf("/%" PRIu64, Host::GetCurrentProcessID());
- if (!FileSystem::MakeDirectory(pid_tmpdir.GetString().c_str(), eFilePermissionsDirectoryDefault).Success())
+ std::string pid_str;
+ llvm::raw_string_ostream pid_stream(pid_str);
+ pid_stream << Host::GetCurrentProcessID();
+ temp_file_spec.AppendPathComponent(pid_stream.str().c_str());
+ std::string final_path = temp_file_spec.GetPath();
+ if (!FileSystem::MakeDirectory(final_path.c_str(), eFilePermissionsDirectoryDefault).Success())
return false;
// Make an atexit handler to clean up the process specify LLDB temp dir
// and all of its contents.
::atexit(CleanupProcessSpecificLLDBTempDir);
- file_spec.GetDirectory().SetCStringWithLength(pid_tmpdir.GetString().c_str(), pid_tmpdir.GetString().size());
+ file_spec.GetDirectory().SetCStringWithLength(final_path.c_str(), final_path.size());
return true;
}
@@ -283,6 +301,12 @@ HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec)
}
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.
diff --git a/source/Host/common/HostNativeThreadBase.cpp b/source/Host/common/HostNativeThreadBase.cpp
new file mode 100644
index 000000000000..9fea54d1e3fa
--- /dev/null
+++ b/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/source/Host/common/HostProcess.cpp b/source/Host/common/HostProcess.cpp
new file mode 100644
index 000000000000..58a214693a52
--- /dev/null
+++ b/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/source/Host/common/HostThread.cpp b/source/Host/common/HostThread.cpp
new file mode 100644
index 000000000000..7757477126c4
--- /dev/null
+++ b/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/source/Host/common/MonitoringProcessLauncher.cpp b/source/Host/common/MonitoringProcessLauncher.cpp
new file mode 100644
index 000000000000..0fad44a9ec08
--- /dev/null
+++ b/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/source/Host/common/NativeProcessProtocol.cpp b/source/Host/common/NativeProcessProtocol.cpp
index b7a77266c58c..e192f19a8896 100644
--- a/source/Host/common/NativeProcessProtocol.cpp
+++ b/source/Host/common/NativeProcessProtocol.cpp
@@ -13,6 +13,7 @@
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/State.h"
+#include "lldb/Host/Host.h"
#include "lldb/Target/NativeRegisterContext.h"
#include "NativeThreadProtocol.h"
@@ -44,6 +45,18 @@ NativeProcessProtocol::NativeProcessProtocol (lldb::pid_t pid) :
}
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.
@@ -110,9 +123,8 @@ NativeProcessProtocol::GetThreadAtIndex (uint32_t idx)
}
NativeThreadProtocolSP
-NativeProcessProtocol::GetThreadByID (lldb::tid_t tid)
+NativeProcessProtocol::GetThreadByIDUnlocked (lldb::tid_t tid)
{
- Mutex::Locker locker (m_threads_mutex);
for (auto thread_sp : m_threads)
{
if (thread_sp->GetID() == tid)
@@ -121,6 +133,13 @@ NativeProcessProtocol::GetThreadByID (lldb::tid_t tid)
return NativeThreadProtocolSP ();
}
+NativeThreadProtocolSP
+NativeProcessProtocol::GetThreadByID (lldb::tid_t tid)
+{
+ Mutex::Locker locker (m_threads_mutex);
+ return GetThreadByIDUnlocked (tid);
+}
+
bool
NativeProcessProtocol::IsAlive () const
{
diff --git a/source/Host/common/NativeProcessProtocol.h b/source/Host/common/NativeProcessProtocol.h
index 035a264e172e..19d8f353b26f 100644
--- a/source/Host/common/NativeProcessProtocol.h
+++ b/source/Host/common/NativeProcessProtocol.h
@@ -59,17 +59,25 @@ namespace lldb_private
//------------------------------------------------------------------
/// Sends a process a UNIX signal \a signal.
///
- /// Implementer note: the WillSignal ()/DidSignal () calls
- /// from the Process class are not replicated here since no
- /// concrete classes implemented any behavior for those and
- /// put all the work in DoSignal (...).
- ///
/// @return
/// Returns an error object.
//------------------------------------------------------------------
virtual Error
Signal (int signo) = 0;
+ //------------------------------------------------------------------
+ /// Tells a process to interrupt all operations as if by a Ctrl-C.
+ ///
+ /// The default implementation will send a local host's equivalent of
+ /// a SIGSTOP to the process via the NativeProcessProtocol::Signal()
+ /// operation.
+ ///
+ /// @return
+ /// Returns an error object.
+ //------------------------------------------------------------------
+ virtual Error
+ Interrupt ();
+
virtual Error
Kill () = 0;
@@ -296,7 +304,7 @@ namespace lldb_private
void
SetState (lldb::StateType state, bool notify_delegates = true);
- // Derived classes need not impelment this. It can be used as a
+ // Derived classes need not implement this. It can be used as a
// hook to clear internal caches that should be invalidated when
// stop ids change.
//
@@ -323,6 +331,9 @@ namespace lldb_private
void
NotifyDidExec ();
+ NativeThreadProtocolSP
+ GetThreadByIDUnlocked (lldb::tid_t tid);
+
private:
void
diff --git a/source/Host/common/NativeThreadProtocol.h b/source/Host/common/NativeThreadProtocol.h
index 9b404be500b9..15ecffe8b82d 100644
--- a/source/Host/common/NativeThreadProtocol.h
+++ b/source/Host/common/NativeThreadProtocol.h
@@ -31,7 +31,7 @@ namespace lldb_private
{
}
- virtual const char *
+ virtual std::string
GetName() = 0;
virtual lldb::StateType
diff --git a/source/Host/common/Pipe.cpp b/source/Host/common/Pipe.cpp
deleted file mode 100644
index 4db0e32c93b7..000000000000
--- a/source/Host/common/Pipe.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-//===-- Pipe.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/Pipe.h"
-
-#if defined(_WIN32)
-#include <io.h>
-#include <fcntl.h>
-#else
-#include <unistd.h>
-#endif
-
-using namespace lldb_private;
-
-int Pipe::kInvalidDescriptor = -1;
-
-enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
-
-Pipe::Pipe()
-{
- m_fds[READ] = Pipe::kInvalidDescriptor;
- m_fds[WRITE] = Pipe::kInvalidDescriptor;
-}
-
-Pipe::~Pipe()
-{
- Close();
-}
-
-bool
-Pipe::Open()
-{
- if (IsValid())
- return true;
-
-#ifdef _WIN32
- if (::_pipe(m_fds, 256, O_BINARY) == 0)
- return true;
-#else
- if (::pipe(m_fds) == 0)
- return true;
-#endif
- m_fds[READ] = Pipe::kInvalidDescriptor;
- m_fds[WRITE] = Pipe::kInvalidDescriptor;
- return false;
-}
-
-int
-Pipe::GetReadFileDescriptor() const
-{
- return m_fds[READ];
-}
-
-int
-Pipe::GetWriteFileDescriptor() const
-{
- return m_fds[WRITE];
-}
-
-int
-Pipe::ReleaseReadFileDescriptor()
-{
- const int fd = m_fds[READ];
- m_fds[READ] = Pipe::kInvalidDescriptor;
- return fd;
-}
-
-int
-Pipe::ReleaseWriteFileDescriptor()
-{
- const int fd = m_fds[WRITE];
- m_fds[WRITE] = Pipe::kInvalidDescriptor;
- return fd;
-}
-
-void
-Pipe::Close()
-{
- CloseReadFileDescriptor();
- CloseWriteFileDescriptor();
-}
-
-bool
-Pipe::ReadDescriptorIsValid() const
-{
- return m_fds[READ] != Pipe::kInvalidDescriptor;
-}
-
-bool
-Pipe::WriteDescriptorIsValid() const
-{
- return m_fds[WRITE] != Pipe::kInvalidDescriptor;
-}
-
-bool
-Pipe::IsValid() const
-{
- return ReadDescriptorIsValid() && WriteDescriptorIsValid();
-}
-
-bool
-Pipe::CloseReadFileDescriptor()
-{
- if (ReadDescriptorIsValid())
- {
- int err;
-#ifdef _WIN32
- err = _close(m_fds[READ]);
-#else
- err = close(m_fds[READ]);
-#endif
- m_fds[READ] = Pipe::kInvalidDescriptor;
- return err == 0;
- }
- return true;
-}
-
-bool
-Pipe::CloseWriteFileDescriptor()
-{
- if (WriteDescriptorIsValid())
- {
- int err;
-#ifdef _WIN32
- err = _close(m_fds[WRITE]);
-#else
- err = close(m_fds[WRITE]);
-#endif
- m_fds[WRITE] = Pipe::kInvalidDescriptor;
- return err == 0;
- }
- return true;
-}
-
-
-size_t
-Pipe::Read (void *buf, size_t num_bytes)
-{
- if (ReadDescriptorIsValid())
- {
- const int fd = GetReadFileDescriptor();
-#ifdef _WIN32
- return _read (fd, (char *)buf, num_bytes);
-#else
- return read (fd, buf, num_bytes);
-#endif
- }
- return 0; // Return 0 since errno won't be set if we didn't call read
-}
-
-size_t
-Pipe::Write (const void *buf, size_t num_bytes)
-{
- if (WriteDescriptorIsValid())
- {
- const int fd = GetWriteFileDescriptor();
-#ifdef _WIN32
- return _write (fd, (char *)buf, num_bytes);
-#else
- return write (fd, buf, num_bytes);
-#endif
- }
- return 0; // Return 0 since errno won't be set if we didn't call write
-}
-
diff --git a/source/Host/common/PipeBase.cpp b/source/Host/common/PipeBase.cpp
new file mode 100644
index 000000000000..a9d6e6f46c86
--- /dev/null
+++ b/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/source/Host/common/Socket.cpp b/source/Host/common/Socket.cpp
index 31e3228497ec..a6118eef7b79 100644
--- a/source/Host/common/Socket.cpp
+++ b/source/Host/common/Socket.cpp
@@ -18,6 +18,14 @@
#include "lldb/Host/TimeValue.h"
#include "lldb/Interpreter/Args.h"
+#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>
+#endif
+
#ifndef LLDB_DISABLE_POSIX
#include <arpa/inet.h>
#include <netdb.h>
@@ -40,6 +48,40 @@ typedef void * get_socket_option_arg_type;
const NativeSocket Socket::kInvalidSocketValue = -1;
#endif // #if defined(_WIN32)
+#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 {
+
+NativeSocket CreateSocket(const int domain, const int type, const int protocol, bool child_processes_inherit)
+{
+ auto socketType = type;
+#ifdef SOCK_CLOEXEC
+ if (!child_processes_inherit) {
+ socketType |= SOCK_CLOEXEC;
+ }
+#endif
+ return ::socket (domain, socketType, protocol);
+}
+
+NativeSocket Accept(NativeSocket sockfd, struct sockaddr *addr, socklen_t *addrlen, bool child_processes_inherit)
+{
+#ifdef SOCK_CLOEXEC
+ int flags = 0;
+ if (!child_processes_inherit) {
+ flags |= SOCK_CLOEXEC;
+ }
+ return ::accept4 (sockfd, addr, addrlen, flags);
+#else
+ return ::accept (sockfd, addr, addrlen);
+#endif
+}
+}
+
Socket::Socket(NativeSocket socket, SocketProtocol protocol, bool should_close)
: IOObject(eFDTypeSocket, should_close)
, m_protocol(protocol)
@@ -53,7 +95,7 @@ Socket::~Socket()
Close();
}
-Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket)
+Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket)
{
// Store the result in a unique_ptr in case we error out, the memory will get correctly freed.
std::unique_ptr<Socket> final_socket;
@@ -71,7 +113,7 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket)
return error;
// Create the socket
- sock = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ sock = CreateSocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, child_processes_inherit);
if (sock == kInvalidSocketValue)
{
// TODO: On Windows, use WSAGetLastError().
@@ -125,7 +167,7 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket)
return error;
}
-Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicate<uint16_t>* predicate)
+Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket, Predicate<uint16_t>* predicate)
{
std::unique_ptr<Socket> listen_socket;
NativeSocket listen_sock = kInvalidSocketValue;
@@ -134,7 +176,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicat
const sa_family_t family = AF_INET;
const int socktype = SOCK_STREAM;
const int protocol = IPPROTO_TCP;
- listen_sock = ::socket (family, socktype, protocol);
+ listen_sock = ::CreateSocket (family, socktype, protocol, child_processes_inherit);
if (listen_sock == kInvalidSocketValue)
{
error.SetErrorToErrno();
@@ -196,7 +238,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicat
return error;
}
-Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket)
+Error Socket::BlockingAccept(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket)
{
Error error;
std::string host_str;
@@ -235,7 +277,10 @@ Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket)
#endif
socklen_t accept_addr_len = sizeof accept_addr;
- int sock = ::accept (this->GetNativeSocket(), (struct sockaddr *)&accept_addr, &accept_addr_len);
+ int sock = Accept (this->GetNativeSocket(),
+ (struct sockaddr *)&accept_addr,
+ &accept_addr_len,
+ child_processes_inherit);
if (sock == kInvalidSocketValue)
{
@@ -280,7 +325,7 @@ Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket)
}
-Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, Socket *&recv_socket)
+Error Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&send_socket, Socket *&recv_socket)
{
std::unique_ptr<Socket> final_send_socket;
std::unique_ptr<Socket> final_recv_socket;
@@ -300,7 +345,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, So
// 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_fd = ::socket (AF_INET, SOCK_DGRAM, 0);
+ final_recv_fd = ::CreateSocket (AF_INET, SOCK_DGRAM, 0, child_processes_inherit);
if (final_recv_fd == kInvalidSocketValue)
{
// Socket creation failed...
@@ -351,9 +396,10 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, So
service_info_ptr != NULL;
service_info_ptr = service_info_ptr->ai_next)
{
- final_send_fd = ::socket (service_info_ptr->ai_family,
- service_info_ptr->ai_socktype,
- service_info_ptr->ai_protocol);
+ final_send_fd = ::CreateSocket (service_info_ptr->ai_family,
+ service_info_ptr->ai_socktype,
+ service_info_ptr->ai_protocol,
+ child_processes_inherit);
if (final_send_fd != kInvalidSocketValue)
{
@@ -380,7 +426,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, So
return error;
}
-Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket)
+Error Socket::UnixDomainConnect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket)
{
Error error;
#ifndef LLDB_DISABLE_POSIX
@@ -388,7 +434,7 @@ Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket)
// Open the socket that was passed in as an option
struct sockaddr_un saddr_un;
- int fd = ::socket (AF_UNIX, SOCK_STREAM, 0);
+ int fd = ::CreateSocket (AF_UNIX, SOCK_STREAM, 0, child_processes_inherit);
if (fd == kInvalidSocketValue)
{
error.SetErrorToErrno();
@@ -417,7 +463,7 @@ Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket)
return error;
}
-Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket)
+Error Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket)
{
Error error;
#ifndef LLDB_DISABLE_POSIX
@@ -427,7 +473,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket)
NativeSocket listen_fd = kInvalidSocketValue;
NativeSocket socket_fd = kInvalidSocketValue;
- listen_fd = ::socket (AF_UNIX, SOCK_STREAM, 0);
+ listen_fd = ::CreateSocket (AF_UNIX, SOCK_STREAM, 0, child_processes_inherit);
if (listen_fd == kInvalidSocketValue)
{
error.SetErrorToErrno();
@@ -449,7 +495,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket)
{
if (::listen (listen_fd, 5) == 0)
{
- socket_fd = ::accept (listen_fd, NULL, 0);
+ socket_fd = Accept (listen_fd, NULL, 0, child_processes_inherit);
if (socket_fd > 0)
{
final_socket.reset(new Socket(socket_fd, ProtocolUnixDomain, true));
diff --git a/source/Host/common/SocketAddress.cpp b/source/Host/common/SocketAddress.cpp
index a952a83185fb..6231631934df 100644
--- a/source/Host/common/SocketAddress.cpp
+++ b/source/Host/common/SocketAddress.cpp
@@ -214,6 +214,8 @@ SocketAddress::getaddrinfo (const char *host,
int ai_protocol,
int ai_flags)
{
+ Clear ();
+
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = ai_family;
@@ -221,15 +223,17 @@ SocketAddress::getaddrinfo (const char *host,
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;
- else
- Clear();
+ result = IsValid ();
+ }
:: freeaddrinfo (service_info_list);
- return IsValid();
+ return result;
}
diff --git a/source/Host/common/SoftwareBreakpoint.cpp b/source/Host/common/SoftwareBreakpoint.cpp
index fe2f504ebc71..d9d1fa67156f 100644
--- a/source/Host/common/SoftwareBreakpoint.cpp
+++ b/source/Host/common/SoftwareBreakpoint.cpp
@@ -119,6 +119,16 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll
return Error ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, bytes_read);
}
+ // Log 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%x)", __FUNCTION__, addr, i++, static_cast<int> (*read_byte));
+ }
+ }
+
// Write a software breakpoint in place of the original opcode.
lldb::addr_t bytes_written = 0;
error = process.WriteMemory (addr, bp_opcode_bytes, static_cast<lldb::addr_t> (bp_opcode_size), bytes_written);
@@ -207,7 +217,7 @@ SoftwareBreakpoint::DoDisable ()
if (m_opcode_size > 0)
{
- // Clear a software breakoint instruction
+ // 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));
@@ -265,7 +275,14 @@ SoftwareBreakpoint::DoDisable ()
{
// 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%x", __FUNCTION__, m_addr, i++, static_cast<int> (*verify_byte));
+ }
log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- SUCCESS", __FUNCTION__, m_addr);
+ }
return error;
}
else
diff --git a/source/Host/common/ThisThread.cpp b/source/Host/common/ThisThread.cpp
new file mode 100644
index 000000000000..289ec780e9fb
--- /dev/null
+++ b/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.find_last_of(".");
+ 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/source/Host/common/ThreadLauncher.cpp b/source/Host/common/ThreadLauncher.cpp
new file mode 100644
index 000000000000..ec7da325bf92
--- /dev/null
+++ b/source/Host/common/ThreadLauncher.cpp
@@ -0,0 +1,74 @@
+//===-- 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
+
+ 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/source/Host/freebsd/Host.cpp b/source/Host/freebsd/Host.cpp
index dc092e86d78b..2cbf4d8f4696 100644
--- a/source/Host/freebsd/Host.cpp
+++ b/source/Host/freebsd/Host.cpp
@@ -50,80 +50,6 @@ extern "C" {
using namespace lldb;
using namespace lldb_private;
-class FreeBSDThread
-{
-public:
- FreeBSDThread(const char *thread_name)
- {
- Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name);
- }
- static void PThreadDestructor (void *v)
- {
- delete (FreeBSDThread*)v;
- }
-};
-
-static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT;
-static pthread_key_t g_thread_create_key = 0;
-
-static void
-InitThreadCreated()
-{
- ::pthread_key_create (&g_thread_create_key, FreeBSDThread::PThreadDestructor);
-}
-
-void
-Host::ThreadCreated (const char *thread_name)
-{
- ::pthread_once (&g_thread_create_once, InitThreadCreated);
- if (g_thread_create_key)
- {
- ::pthread_setspecific (g_thread_create_key, new FreeBSDThread(thread_name));
- }
-
- Host::SetShortThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name, 16);
-}
-
-std::string
-Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid)
-{
- struct kinfo_proc *kp = nullptr, *nkp;
- size_t len = 0;
- int error;
- int name[4] = {
- CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, (int)pid
- };
-
- while (1) {
- error = sysctl(name, 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 std::string();
- }
- kp = nkp;
- continue;
- }
- if (error != 0)
- len = 0;
- break;
- }
-
- std::string thread_name;
- for (size_t i = 0; i < len / sizeof(*kp); i++) {
- if (kp[i].ki_tid == (int)tid) {
- thread_name = kp[i].ki_tdname;
- break;
- }
- }
- free(kp);
- return thread_name;
-}
-
void
Host::Backtrace (Stream &strm, uint32_t max_frames)
{
@@ -363,43 +289,19 @@ Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
lldb::DataBufferSP
Host::GetAuxvData(lldb_private::Process *process)
{
- int mib[2] = { CTL_KERN, KERN_PS_STRINGS };
- void *ps_strings_addr, *auxv_addr;
- size_t ps_strings_size = sizeof(void *);
- Elf_Auxinfo aux_info[AT_COUNT];
- struct ps_strings ps_strings;
- struct ptrace_io_desc pid;
+ 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(1024, 0));
-
- if (::sysctl(mib, 2, &ps_strings_addr, &ps_strings_size, NULL, 0) == 0) {
- pid.piod_op = PIOD_READ_D;
- pid.piod_addr = &ps_strings;
- pid.piod_offs = ps_strings_addr;
- pid.piod_len = sizeof(ps_strings);
- if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) {
- perror("failed to fetch ps_strings");
- buf_ap.release();
- goto done;
- }
-
- auxv_addr = ps_strings.ps_envstr + ps_strings.ps_nenvstr + 1;
-
- pid.piod_addr = aux_info;
- pid.piod_offs = auxv_addr;
- pid.piod_len = sizeof(aux_info);
- if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) {
- perror("failed to fetch aux_info");
- buf_ap.release();
- goto done;
- }
- memcpy(buf_ap->GetBytes(), aux_info, pid.piod_len);
+
+ 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 ps_strings");
+ perror("sysctl failed on auxv");
}
- done:
return buf_sp;
}
diff --git a/source/Host/freebsd/HostInfoFreeBSD.cpp b/source/Host/freebsd/HostInfoFreeBSD.cpp
index 78458259f0a2..d51109320c3b 100644
--- a/source/Host/freebsd/HostInfoFreeBSD.cpp
+++ b/source/Host/freebsd/HostInfoFreeBSD.cpp
@@ -17,6 +17,12 @@
using namespace lldb_private;
+uint32_t
+HostInfoFreeBSD::GetMaxThreadNameLength()
+{
+ return 16;
+}
+
bool
HostInfoFreeBSD::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
{
diff --git a/source/Host/freebsd/HostThreadFreeBSD.cpp b/source/Host/freebsd/HostThreadFreeBSD.cpp
new file mode 100644
index 000000000000..7d611bb6894b
--- /dev/null
+++ b/source/Host/freebsd/HostThreadFreeBSD.cpp
@@ -0,0 +1,77 @@
+//===-- 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>
+#include <pthread_np.h>
+#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/source/Host/freebsd/ThisThread.cpp b/source/Host/freebsd/ThisThread.cpp
new file mode 100644
index 000000000000..fb25847be24f
--- /dev/null
+++ b/source/Host/freebsd/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 <pthread_np.h>
+
+using namespace lldb_private;
+
+void
+ThisThread::SetName(llvm::StringRef name)
+{
+ ::pthread_set_name_np(::pthread_self(), name.data());
+}
+
+void
+ThisThread::GetName(llvm::SmallVectorImpl<char> &name)
+{
+ HostNativeThread::GetName(::pthread_getthreadid_np(), name);
+}
diff --git a/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/source/Host/posix/ConnectionFileDescriptorPosix.cpp
new file mode 100644
index 000000000000..38ddc0a49220
--- /dev/null
+++ b/source/Host/posix/ConnectionFileDescriptorPosix.cpp
@@ -0,0 +1,797 @@
+//===-- 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"
+
+// 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
+// Other libraries and framework includes
+#include "llvm/Support/ErrorHandling.h"
+#if defined(__APPLE__)
+#include "llvm/ADT/SmallVector.h"
+#endif
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Interpreter/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+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()
+{
+ 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])
+ {
+ if (strstr(s, "listen://") == s)
+ {
+ // listen://HOST:PORT
+ return SocketListen(s + strlen("listen://"), error_ptr);
+ }
+ else if (strstr(s, "accept://") == s)
+ {
+ // unix://SOCKNAME
+ return NamedSocketAccept(s + strlen("accept://"), error_ptr);
+ }
+ else if (strstr(s, "unix-accept://") == s)
+ {
+ // unix://SOCKNAME
+ return NamedSocketAccept(s + strlen("unix-accept://"), error_ptr);
+ }
+ else if (strstr(s, "connect://") == s)
+ {
+ return ConnectTCP(s + strlen("connect://"), error_ptr);
+ }
+ else if (strstr(s, "tcp-connect://") == s)
+ {
+ return ConnectTCP(s + strlen("tcp-connect://"), error_ptr);
+ }
+ else if (strstr(s, "udp://") == s)
+ {
+ return ConnectUDP(s + strlen("udp://"), error_ptr);
+ }
+#ifndef LLDB_DISABLE_POSIX
+ else if (strstr(s, "fd://") == s)
+ {
+ // Just passing a native file descriptor within this current process
+ // that is already opened (possibly from a service or other source).
+ s += strlen("fd://");
+ bool success = false;
+ int fd = Args::StringToSInt32(s, -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<Socket> tcp_socket;
+ tcp_socket.reset(new Socket(fd, Socket::ProtocolTcp, 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));
+ }
+ return eConnectionStatusSuccess;
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"fd://%s\"", s);
+ m_read_sp.reset();
+ m_write_sp.reset();
+ return eConnectionStatusError;
+ }
+ else if (strstr(s, "file://") == s)
+ {
+ // file:///PATH
+ const char *path = s + strlen("file://");
+ 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;
+
+ 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;
+ }
+ else if (m_shutting_down)
+ return eConnectionStatusError;
+
+ 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;
+}
+
+// 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)))
+ {
+ // We got a command to exit. Read the data from that pipe:
+ char buffer[16];
+ 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: %*s from the command channel.",
+ static_cast<void *>(this), static_cast<int>(bytes_read), buffer);
+ 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;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
+}
+
+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;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::SocketListen(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->BlockingAccept(s, m_child_processes_inherit, socket);
+ listening_socket_up.reset();
+ if (error_ptr)
+ *error_ptr = error;
+ if (error.Fail())
+ return eConnectionStatusError;
+
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
+}
+
+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;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
+}
+
+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);
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
+}
+
+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;
+}
diff --git a/source/Host/posix/HostInfoPosix.cpp b/source/Host/posix/HostInfoPosix.cpp
index 77fdc2b61a31..018d423ee9d3 100644
--- a/source/Host/posix/HostInfoPosix.cpp
+++ b/source/Host/posix/HostInfoPosix.cpp
@@ -69,6 +69,7 @@ HostInfoPosix::LookupUserName(uint32_t uid, std::string &user_name)
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;
@@ -94,6 +95,9 @@ HostInfoPosix::LookupGroupName(uint32_t gid, std::string &group_name)
}
}
group_name.clear();
+#else
+ assert(false && "getgrgid_r() not supported on Android");
+#endif
return NULL;
}
@@ -121,6 +125,12 @@ HostInfoPosix::GetEffectiveGroupID()
return getegid();
}
+FileSpec
+HostInfoPosix::GetDefaultShell()
+{
+ return FileSpec("/bin/sh", false);
+}
+
bool
HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec)
{
@@ -173,6 +183,7 @@ HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec)
bool
HostInfoPosix::ComputePythonDirectory(FileSpec &file_spec)
{
+#ifndef LLDB_DISABLE_PYTHON
FileSpec lldb_file_spec;
if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec))
return false;
@@ -190,4 +201,7 @@ HostInfoPosix::ComputePythonDirectory(FileSpec &file_spec)
file_spec.GetDirectory().SetCString(raw_path);
return true;
+#else
+ return false;
+#endif
}
diff --git a/source/Host/posix/HostProcessPosix.cpp b/source/Host/posix/HostProcessPosix.cpp
index 4618de4711de..8e19add048ee 100644
--- a/source/Host/posix/HostProcessPosix.cpp
+++ b/source/Host/posix/HostProcessPosix.cpp
@@ -1,4 +1,4 @@
-//===-- HostProcessWindows.cpp ----------------------------------*- C++ -*-===//
+//===-- HostProcessPosix.cpp ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "lldb/Host/Host.h"
#include "lldb/Host/posix/HostProcessPosix.h"
#include "lldb/Host/FileSystem.h"
@@ -16,49 +17,52 @@
using namespace lldb_private;
-const lldb::pid_t HostProcessPosix::kInvalidProcessId = 0;
+namespace
+{
+ const int kInvalidPosixProcess = 0;
+}
HostProcessPosix::HostProcessPosix()
-: m_pid(kInvalidProcessId)
+ : HostNativeProcessBase(kInvalidPosixProcess)
{
}
-HostProcessPosix::~HostProcessPosix()
+HostProcessPosix::HostProcessPosix(lldb::process_t process)
+ : HostNativeProcessBase(process)
{
}
-Error HostProcessPosix::Create(lldb::pid_t pid)
+HostProcessPosix::~HostProcessPosix()
{
- Error error;
- if (pid == kInvalidProcessId)
- error.SetErrorString("Attempt to create an invalid process");
-
- m_pid = pid;
- return error;
}
Error HostProcessPosix::Signal(int signo) const
{
- if (m_pid <= 0)
+ if (m_process == kInvalidPosixProcess)
{
Error error;
error.SetErrorString("HostProcessPosix refers to an invalid process");
return error;
}
- return HostProcessPosix::Signal(m_pid, signo);
+ return HostProcessPosix::Signal(m_process, signo);
}
-Error HostProcessPosix::Signal(lldb::pid_t pid, int signo)
+Error HostProcessPosix::Signal(lldb::process_t process, int signo)
{
Error error;
- if (-1 == ::kill(pid, signo))
+ if (-1 == ::kill(process, signo))
error.SetErrorToErrno();
return error;
}
+Error HostProcessPosix::Terminate()
+{
+ return Signal(SIGKILL);
+}
+
Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const
{
Error error;
@@ -66,7 +70,7 @@ Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const
// Use special code here because proc/[pid]/exe is a symbolic link.
char link_path[PATH_MAX];
char exe_path[PATH_MAX] = "";
- if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_pid) <= 0)
+ if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) <= 0)
{
error.SetErrorString("Unable to build /proc/<pid>/exe string");
return error;
@@ -92,12 +96,21 @@ Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const
lldb::pid_t HostProcessPosix::GetProcessId() const
{
- return m_pid;
+ 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/source/Host/posix/HostThreadPosix.cpp b/source/Host/posix/HostThreadPosix.cpp
new file mode 100644
index 000000000000..9c4892431938
--- /dev/null
+++ b/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/source/Host/posix/PipePosix.cpp b/source/Host/posix/PipePosix.cpp
new file mode 100644
index 000000000000..02838ec5124e
--- /dev/null
+++ b/source/Host/posix/PipePosix.cpp
@@ -0,0 +1,378 @@
+//===-- 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 <functional>
+#include <thread>
+
+#include <errno.h>
+#include <fcntl.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 Linux, FreeBSD v10 and higher.
+// TODO: Add more platforms that support pipe2.
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10)
+#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[READ] = PipePosix::kInvalidDescriptor;
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+}
+
+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::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(name.data());
+}
+
+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/source/Host/posix/ProcessLauncherPosix.cpp b/source/Host/posix/ProcessLauncherPosix.cpp
new file mode 100644
index 000000000000..dd5c5121a97b
--- /dev/null
+++ b/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);
+}