From f21a844f60ae6c74fcf1fddca32461acce3c1ee0 Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Wed, 6 Nov 2013 16:48:53 +0000 Subject: Import lldb as of SVN r194122 Sponsored by: DARPA, AFRL --- tools/driver/Driver.cpp | 227 ++++++++++++------ tools/driver/Driver.h | 17 +- tools/driver/ELWrapper.cpp | 422 ++++++++++++++++++++++++++++++++++ tools/driver/ELWrapper.h | 122 ++++++++++ tools/driver/GetOptWrapper.cpp | 33 +++ tools/driver/GetOptWrapper.h | 49 ++++ tools/driver/IOChannel.cpp | 121 +++++----- tools/driver/IOChannel.h | 42 +--- tools/driver/Platform.cpp | 111 +++++++++ tools/driver/Platform.h | 113 +++++++++ tools/lldb-platform/lldb-platform.cpp | 270 ++++++++++++++++++++++ 11 files changed, 1357 insertions(+), 170 deletions(-) create mode 100644 tools/driver/ELWrapper.cpp create mode 100644 tools/driver/ELWrapper.h create mode 100644 tools/driver/GetOptWrapper.cpp create mode 100644 tools/driver/GetOptWrapper.h create mode 100644 tools/driver/Platform.cpp create mode 100644 tools/driver/Platform.h create mode 100644 tools/lldb-platform/lldb-platform.cpp (limited to 'tools') 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 -#include -#include -#include -#include +#include #include #include #include #include -#include #include +#include #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 , 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 -- [ []]' 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; @@ -423,6 +442,34 @@ Driver::OptionData::Clear () m_process_pid = LLDB_INVALID_PROCESS_ID; } +void +Driver::OptionData::AddInitialCommand (const char *command, bool before_file, bool is_file, SBError &error) +{ + std::vector > *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 (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 (true, path_str)); + } + else + error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", optarg); + } + else + command_set->push_back (std::pair (false, optarg)); +} + void Driver::ResetOptionValues () { @@ -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 > *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, "\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 @@ -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 m_args; lldb::ScriptLanguage m_script_lang; std::string m_core_file; std::string m_crash_log; - std::vector m_source_command_files; + std::vector > m_initial_commands; + std::vector > 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 +#include + +// 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 _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**) ⪙ + // + 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 + +// 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 @@ -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 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 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 (ptr); @@ -502,8 +526,7 @@ IOChannel::Start () if (IS_VALID_LLDB_HOST_THREAD(m_read_thread)) return true; - m_read_thread = SBHostOS::ThreadCreate ("", IOChannel::IOReadThread, this, - NULL); + m_read_thread = SBHostOS::ThreadCreate("", (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 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 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 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 +#include +#include +#include + #include #include - -#if defined(__FreeBSD__) -#include -#else -#include -#endif -#include -#include -#include - #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 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 +#include + +#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 + #include + #include + #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 + + #include + #include + #include + #include + #include + + #include + #include + #include + + #if defined(__FreeBSD__) + #include + #else + #include + #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 +#include +#include +#include +#include +#include +#include + +// 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 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 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; +} -- cgit v1.2.3