aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/driver/Driver.cpp227
-rw-r--r--tools/driver/Driver.h17
-rw-r--r--tools/driver/ELWrapper.cpp422
-rw-r--r--tools/driver/ELWrapper.h122
-rw-r--r--tools/driver/GetOptWrapper.cpp33
-rw-r--r--tools/driver/GetOptWrapper.h49
-rw-r--r--tools/driver/IOChannel.cpp121
-rw-r--r--tools/driver/IOChannel.h42
-rw-r--r--tools/driver/Platform.cpp111
-rw-r--r--tools/driver/Platform.h113
-rw-r--r--tools/lldb-platform/lldb-platform.cpp270
11 files changed, 1357 insertions, 170 deletions
diff --git a/tools/driver/Driver.cpp b/tools/driver/Driver.cpp
index 875adc22283a..e2742425dd4c 100644
--- a/tools/driver/Driver.cpp
+++ b/tools/driver/Driver.cpp
@@ -9,19 +9,15 @@
#include "Driver.h"
-#include <getopt.h>
-#include <libgen.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <unistd.h>
+#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
-#include <inttypes.h>
#include <string>
+#include <thread>
#include "IOChannel.h"
#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBCommandInterpreter.h"
@@ -99,13 +95,21 @@ static OptionDefinition g_options[] =
"extensions have been implemented." },
{ LLDB_3_TO_5, false, "debug" , 'd', no_argument , 0, eArgTypeNone,
"Tells the debugger to print out extra information for debugging itself." },
+ { LLDB_3_TO_5, false, "source-quietly" , 'b', no_argument , 0, eArgTypeNone,
+ "Tells the debugger to print out extra information for debugging itself." },
{ LLDB_3_TO_5, false, "source" , 's', required_argument, 0, eArgTypeFilename,
- "Tells the debugger to read in and execute the file <file>, which should contain lldb commands." },
+ "Tells the debugger to read in and execute the lldb commands in the given file, after any file provided on the command line has been loaded." },
+ { LLDB_3_TO_5, false, "one-line" , 'o', required_argument, 0, eArgTypeNone,
+ "Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded." },
+ { LLDB_3_TO_5, false, "source-before-file" , 'S', required_argument, 0, eArgTypeFilename,
+ "Tells the debugger to read in and execute the lldb commands in the given file, before any file provided on the command line has been loaded." },
+ { LLDB_3_TO_5, false, "one-line-before-file" , 'O', required_argument, 0, eArgTypeNone,
+ "Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded." },
{ LLDB_3_TO_5, false, "editor" , 'e', no_argument , 0, eArgTypeNone,
"Tells the debugger to open source files using the host's \"external editor\" mechanism." },
{ LLDB_3_TO_5, false, "no-lldbinit" , 'x', no_argument , 0, eArgTypeNone,
"Do not automatically parse any '.lldbinit' files." },
- { LLDB_3_TO_5, false, "no-use-colors" , 'o', no_argument , 0, eArgTypeNone,
+ { LLDB_3_TO_5, false, "no-use-colors" , 'X', no_argument , 0, eArgTypeNone,
"Do not use colors." },
{ LLDB_OPT_SET_6, true , "python-path" , 'P', no_argument , 0, eArgTypeNone,
"Prints out the path to the lldb.py file for this version of lldb." },
@@ -147,7 +151,9 @@ Driver::CloseIOChannelFile ()
// Write an End of File sequence to the file descriptor to ensure any
// read functions can exit.
char eof_str[] = "\x04";
- ::write (m_editline_pty.GetMasterFileDescriptor(), eof_str, strlen(eof_str));
+ int mfd = m_editline_pty.GetMasterFileDescriptor();
+ if (mfd != -1)
+ ::write (m_editline_pty.GetMasterFileDescriptor(), eof_str, strlen(eof_str));
m_editline_pty.CloseMasterFileDescriptor();
@@ -341,6 +347,15 @@ ShowUsage (FILE *out, OptionDefinition *option_table, Driver::OptionData data)
indent_level -= 5;
+ fprintf (out, "\n%*sMultiple \"-s\" and \"-o\" options can be provided. They will be processed from left to right in order, "
+ "\n%*swith the source files and commands interleaved. The same is true of the \"-S\" and \"-O\" options."
+ "\n%*sThe before file and after file sets can intermixed freely, the command parser will sort them out."
+ "\n%*sThe order of the file specifiers (\"-c\", \"-f\", etc.) is not significant in this regard.\n\n",
+ indent_level, "",
+ indent_level, "",
+ indent_level, "",
+ indent_level, "");
+
fprintf (out, "\n%*s(If you don't provide -f then the first argument will be the file to be debugged"
"\n%*s so '%s -- <filename> [<ARG1> [<ARG2>]]' also works."
"\n%*s Remember to end the options with \"--\" if any of your arguments have a \"-\" in them.)\n\n",
@@ -390,8 +405,10 @@ Driver::OptionData::OptionData () :
m_script_lang (lldb::eScriptLanguageDefault),
m_core_file (),
m_crash_log (),
- m_source_command_files (),
+ m_initial_commands (),
+ m_after_file_commands (),
m_debug_mode (false),
+ m_source_quietly(false),
m_print_version (false),
m_print_python_path (false),
m_print_help (false),
@@ -412,8 +429,10 @@ Driver::OptionData::Clear ()
{
m_args.clear ();
m_script_lang = lldb::eScriptLanguageDefault;
- m_source_command_files.clear ();
+ m_initial_commands.clear ();
+ m_after_file_commands.clear ();
m_debug_mode = false;
+ m_source_quietly = false;
m_print_help = false;
m_print_version = false;
m_print_python_path = false;
@@ -424,6 +443,34 @@ Driver::OptionData::Clear ()
}
void
+Driver::OptionData::AddInitialCommand (const char *command, bool before_file, bool is_file, SBError &error)
+{
+ std::vector<std::pair<bool, std::string> > *command_set;
+ if (before_file)
+ command_set = &(m_initial_commands);
+ else
+ command_set = &(m_after_file_commands);
+
+ if (is_file)
+ {
+ SBFileSpec file(command);
+ if (file.Exists())
+ command_set->push_back (std::pair<bool, std::string> (true, optarg));
+ else if (file.ResolveExecutableLocation())
+ {
+ char final_path[PATH_MAX];
+ file.GetPath (final_path, sizeof(final_path));
+ std::string path_str (final_path);
+ command_set->push_back (std::pair<bool, std::string> (true, path_str));
+ }
+ else
+ error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", optarg);
+ }
+ else
+ command_set->push_back (std::pair<bool, std::string> (false, optarg));
+}
+
+void
Driver::ResetOptionValues ()
{
m_option_data.Clear ();
@@ -451,18 +498,60 @@ Driver::GetScriptLanguage() const
return m_option_data.m_script_lang;
}
-size_t
-Driver::GetNumSourceCommandFiles () const
-{
- return m_option_data.m_source_command_files.size();
-}
-
-const char *
-Driver::GetSourceCommandFileAtIndex (uint32_t idx) const
+void
+Driver::ExecuteInitialCommands (bool before_file)
{
- if (idx < m_option_data.m_source_command_files.size())
- return m_option_data.m_source_command_files[idx].c_str();
- return NULL;
+ size_t num_commands;
+ std::vector<std::pair<bool, std::string> > *command_set;
+ if (before_file)
+ command_set = &(m_option_data.m_initial_commands);
+ else
+ command_set = &(m_option_data.m_after_file_commands);
+
+ num_commands = command_set->size();
+ SBCommandReturnObject result;
+ bool old_async = GetDebugger().GetAsync();
+ GetDebugger().SetAsync(false);
+ for (size_t idx = 0; idx < num_commands; idx++)
+ {
+ bool is_file = (*command_set)[idx].first;
+ const char *command = (*command_set)[idx].second.c_str();
+ char command_string[PATH_MAX * 2];
+ const bool dump_stream_only_if_no_immediate = true;
+ const char *executed_command = command;
+ if (is_file)
+ {
+ ::snprintf (command_string, sizeof(command_string), "command source '%s'", command);
+ executed_command = command_string;
+ }
+
+ m_debugger.GetCommandInterpreter().HandleCommand (executed_command, result, false);
+ if (!m_option_data.m_source_quietly || result.Succeeded() == false)
+ {
+ const size_t output_size = result.GetOutputSize();
+ if (output_size > 0)
+ m_io_channel_ap->OutWrite (result.GetOutput(dump_stream_only_if_no_immediate), output_size, NO_ASYNC);
+ const size_t error_size = result.GetErrorSize();
+ if (error_size > 0)
+ m_io_channel_ap->OutWrite (result.GetError(dump_stream_only_if_no_immediate), error_size, NO_ASYNC);
+ }
+
+ if (result.Succeeded() == false)
+ {
+ char error_buffer[1024];
+ size_t error_size;
+ const char *type = before_file ? "before file" : "after_file";
+ if (is_file)
+ error_size = ::snprintf(error_buffer, sizeof(error_buffer), "Aborting %s command execution, command file: '%s' failed.\n", type, command);
+ else
+ error_size = ::snprintf(error_buffer, sizeof(error_buffer), "Aborting %s command execution, command: '%s' failed.\n", type, command);
+
+ m_io_channel_ap->OutWrite(error_buffer, error_size, NO_ASYNC);
+ break;
+ }
+ result.Clear();
+ }
+ GetDebugger().SetAsync(old_async);
}
bool
@@ -478,7 +567,7 @@ Driver::GetDebugMode() const
// if the user only wanted help or version information.
SBError
-Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
+Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
{
ResetOptionValues ();
@@ -623,7 +712,7 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
m_debugger.SkipAppInitFiles (true);
break;
- case 'o':
+ case 'X':
m_debugger.SetUseColor (false);
break;
@@ -658,6 +747,10 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
m_option_data.m_debug_mode = true;
break;
+ case 'q':
+ m_option_data.m_source_quietly = true;
+ break;
+
case 'n':
m_option_data.m_process_name = optarg;
break;
@@ -676,22 +769,17 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
}
break;
case 's':
- {
- SBFileSpec file(optarg);
- if (file.Exists())
- m_option_data.m_source_command_files.push_back (optarg);
- else if (file.ResolveExecutableLocation())
- {
- char final_path[PATH_MAX];
- file.GetPath (final_path, sizeof(final_path));
- std::string path_str (final_path);
- m_option_data.m_source_command_files.push_back (path_str);
- }
- else
- error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", optarg);
- }
+ m_option_data.AddInitialCommand(optarg, false, true, error);
+ break;
+ case 'o':
+ m_option_data.AddInitialCommand(optarg, false, false, error);
+ break;
+ case 'S':
+ m_option_data.AddInitialCommand(optarg, true, true, error);
+ break;
+ case 'O':
+ m_option_data.AddInitialCommand(optarg, true, false, error);
break;
-
default:
m_option_data.m_print_help = true;
error.SetErrorStringWithFormat ("unrecognized option %c", short_option);
@@ -712,12 +800,12 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
if (error.Fail() || m_option_data.m_print_help)
{
ShowUsage (out_fh, g_options, m_option_data);
- exit = true;
+ exiting = true;
}
else if (m_option_data.m_print_version)
{
::fprintf (out_fh, "%s\n", m_debugger.GetVersionString());
- exit = true;
+ exiting = true;
}
else if (m_option_data.m_print_python_path)
{
@@ -735,7 +823,7 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit)
}
else
::fprintf (out_fh, "<COULD NOT FIND PATH>\n");
- exit = true;
+ exiting = true;
}
else if (m_option_data.m_process_name.empty() && m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID)
{
@@ -1221,6 +1309,12 @@ Driver::EditLineInputReaderCallback
void
Driver::MainLoop ()
{
+#if defined(_MSC_VER)
+ m_editline_slave_fh = stdin;
+ FILE *editline_output_slave_fh = stdout;
+ lldb_utility::PseudoTerminal editline_output_pty;
+#else
+
char error_str[1024];
if (m_editline_pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, error_str, sizeof(error_str)) == false)
{
@@ -1281,6 +1375,7 @@ Driver::MainLoop ()
::setbuf (editline_output_slave_fh, NULL);
}
}
+#endif
// struct termios stdin_termios;
@@ -1322,6 +1417,7 @@ Driver::MainLoop ()
m_io_channel_ap.reset (new IOChannel(m_editline_slave_fh, editline_output_slave_fh, stdout, stderr, this));
+#if !defined (_MSC_VER)
SBCommunication out_comm_2("driver.editline_output");
out_comm_2.SetCloseOnEOF (false);
out_comm_2.AdoptFileDesriptor (editline_output_pty.GetMasterFileDescriptor(), false);
@@ -1332,6 +1428,7 @@ Driver::MainLoop ()
::fprintf (stderr, "error: failed to start libedit output read thread");
exit (5);
}
+#endif
struct winsize window_size;
@@ -1400,42 +1497,15 @@ Driver::MainLoop ()
}
// Now we handle options we got from the command line
- char command_string[PATH_MAX * 2];
- const size_t num_source_command_files = GetNumSourceCommandFiles();
- const bool dump_stream_only_if_no_immediate = true;
- if (num_source_command_files > 0)
- {
- for (size_t i=0; i < num_source_command_files; ++i)
- {
- const char *command_file = GetSourceCommandFileAtIndex(i);
- ::snprintf (command_string, sizeof(command_string), "command source '%s'", command_file);
- m_debugger.GetCommandInterpreter().HandleCommand (command_string, result, false);
- if (GetDebugMode())
- {
- result.PutError (m_debugger.GetErrorFileHandle());
- result.PutOutput (m_debugger.GetOutputFileHandle());
- }
-
- // if the command sourcing generated an error - dump the result object
- if (result.Succeeded() == false)
- {
- const size_t output_size = result.GetOutputSize();
- if (output_size > 0)
- m_io_channel_ap->OutWrite (result.GetOutput(dump_stream_only_if_no_immediate), output_size, NO_ASYNC);
- const size_t error_size = result.GetErrorSize();
- if (error_size > 0)
- m_io_channel_ap->OutWrite (result.GetError(dump_stream_only_if_no_immediate), error_size, NO_ASYNC);
- }
-
- result.Clear();
- }
- }
-
+ // First source in the commands specified to be run before the file arguments are processed.
+ ExecuteInitialCommands(true);
+
// Was there a core file specified?
std::string core_file_spec("");
if (!m_option_data.m_core_file.empty())
core_file_spec.append("--core ").append(m_option_data.m_core_file);
+ char command_string[PATH_MAX * 2];
const size_t num_args = m_option_data.m_args.size();
if (num_args > 0)
{
@@ -1487,6 +1557,9 @@ Driver::MainLoop ()
result.PutError(m_debugger.GetErrorFileHandle());
result.PutOutput(m_debugger.GetOutputFileHandle());
}
+
+ // Now execute the commands specified for after the file arguments are processed.
+ ExecuteInitialCommands(false);
SBEvent event;
@@ -1597,9 +1670,11 @@ Driver::MainLoop ()
master_out_comm.Disconnect();
master_out_comm.ReadThreadStop();
+#if !defined(_MSC_VER)
out_comm_2.SetReadThreadBytesReceivedCallback(NULL, NULL);
out_comm_2.Disconnect();
out_comm_2.ReadThreadStop();
+#endif
editline_output_pty.CloseMasterFileDescriptor();
reset_stdin_termios();
@@ -1714,15 +1789,15 @@ main (int argc, char const *argv[], const char *envp[])
{
Driver driver;
- bool exit = false;
- SBError error (driver.ParseArgs (argc, argv, stdout, exit));
+ bool exiting = false;
+ SBError error (driver.ParseArgs (argc, argv, stdout, exiting));
if (error.Fail())
{
const char *error_cstr = error.GetCString ();
if (error_cstr)
::fprintf (stderr, "error: %s\n", error_cstr);
}
- else if (!exit)
+ else if (!exiting)
{
driver.MainLoop ();
}
diff --git a/tools/driver/Driver.h b/tools/driver/Driver.h
index 2a4a27df4cdc..dcfd5ed11cd1 100644
--- a/tools/driver/Driver.h
+++ b/tools/driver/Driver.h
@@ -10,6 +10,7 @@
#ifndef lldb_Driver_h_
#define lldb_Driver_h_
+#include "Platform.h"
#include "lldb/Utility/PseudoTerminal.h"
#include <set>
@@ -83,12 +84,9 @@ public:
lldb::ScriptLanguage
GetScriptLanguage() const;
- size_t
- GetNumSourceCommandFiles () const;
-
- const char *
- GetSourceCommandFileAtIndex (uint32_t idx) const;
-
+ void
+ ExecuteInitialCommands (bool before_file);
+
bool
GetDebugMode() const;
@@ -102,14 +100,19 @@ public:
void
Clear();
+ void
+ AddInitialCommand (const char *command, bool before_file, bool is_file, lldb::SBError &error);
+
//static OptionDefinition m_cmd_option_table[];
std::vector<std::string> m_args;
lldb::ScriptLanguage m_script_lang;
std::string m_core_file;
std::string m_crash_log;
- std::vector<std::string> m_source_command_files;
+ std::vector<std::pair<bool,std::string> > m_initial_commands;
+ std::vector<std::pair<bool,std::string> > m_after_file_commands;
bool m_debug_mode;
+ bool m_source_quietly;
bool m_print_version;
bool m_print_python_path;
bool m_print_help;
diff --git a/tools/driver/ELWrapper.cpp b/tools/driver/ELWrapper.cpp
new file mode 100644
index 000000000000..258f47e5169c
--- /dev/null
+++ b/tools/driver/ELWrapper.cpp
@@ -0,0 +1,422 @@
+//===-- ELWrapper.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// this file is only relevant for Visual C++
+#if defined( _MSC_VER )
+
+#include "lldb/Host/windows/windows.h"
+
+#include "ELWrapper.h"
+#include <vector>
+#include <assert.h>
+
+// index one of the variable arguments
+// presuming "(EditLine *el, ..." is first in the argument list
+#define GETARG( X ) ( (void* ) *( ( (int**) &el ) + ((X) + 2) ) )
+
+// edit line EL_ADDFN function pointer type
+typedef unsigned char (*el_addfn_func)(EditLine *e, int ch);
+
+// edit line wrapper binding container
+struct el_binding
+{
+ //
+ const char *name;
+ const char *help;
+ // function pointer to callback routine
+ el_addfn_func func;
+ // ascii key this function is bound to
+ const char *key;
+};
+
+// stored key bindings
+static std::vector<el_binding*> _bindings;
+
+//TODO: this should infact be related to the exact edit line context we create
+static void *clientData = NULL;
+
+// store the current prompt string
+// default to what we expect to receive anyway
+static const char *_prompt = "(lldb) ";
+
+#if !defined( _WIP_INPUT_METHOD )
+
+static char *
+el_get_s (char *buffer, int chars)
+{
+ return gets_s(buffer, chars);
+}
+#else
+
+static void
+con_output (char _in)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get the cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // output this char
+ WriteConsoleOutputCharacterA( hout, &_in, 1, info.dwCursorPosition, &written );
+ // advance cursor position
+ info.dwCursorPosition.X++;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+}
+
+static void
+con_backspace (void)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // nudge cursor backwards
+ info.dwCursorPosition.X--;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+ // blank out the last character
+ WriteConsoleOutputCharacterA( hout, " ", 1, info.dwCursorPosition, &written );
+}
+
+static void
+con_return (void)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // move onto the new line
+ info.dwCursorPosition.X = 0;
+ info.dwCursorPosition.Y++;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+}
+
+static bool
+runBind (char _key)
+{
+ for ( int i=0; i<_bindings.size(); i++ )
+ {
+ el_binding *bind = _bindings[i];
+ if ( bind->key[0] == _key )
+ {
+ bind->func( (EditLine*) -1, _key );
+ return true;
+ }
+ }
+ return false;
+}
+
+// replacement get_s which is EL_BIND aware
+static char *
+el_get_s (char *buffer, int chars)
+{
+ //
+ char *head = buffer;
+ //
+ for ( ;; Sleep( 10 ) )
+ {
+ //
+ INPUT_RECORD _record;
+ //
+ DWORD _read = 0;
+ if ( ReadConsoleInputA( GetStdHandle( STD_INPUT_HANDLE ), &_record, 1, &_read ) == FALSE )
+ break;
+ // if we didnt read a key
+ if ( _read == 0 )
+ continue;
+ // only interested in key events
+ if ( _record.EventType != KEY_EVENT )
+ continue;
+ // is the key down
+ if (! _record.Event.KeyEvent.bKeyDown )
+ continue;
+ // read the ascii key character
+ char _key = _record.Event.KeyEvent.uChar.AsciiChar;
+ // non ascii conformant key press
+ if ( _key == 0 )
+ {
+ // check the scan code
+ // if VK_UP scroll back through history
+ // if VK_DOWN scroll forward through history
+ continue;
+ }
+ // try to execute any bind this key may have
+ if ( runBind( _key ) )
+ continue;
+ // if we read a return key
+ if ( _key == '\n' || _key == '\r' )
+ {
+ con_return( );
+ break;
+ }
+ // key is backspace
+ if ( _key == 0x8 )
+ {
+ // avoid deleting past beginning
+ if ( head > buffer )
+ {
+ con_backspace( );
+ head--;
+ }
+ continue;
+ }
+
+ // add this key to the input buffer
+ if ( (head-buffer) < (chars-1) )
+ {
+ con_output( _key );
+ *(head++) = _key;
+ }
+ }
+ // insert end of line character
+ *head = '\0';
+
+ return buffer;
+}
+#endif
+
+// edit line initalise
+EditLine *
+el_init (const char *, FILE *, FILE *, FILE *)
+{
+ //
+ SetConsoleTitleA( "lldb" );
+ // return dummy handle
+ return (EditLine*) -1;
+}
+
+const char *
+el_gets (EditLine *el, int *length)
+{
+ // print the prompt if we have one
+ if ( _prompt != NULL )
+ printf( _prompt );
+ // create a buffer for the user input
+ char *buffer = new char[ MAX_PATH ];
+ // try to get user input string
+ if ( el_get_s( buffer, MAX_PATH ) )
+ {
+ // get the string length in 'length'
+ while ( buffer[ *length ] != '\0' )
+ (*length)++;
+ // return the input buffer
+ // remember that this memory has the be free'd somewhere
+ return buffer;
+ }
+ else
+ {
+ // on error
+ delete [] buffer;
+ return NULL;
+ }
+}
+
+int
+el_set (EditLine *el, int code, ...)
+{
+ int **arg = (int**) &el;
+ //
+ switch ( code )
+ {
+ // edit line set prompt message
+ case ( EL_PROMPT ):
+ {
+ // EL_PROMPT, char *(*f)( EditLine *)
+ // define a prompt printing function as 'f', which is to return a string that
+ // contains the prompt.
+
+ // get the function pointer from the arg list
+ void *func_vp = (void*) *(arg+2);
+ // cast to suitable prototype
+ const char* (*func_fp)(EditLine*) = (const char*(*)(EditLine *)) func_vp;
+ // call to get the prompt as a string
+ _prompt = func_fp( el );
+ }
+ break;
+ case ( EL_EDITOR ):
+ {
+ // EL_EDITOR, const char *mode
+ // set editing mode to "emacs" or "vi"
+ }
+ break;
+ case ( EL_HIST ):
+ {
+ // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr
+ // defines which histroy function to use, which is usualy history(). Ptr should be the
+ // value returned by history_init().
+ }
+ break;
+ case ( EL_ADDFN ):
+ {
+ // EL_ADDFN, const char *name, const char *help, unsigned char (*func)(EditLine *e, int ch)
+ // add a user defined function, func), referred to as 'name' which is invoked when a key which is bound to 'name' is
+ // entered. 'help' is a description of 'name'. at involcation time, 'ch' is the key which caused the invocation. the
+ // return value of 'func()' should be one of:
+ // CC_NORM add a normal character
+ // CC_NEWLINE end of line was entered
+ // CC_EOF EOF was entered
+ // CC_ARGHACK expecting further command input as arguments, do nothing visually.
+ // CC_REFRESH refresh display.
+ // CC_REFRESH_BEEP refresh display and beep.
+ // CC_CURSOR cursor moved so update and perform CC_REFRESH
+ // CC_REDISPLAY redisplay entire input line. this is usefull if a key binding outputs extra information.
+ // CC_ERROR an error occured. beep and flush tty.
+ // CC_FATAL fatal error, reset tty to known state.
+
+ el_binding *binding = new el_binding;
+ binding->name = (const char *) GETARG( 0 );
+ binding->help = (const char *) GETARG( 1 );
+ binding->func = (el_addfn_func) GETARG( 2 );
+ binding->key = 0;
+ // add this to the bindings list
+ _bindings.push_back( binding );
+ }
+ break;
+ case ( EL_BIND ):
+ {
+ // EL_BIND, const char *, ..., NULL
+ // perform the BIND buildin command. Refer to editrc(5) for more information.
+
+ const char *name = (const char*) GETARG( 1 );
+
+ for ( int i=0; i<_bindings.size(); i++ )
+ {
+ el_binding *bind = _bindings[i];
+ if ( strcmp( bind->name, name ) == 0 )
+ {
+ bind->key = (const char *) GETARG( 0 );
+ break;
+ }
+ }
+
+ }
+ break;
+ case ( EL_CLIENTDATA ):
+ {
+ clientData = GETARG( 0 );
+ }
+ break;
+ default:
+ assert( !"Not Implemented!" );
+ }
+ return 0;
+}
+
+void
+el_end (EditLine *el)
+{
+ assert( !"Not implemented!" );
+}
+
+void
+el_reset (EditLine *)
+{
+ assert( !"Not implemented!" );
+}
+
+int
+el_getc (EditLine *, char *)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+void
+el_push (EditLine *, char *)
+{
+ assert( !"Not implemented!" );
+}
+
+void
+el_beep (EditLine *)
+{
+ Beep( 1000, 500 );
+}
+
+int
+el_parse (EditLine *, int, const char **)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+int
+el_get (EditLine *el, int code, ...)
+{
+ switch ( code )
+ {
+ case ( EL_CLIENTDATA ):
+ {
+ void **dout = (void**) GETARG( 0 );
+ *dout = clientData;
+ }
+ break;
+ default:
+ assert( !"Not implemented!" );
+ }
+ return 0;
+}
+
+int
+el_source (EditLine *el, const char *file)
+{
+ // init edit line by reading the contents of 'file'
+ // nothing to do here on windows...
+ return 0;
+}
+
+void
+el_resize (EditLine *)
+{
+ assert( !"Not implemented!" );
+}
+
+const LineInfo *
+el_line (EditLine *el)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+int
+el_insertstr (EditLine *, const char *)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+void
+el_deletestr (EditLine *, int)
+{
+ assert( !"Not implemented!" );
+}
+
+History *
+history_init (void)
+{
+ // return dummy handle
+ return (History*) -1;
+}
+
+void
+history_end (History *)
+{
+ assert( !"Not implemented!" );
+}
+
+int
+history (History *, HistEvent *, int op, ...)
+{
+ // perform operation 'op' on the history list with optional argumetns as needed by
+ // the operation.
+ return 0;
+}
+
+#endif
diff --git a/tools/driver/ELWrapper.h b/tools/driver/ELWrapper.h
new file mode 100644
index 000000000000..a30182d948b1
--- /dev/null
+++ b/tools/driver/ELWrapper.h
@@ -0,0 +1,122 @@
+//===-- ELWrapper.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include <stdio.h>
+
+// EditLine editor function return codes.
+// For user-defined function interface
+#define CC_NORM 0
+#define CC_NEWLINE 1
+#define CC_EOF 2
+#define CC_ARGHACK 3
+#define CC_REFRESH 4
+#define CC_CURSOR 5
+#define CC_ERROR 6
+#define CC_FATAL 7
+#define CC_REDISPLAY 8
+#define CC_REFRESH_BEEP 9
+
+// el_set/el_get parameters
+#define EL_PROMPT 0 // , el_pfunc_t
+#define EL_TERMINAL 1 // , const char *
+#define EL_EDITOR 2 // , const char *
+#define EL_SIGNAL 3 // , int);
+#define EL_BIND 4 // , const char *, ..., NULL
+#define EL_TELLTC 5 // , const char *, ..., NULL
+#define EL_SETTC 6 // , const char *, ..., NULL
+#define EL_ECHOTC 7 // , const char *, ..., NULL
+#define EL_SETTY 8 // , const char *, ..., NULL
+#define EL_ADDFN 9 // , const char *, const char *, el_func_t
+#define EL_HIST 10 // , hist_fun_t, const char *
+#define EL_EDITMODE 11 // , int
+#define EL_RPROMPT 12 // , el_pfunc_t
+#define EL_GETCFN 13 // , el_rfunc_t
+#define EL_CLIENTDATA 14 // , void *
+#define EL_UNBUFFERED 15 // , int
+#define EL_PREP_TERM 16 // , int
+#define EL_GETTC 17 // , const char *, ..., NULL
+#define EL_GETFP 18 // , int, FILE **
+#define EL_SETFP 19 // , int, FILE *
+#define EL_REFRESH 20 // , void
+
+#define EL_BUILTIN_GETCFN (NULL)
+
+// history defines
+#define H_FUNC 0 // , UTSL
+#define H_SETSIZE 1 // , const int
+#define H_GETSIZE 2 // , void
+#define H_FIRST 3 // , void
+#define H_LAST 4 // , void
+#define H_PREV 5 // , void
+#define H_NEXT 6 // , void
+#define H_CURR 8 // , const int
+#define H_SET 7 // , int
+#define H_ADD 9 // , const char *
+#define H_ENTER 10 // , const char *
+#define H_APPEND 11 // , const char *
+#define H_END 12 // , void
+#define H_NEXT_STR 13 // , const char *
+#define H_PREV_STR 14 // , const char *
+#define H_NEXT_EVENT 15 // , const int
+#define H_PREV_EVENT 16 // , const int
+#define H_LOAD 17 // , const char *
+#define H_SAVE 18 // , const char *
+#define H_CLEAR 19 // , void
+#define H_SETUNIQUE 20 // , int
+#define H_GETUNIQUE 21 // , void
+#define H_DEL 22 // , int
+
+struct EditLine
+{
+};
+
+struct LineInfo
+{
+ const char *buffer;
+ const char *cursor;
+ const char *lastchar;
+};
+
+struct History
+{
+};
+
+struct HistEvent
+{
+ int num;
+ const char *str;
+};
+
+extern "C"
+{
+ // edit line API
+ EditLine *el_init ( const char *, FILE *, FILE *, FILE * );
+ const char *el_gets ( EditLine *, int * );
+ int el_set ( EditLine *, int, ... );
+
+ void el_end ( EditLine * );
+ void el_reset ( EditLine * );
+ int el_getc ( EditLine *, char * );
+ void el_push ( EditLine *, char * );
+ void el_beep ( EditLine * );
+ int el_parse ( EditLine *, int, const char ** );
+ int el_get ( EditLine *, int, ... );
+ int el_source ( EditLine *, const char * );
+ void el_resize ( EditLine * );
+ const LineInfo *el_line ( EditLine * );
+ int el_insertstr( EditLine *, const char * );
+ void el_deletestr( EditLine *, int );
+
+ // history API
+ History *history_init( void );
+ void history_end ( History * );
+ int history ( History *, HistEvent *, int, ... );
+}; \ No newline at end of file
diff --git a/tools/driver/GetOptWrapper.cpp b/tools/driver/GetOptWrapper.cpp
new file mode 100644
index 000000000000..e7cdfd786c6e
--- /dev/null
+++ b/tools/driver/GetOptWrapper.cpp
@@ -0,0 +1,33 @@
+//===-- GetOptWrapper.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// this file is only relevant for Visual C++
+#if defined( _MSC_VER )
+
+#include "GetOptWrapper.h"
+
+/*
+
+// already defined in lldbHostCommon.lib due to 'getopt.inc'
+
+extern int
+getopt_long_only
+(
+ int ___argc,
+ char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts,
+ int *__longind
+)
+{
+ return -1;
+}
+*/
+
+#endif \ No newline at end of file
diff --git a/tools/driver/GetOptWrapper.h b/tools/driver/GetOptWrapper.h
new file mode 100644
index 000000000000..9c9cf03d7626
--- /dev/null
+++ b/tools/driver/GetOptWrapper.h
@@ -0,0 +1,49 @@
+//===-- GetOptWrapper.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_GetOptWrapper_h_
+#define lldb_GetOptWrapper_h_
+
+// from getopt.h
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+// defined int unistd.h
+extern int optreset;
+
+// from getopt.h
+extern char *optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+// option structure
+struct option
+{
+ const char *name;
+ // has_arg can't be an enum because some compilers complain about
+ // type mismatches in all the code that assumes it is an int.
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+//
+extern int
+getopt_long_only
+(
+ int ___argc,
+ char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts,
+ int *__longind
+);
+
+#endif // lldb_GetOptWrapper_h_ \ No newline at end of file
diff --git a/tools/driver/IOChannel.cpp b/tools/driver/IOChannel.cpp
index 7adf2e4e85e9..7cba73aaae8c 100644
--- a/tools/driver/IOChannel.cpp
+++ b/tools/driver/IOChannel.cpp
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Platform.h"
#include "IOChannel.h"
#include <map>
@@ -73,7 +74,8 @@ void
IOChannel::EraseCharsBeforeCursor ()
{
const LineInfo *line_info = el_line(m_edit_line);
- el_deletestr(m_edit_line, line_info->cursor - line_info->buffer);
+ if (line_info != NULL)
+ el_deletestr(m_edit_line, line_info->cursor - line_info->buffer);
}
unsigned char
@@ -205,6 +207,7 @@ IOChannel::IOChannel
m_read_thread_should_exit (false),
m_out_file (out),
m_err_file (err),
+ m_editline_out (editline_out),
m_command_queue (),
m_completion_key ("\t"),
m_edit_line (::el_init (SBHostOS::GetProgramFileSpec().GetFilename(), editline_in, editline_out, editline_out)),
@@ -212,50 +215,35 @@ IOChannel::IOChannel
m_history_event(),
m_getting_command (false),
m_expecting_prompt (false),
- m_prompt_str (),
+ m_prompt_str (),
m_refresh_request_pending (false)
{
assert (m_edit_line);
- ::el_set (m_edit_line, EL_PROMPT, el_prompt);
- ::el_set (m_edit_line, EL_EDITOR, "emacs");
- ::el_set (m_edit_line, EL_HIST, history, m_history);
-
- el_set (m_edit_line, EL_ADDFN, "lldb_complete",
- "LLDB completion function",
- IOChannel::ElCompletionFn);
+ el_set (m_edit_line, EL_PROMPT, el_prompt);
+ el_set (m_edit_line, EL_EDITOR, "emacs");
+ el_set (m_edit_line, EL_HIST, history, m_history);
+ el_set (m_edit_line, EL_ADDFN, "lldb_complete", "LLDB completion function", IOChannel::ElCompletionFn);
el_set (m_edit_line, EL_BIND, m_completion_key, "lldb_complete", NULL);
el_set (m_edit_line, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
el_set (m_edit_line, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does.
- el_set (m_edit_line, EL_BIND, "\e[3~", "ed-delete-next-char", NULL); // Fix the delete key.
+ el_set (m_edit_line, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
el_set (m_edit_line, EL_CLIENTDATA, this);
// Source $PWD/.editrc then $HOME/.editrc
- ::el_source (m_edit_line, NULL);
+ el_source (m_edit_line, NULL);
assert (m_history);
- ::history (m_history, &m_history_event, H_SETSIZE, 800);
- ::history (m_history, &m_history_event, H_SETUNIQUE, 1);
+ history (m_history, &m_history_event, H_SETSIZE, 800);
+ history (m_history, &m_history_event, H_SETUNIQUE, 1);
// Load history
HistorySaveLoad (false);
- // Set up mutex to make sure OutErr, OutWrite and RefreshPrompt do not interfere
- // with each other when writing.
-
- int error;
- ::pthread_mutexattr_t attr;
- error = ::pthread_mutexattr_init (&attr);
- assert (error == 0);
- error = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
- assert (error == 0);
- error = ::pthread_mutex_init (&m_output_mutex, &attr);
- assert (error == 0);
- error = ::pthread_mutexattr_destroy (&attr);
- assert (error == 0);
-
// Initialize time that ::el_gets was last called.
-
m_enter_elgets_time.tv_sec = 0;
m_enter_elgets_time.tv_usec = 0;
+
+ // set the initial state to non flushed
+ m_output_flushed = false;
}
IOChannel::~IOChannel ()
@@ -274,8 +262,6 @@ IOChannel::~IOChannel ()
::el_end (m_edit_line);
m_edit_line = NULL;
}
-
- ::pthread_mutex_destroy (&m_output_mutex);
}
void
@@ -299,16 +285,24 @@ IOChannel::HistorySaveLoad (bool save)
void
IOChannel::LibeditOutputBytesReceived (void *baton, const void *src, size_t src_len)
{
- // Make this a member variable.
- // static std::string prompt_str;
IOChannel *io_channel = (IOChannel *) baton;
- IOLocker locker (io_channel->m_output_mutex);
+ std::lock_guard<std::recursive_mutex> locker(io_channel->m_output_mutex);
const char *bytes = (const char *) src;
+ bool flush = false;
+
+ // See if we have a 'flush' synchronization point in there.
+ // this is performed from 'fputc ('\0', m_editline_out);' in LibeditGetInput()
+ if (src_len > 0 && bytes[src_len-1] == '\0')
+ {
+ src_len--;
+ flush = true;
+ }
+
if (io_channel->IsGettingCommand() && io_channel->m_expecting_prompt)
{
io_channel->m_prompt_str.append (bytes, src_len);
- // Log this to make sure the prompt is really what you think it is.
+ // Log this to make sure the prompt is really what you think it is.
if (io_channel->m_prompt_str.find (el_prompt(io_channel->m_edit_line)) == 0)
{
io_channel->m_expecting_prompt = false;
@@ -327,6 +321,15 @@ IOChannel::LibeditOutputBytesReceived (void *baton, const void *src, size_t src_
io_channel->m_refresh_request_pending = false;
io_channel->OutWrite (bytes, src_len, NO_ASYNC);
}
+
+#if !defined (_MSC_VER)
+ if (flush)
+ {
+ io_channel->m_output_flushed = true;
+ io_channel->m_output_cond.notify_all();
+ }
+#endif
+
}
IOChannel::LibeditGetInputResult
@@ -344,7 +347,28 @@ IOChannel::LibeditGetInput (std::string &new_line)
// Call el_gets to prompt the user and read the user's input.
const char *line = ::el_gets (m_edit_line, &line_len);
-
+
+#if !defined (_MSC_VER)
+ // Force the piped output from el_gets to finish processing.
+ // el_gets does an fflush internally, which is not sufficient here; it only
+ // writes the data into m_editline_out, but doesn't affect whether our worker
+ // thread will read that data yet. So we block here manually.
+ {
+ std::lock_guard<std::recursive_mutex> locker(m_output_mutex);
+ m_output_flushed = false;
+
+ // Write a synchronization point into the stream, so we can guarantee
+ // LibeditOutputBytesReceived has processed everything up till that mark.
+ fputc ('\0', m_editline_out);
+
+ while (!m_output_flushed)
+ {
+ // wait until the condition variable is signaled
+ m_output_cond.wait(m_output_mutex);
+ }
+ }
+#endif
+
// Re-set the boolean indicating whether or not el_gets is trying to get input.
SetGettingCommand (false);
@@ -379,7 +403,7 @@ IOChannel::LibeditGetInput (std::string &new_line)
return retval;
}
-void *
+thread_result_t
IOChannel::IOReadThread (void *ptr)
{
IOChannel *myself = static_cast<IOChannel *> (ptr);
@@ -502,8 +526,7 @@ IOChannel::Start ()
if (IS_VALID_LLDB_HOST_THREAD(m_read_thread))
return true;
- m_read_thread = SBHostOS::ThreadCreate ("<lldb.driver.commandline_io>", IOChannel::IOReadThread, this,
- NULL);
+ m_read_thread = SBHostOS::ThreadCreate("<lldb.driver.commandline_io>", (lldb::thread_func_t) IOChannel::IOReadThread, this, NULL);
return (IS_VALID_LLDB_HOST_THREAD(m_read_thread));
}
@@ -531,11 +554,11 @@ IOChannel::RefreshPrompt ()
{
// If we are not in the middle of getting input from the user, there is no need to
// refresh the prompt.
- IOLocker locker (m_output_mutex);
+ std::lock_guard<std::recursive_mutex> locker(m_output_mutex);
if (! IsGettingCommand())
return;
- // If we haven't finished writing the prompt, there's no need to refresh it.
+ // If we haven't finished writing the prompt, there's no need to refresh it.
if (m_expecting_prompt)
return;
@@ -562,7 +585,7 @@ IOChannel::OutWrite (const char *buffer, size_t len, bool asynchronous)
}
// Use the mutex to make sure OutWrite and ErrWrite do not interfere with each other's output.
- IOLocker locker (m_output_mutex);
+ std::lock_guard<std::recursive_mutex> locker(m_output_mutex);
if (m_driver->EditlineReaderIsTop() && asynchronous)
::fwrite (undo_prompt_string, 1, 4, m_out_file);
::fwrite (buffer, 1, len, m_out_file);
@@ -577,7 +600,7 @@ IOChannel::ErrWrite (const char *buffer, size_t len, bool asynchronous)
return;
// Use the mutex to make sure OutWrite and ErrWrite do not interfere with each other's output.
- IOLocker locker (m_output_mutex);
+ std::lock_guard<std::recursive_mutex> locker(m_output_mutex);
if (asynchronous)
::fwrite (undo_prompt_string, 1, 4, m_err_file);
::fwrite (buffer, 1, len, m_err_file);
@@ -631,17 +654,3 @@ IOChannel::SetGettingCommand (bool new_value)
{
m_getting_command = new_value;
}
-
-IOLocker::IOLocker (pthread_mutex_t &mutex) :
- m_mutex_ptr (&mutex)
-{
- if (m_mutex_ptr)
- ::pthread_mutex_lock (m_mutex_ptr);
-
-}
-
-IOLocker::~IOLocker ()
-{
- if (m_mutex_ptr)
- ::pthread_mutex_unlock (m_mutex_ptr);
-}
diff --git a/tools/driver/IOChannel.h b/tools/driver/IOChannel.h
index 36653a0c289f..3788673da972 100644
--- a/tools/driver/IOChannel.h
+++ b/tools/driver/IOChannel.h
@@ -10,18 +10,13 @@
#ifndef lldb_IOChannel_h_
#define lldb_IOChannel_h_
+#include <thread>
+#include <mutex>
+#include <atomic>
+#include <condition_variable>
+
#include <string>
#include <queue>
-
-#if defined(__FreeBSD__)
-#include <readline/readline.h>
-#else
-#include <editline/readline.h>
-#endif
-#include <histedit.h>
-#include <pthread.h>
-#include <sys/time.h>
-
#include "Driver.h"
class IOChannel : public lldb::SBBroadcaster
@@ -63,7 +58,7 @@ public:
bool
Stop ();
- static void *
+ static lldb::thread_result_t
IOReadThread (void *);
void
@@ -127,7 +122,8 @@ protected:
private:
- pthread_mutex_t m_output_mutex;
+ std::recursive_mutex m_output_mutex;
+ std::condition_variable_any m_output_cond;
struct timeval m_enter_elgets_time;
Driver *m_driver;
@@ -135,6 +131,7 @@ private:
bool m_read_thread_should_exit;
FILE *m_out_file;
FILE *m_err_file;
+ FILE *m_editline_out;
std::queue<std::string> m_command_queue;
const char *m_completion_key;
@@ -143,7 +140,8 @@ private:
HistEvent m_history_event;
bool m_getting_command;
bool m_expecting_prompt;
- std::string m_prompt_str; // for accumlating the prompt as it gets written out by editline
+ bool m_output_flushed;
+ std::string m_prompt_str; // for accumlating the prompt as it gets written out by editline
bool m_refresh_request_pending;
void
@@ -153,22 +151,4 @@ private:
HandleCompletion (EditLine *e, int ch);
};
-class IOLocker
-{
-public:
-
- IOLocker (pthread_mutex_t &mutex);
-
- ~IOLocker ();
-
-protected:
-
- pthread_mutex_t *m_mutex_ptr;
-
-private:
-
- IOLocker (const IOLocker&);
- const IOLocker& operator= (const IOLocker&);
-};
-
#endif // lldb_IOChannel_h_
diff --git a/tools/driver/Platform.cpp b/tools/driver/Platform.cpp
new file mode 100644
index 000000000000..5b5286e68114
--- /dev/null
+++ b/tools/driver/Platform.cpp
@@ -0,0 +1,111 @@
+//===-- Platform.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// this file is only relevant for Visual C++
+#if defined( _MSC_VER )
+
+#include <process.h>
+#include <assert.h>
+
+#include "Platform.h"
+
+// index one of the variable arguments
+// presuming "(EditLine *el, ..." is first in the argument list
+#define GETARG( Y, X ) ( (void* ) *( ( (int**) &(Y) ) + (X) ) )
+
+// the control handler or SIGINT handler
+static sighandler_t _ctrlHandler = NULL;
+
+// the default console control handler
+BOOL
+WINAPI CtrlHandler (DWORD ctrlType)
+{
+ if ( _ctrlHandler != NULL )
+ {
+ _ctrlHandler( 0 );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+ioctl (int d, int request, ...)
+{
+ switch ( request )
+ {
+ // request the console windows size
+ case ( TIOCGWINSZ ):
+ {
+ // locate the window size structure on stack
+ winsize *ws = (winsize*) GETARG( d, 2 );
+ // get screen buffer information
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &info );
+ // fill in the columns
+ ws->ws_col = info.dwMaximumWindowSize.X;
+ //
+ return 0;
+ }
+ break;
+ default:
+ assert( !"Not implemented!" );
+ }
+ return -1;
+}
+
+int
+kill (pid_t pid, int sig)
+{
+ // is the app trying to kill itself
+ if ( pid == getpid( ) )
+ exit( sig );
+ //
+ assert( !"Not implemented!" );
+ return -1;
+}
+
+int
+tcsetattr (int fd, int optional_actions, const struct termios *termios_p)
+{
+ assert( !"Not implemented!" );
+ return -1;
+}
+
+int
+tcgetattr (int fildes, struct termios *termios_p)
+{
+// assert( !"Not implemented!" );
+ // error return value (0=success)
+ return -1;
+}
+
+sighandler_t
+signal (int sig, sighandler_t sigFunc)
+{
+ switch ( sig )
+ {
+ case ( SIGINT ):
+ {
+ _ctrlHandler = sigFunc;
+ SetConsoleCtrlHandler( CtrlHandler, TRUE );
+ }
+ break;
+ case ( SIGPIPE ):
+ case ( SIGWINCH ):
+ case ( SIGTSTP ):
+ case ( SIGCONT ):
+ // ignore these for now
+ break;
+ default:
+ assert( !"Not implemented!" );
+ }
+ return 0;
+}
+
+#endif \ No newline at end of file
diff --git a/tools/driver/Platform.h b/tools/driver/Platform.h
new file mode 100644
index 000000000000..f1fe1e4aac18
--- /dev/null
+++ b/tools/driver/Platform.h
@@ -0,0 +1,113 @@
+//===-- Platform.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Platform_h_
+#define lldb_Platform_h_
+
+#if defined( _MSC_VER )
+
+ // this will stop signal.h being included
+ #define _INC_SIGNAL
+
+ #include <io.h>
+ #include <eh.h>
+ #include <inttypes.h>
+ #include "ELWrapper.h"
+ #include "lldb/Host/windows/Windows.h"
+ #include "GetOptWrapper.h"
+
+ struct timeval
+ {
+ long tv_sec;
+ long tv_usec;
+ };
+
+ struct winsize
+ {
+ long ws_col;
+ };
+
+ typedef unsigned char cc_t;
+ typedef unsigned int speed_t;
+ typedef unsigned int tcflag_t;
+
+ // fcntl.h
+ #define O_NOCTTY 0400
+
+ // ioctls.h
+ #define TIOCGWINSZ 0x5413
+
+ // signal.h
+ #define SIGPIPE 13
+ #define SIGCONT 18
+ #define SIGTSTP 20
+ #define SIGWINCH 28
+
+ // tcsetattr arguments
+ #define TCSANOW 0
+
+ #define NCCS 32
+ struct termios
+ {
+ tcflag_t c_iflag; // input mode flags
+ tcflag_t c_oflag; // output mode flags
+ tcflag_t c_cflag; // control mode flags
+ tcflag_t c_lflag; // local mode flags
+ cc_t c_line; // line discipline
+ cc_t c_cc[NCCS]; // control characters
+ speed_t c_ispeed; // input speed
+ speed_t c_ospeed; // output speed
+ };
+
+ typedef long pid_t;
+
+ #define STDIN_FILENO 0
+
+ #define PATH_MAX MAX_PATH
+ #define snprintf _snprintf
+
+ extern int ioctl( int d, int request, ... );
+ extern int kill ( pid_t pid, int sig );
+ extern int tcsetattr( int fd, int optional_actions, const struct termios *termios_p );
+ extern int tcgetattr( int fildes, struct termios *termios_p );
+
+ // signal handler function pointer type
+ typedef void (*sighandler_t)(int);
+
+ // signal.h
+ #define SIGINT 2
+ // default handler
+ #define SIG_DFL ( (sighandler_t) -1 )
+ // ignored
+ #define SIG_IGN ( (sighandler_t) -2 )
+ extern sighandler_t signal( int sig, sighandler_t );
+
+#else
+
+ #include <inttypes.h>
+
+ #include <getopt.h>
+ #include <libgen.h>
+ #include <sys/ioctl.h>
+ #include <termios.h>
+ #include <unistd.h>
+
+ #include <histedit.h>
+ #include <pthread.h>
+ #include <sys/time.h>
+
+ #if defined(__FreeBSD__)
+ #include <readline/readline.h>
+ #else
+ #include <editline/readline.h>
+ #endif
+
+#endif
+
+#endif // lldb_Platform_h_
diff --git a/tools/lldb-platform/lldb-platform.cpp b/tools/lldb-platform/lldb-platform.cpp
new file mode 100644
index 000000000000..007e69c92814
--- /dev/null
+++ b/tools/lldb-platform/lldb-platform.cpp
@@ -0,0 +1,270 @@
+//===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-python.h"
+
+// C Includes
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// C++ Includes
+
+// Other libraries and framework includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Core/ConnectionMachPort.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// option descriptors for getopt_long_only()
+//----------------------------------------------------------------------
+
+int g_debug = 0;
+int g_verbose = 0;
+int g_stay_alive = 0;
+
+static struct option g_long_options[] =
+{
+ { "debug", no_argument, &g_debug, 1 },
+ { "verbose", no_argument, &g_verbose, 1 },
+ { "stay-alive", no_argument, &g_stay_alive, 1 },
+ { "log-file", required_argument, NULL, 'l' },
+ { "log-flags", required_argument, NULL, 'f' },
+ { "listen", required_argument, NULL, 'L' },
+ { NULL, 0, NULL, 0 }
+};
+
+//----------------------------------------------------------------------
+// Watch for signals
+//----------------------------------------------------------------------
+int g_sigpipe_received = 0;
+void
+signal_handler(int signo)
+{
+ switch (signo)
+ {
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ case SIGHUP:
+ // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
+ // And we should not call exit() here because it results in the global destructors
+ // to be invoked and wreaking havoc on the threads still running.
+ Host::SystemLog(Host::eSystemLogWarning, "SIGHUP received, exiting lldb-platform...\n");
+ abort();
+ break;
+ }
+}
+
+static void
+display_usage (const char *progname)
+{
+ fprintf(stderr, "Usage:\n %s [--log-file log-file-path] [--log-flags flags] --listen port\n", progname);
+ exit(0);
+}
+
+//----------------------------------------------------------------------
+// main
+//----------------------------------------------------------------------
+int
+main (int argc, char *argv[])
+{
+ const char *progname = argv[0];
+ signal (SIGPIPE, signal_handler);
+ signal (SIGHUP, signal_handler);
+ int long_option_index = 0;
+ StreamSP log_stream_sp;
+ Args log_args;
+ Error error;
+ std::string listen_host_port;
+ int ch;
+ Debugger::Initialize();
+
+// ConnectionMachPort a;
+// ConnectionMachPort b;
+//
+// lldb::ConnectionStatus status;
+// const char *bootstrap_service_name = "HelloWorld";
+// status = a.BootstrapCheckIn(bootstrap_service_name, &error);
+//
+// if (status != eConnectionStatusSuccess)
+// {
+// fprintf(stderr, "%s", error.AsCString());
+// return 1;
+// }
+// status = b.BootstrapLookup (bootstrap_service_name, &error);
+// if (status != eConnectionStatusSuccess)
+// {
+// fprintf(stderr, "%s", error.AsCString());
+// return 2;
+// }
+//
+// if (a.Write ("hello", 5, status, &error) == 5)
+// {
+// char buf[32];
+// memset(buf, 0, sizeof(buf));
+// if (b.Read (buf, 5, status, &error))
+// {
+// printf("read returned bytes: %s", buf);
+// }
+// else
+// {
+// fprintf(stderr, "%s", error.AsCString());
+// return 4;
+// }
+// }
+// else
+// {
+// fprintf(stderr, "%s", error.AsCString());
+// return 3;
+// }
+
+ while ((ch = getopt_long_only(argc, argv, "l:f:L:", g_long_options, &long_option_index)) != -1)
+ {
+// DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
+// ch, (uint8_t)ch,
+// g_long_options[long_option_index].name,
+// g_long_options[long_option_index].has_arg ? '=' : ' ',
+// optarg ? optarg : "");
+ switch (ch)
+ {
+ case 0: // Any optional that auto set themselves will return 0
+ break;
+
+ case 'l': // Set Log File
+ if (optarg && optarg[0])
+ {
+ if ((strcasecmp(optarg, "stdout") == 0) || (strcmp(optarg, "/dev/stdout") == 0))
+ {
+ log_stream_sp.reset (new StreamFile (stdout, false));
+ }
+ else if ((strcasecmp(optarg, "stderr") == 0) || (strcmp(optarg, "/dev/stderr") == 0))
+ {
+ log_stream_sp.reset (new StreamFile (stderr, false));
+ }
+ else
+ {
+ FILE *log_file = fopen(optarg, "w");
+ if (log_file)
+ {
+ setlinebuf(log_file);
+ log_stream_sp.reset (new StreamFile (log_file, true));
+ }
+ else
+ {
+ const char *errno_str = strerror(errno);
+ fprintf (stderr, "Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
+ }
+
+ }
+
+ }
+ break;
+
+ case 'f': // Log Flags
+ if (optarg && optarg[0])
+ log_args.AppendArgument(optarg);
+ break;
+
+ case 'L':
+ listen_host_port.append (optarg);
+ break;
+
+ case 'h': /* fall-through is intentional */
+ case '?':
+ display_usage(progname);
+ break;
+ }
+ }
+ // Print usage and exit if no listening port is specified.
+ if (listen_host_port.empty())
+ display_usage(progname);
+
+ if (log_stream_sp)
+ {
+ if (log_args.GetArgumentCount() == 0)
+ log_args.AppendArgument("default");
+ ProcessGDBRemoteLog::EnableLog (log_stream_sp, 0,log_args.GetConstArgumentVector(), log_stream_sp.get());
+ }
+
+ // Skip any options we consumed with getopt_long_only
+ argc -= optind;
+ argv += optind;
+
+
+ do {
+ GDBRemoteCommunicationServer gdb_server (true);
+ if (!listen_host_port.empty())
+ {
+ std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
+ if (conn_ap.get())
+ {
+ for (int j = 0; j < listen_host_port.size(); j++)
+ {
+ char c = listen_host_port[j];
+ if (c > '9' || c < '0')
+ printf("WARNING: passing anything but a number as argument to --listen will most probably make connecting impossible.\n");
+ }
+ std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
+ if (conn_ap.get())
+ {
+ std::string connect_url ("listen://");
+ connect_url.append(listen_host_port.c_str());
+
+ printf ("Listening for a connection on %s...\n", listen_host_port.c_str());
+ if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess)
+ {
+ printf ("Connection established.\n");
+ gdb_server.SetConnection (conn_ap.release());
+ }
+ }
+ }
+
+ if (gdb_server.IsConnected())
+ {
+ // After we connected, we need to get an initial ack from...
+ if (gdb_server.HandshakeWithClient(&error))
+ {
+ bool interrupt = false;
+ bool done = false;
+ while (!interrupt && !done)
+ {
+ if (!gdb_server.GetPacketAndSendResponse (UINT32_MAX, error, interrupt, done))
+ break;
+ }
+
+ if (error.Fail())
+ {
+ fprintf(stderr, "error: %s\n", error.AsCString());
+ }
+ }
+ else
+ {
+ fprintf(stderr, "error: handshake with client failed\n");
+ }
+ }
+ }
+ } while (g_stay_alive);
+
+ Debugger::Terminate();
+
+ fprintf(stderr, "lldb-platform exiting...\n");
+
+ return 0;
+}