summaryrefslogtreecommitdiff
path: root/source/Host/common
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host/common')
-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
19 files changed, 2019 insertions, 1220 deletions
diff --git a/source/Host/common/Editline.cpp b/source/Host/common/Editline.cpp
index 7af9f39a78636..b82fbea90c6ca 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 50513af2dc143..c3c77835ce86a 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 8c4014c074f55..0af0556d30c9f 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 00c2fa37b3832..c8daa175d1bd4 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 4eb43bfaf6ff4..d65b796983842 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 0000000000000..9fea54d1e3faa
--- /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 0000000000000..58a214693a521
--- /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 0000000000000..7757477126c40
--- /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 0000000000000..0fad44a9ec089
--- /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 b7a77266c58c6..e192f19a8896e 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 035a264e172e1..19d8f353b26fe 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 9b404be500b97..15ecffe8b82df 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 4db0e32c93b7f..0000000000000
--- 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 0000000000000..a9d6e6f46c86e
--- /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 31e3228497ec4..a6118eef7b79e 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 a952a83185fb2..6231631934dfe 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 fe2f504ebc719..d9d1fa67156f9 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 0000000000000..289ec780e9fb3
--- /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 0000000000000..ec7da325bf92b
--- /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);
+}