Patch enabling lldb command line driver to run on windows.

CHANGES:
- Thread locking switched from pthreads to C++11 standard library.
- Abstracted platform specific header includes into 'platform.h'.
- Create editline emulator for windows.
- Emulated various platform dependant functions on windows.
TODO:
- User input currently handled by gets_s(), work started on better handler:
    see _WIP_INPUT_METHOD define blocks in 'ELWrapper.cpp'.
    Aim is to handle 'tab' auto completion on windows.
- Tidy up 'getopt.inc' from lldbHostCommon to serve as LLDB Drivers getopt windows implementation.

llvm-svn: 192714
This commit is contained in:
Deepak Panickal 2013-10-15 15:46:40 +00:00
parent 0f3b4aaf34
commit 429222c1f6
12 changed files with 932 additions and 114 deletions

View File

@ -1,7 +1,7 @@
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(debugserver)
endif()
if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(driver)
if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(lldb-platform)
endif()

View File

@ -5,6 +5,9 @@ add_lldb_executable(lldb
#DriverOptions.cpp
#DriverPosix.cpp
IOChannel.cpp
ELWrapper.cpp
Platform.cpp
GetOptWrapper.cpp
)
target_link_libraries(lldb liblldb)

View File

@ -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"
@ -155,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();
@ -569,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 ();
@ -802,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)
{
@ -825,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)
{
@ -1311,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)
{
@ -1371,6 +1375,7 @@ Driver::MainLoop ()
::setbuf (editline_output_slave_fh, NULL);
}
}
#endif
// struct termios stdin_termios;
@ -1412,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);
@ -1422,6 +1428,7 @@ Driver::MainLoop ()
::fprintf (stderr, "error: failed to start libedit output read thread");
exit (5);
}
#endif
struct winsize window_size;
@ -1663,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();
@ -1780,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 ();
}

View File

@ -10,6 +10,7 @@
#ifndef lldb_Driver_h_
#define lldb_Driver_h_
#include "Platform.h"
#include "lldb/Utility/PseudoTerminal.h"
#include <set>

View File

@ -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[ 64 ];
// try to get user input string
if ( el_get_s( buffer, 64 ) )
{
// 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

View File

@ -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, ... );
};

View File

@ -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

View File

@ -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_

View File

@ -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
@ -213,17 +215,14 @@ 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.
@ -231,35 +230,20 @@ IOChannel::IOChannel
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);
error = ::pthread_cond_init (&m_output_cond, NULL);
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 ()
@ -278,9 +262,6 @@ IOChannel::~IOChannel ()
::el_end (m_edit_line);
m_edit_line = NULL;
}
::pthread_cond_destroy (&m_output_cond);
::pthread_mutex_destroy (&m_output_mutex);
}
void
@ -304,16 +285,15 @@ 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' syncronization point in there.
if (src_len > 0 && bytes[src_len-1] == 0)
// 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;
@ -322,7 +302,7 @@ IOChannel::LibeditOutputBytesReceived (void *baton, const void *src, size_t src_
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;
@ -342,13 +322,14 @@ IOChannel::LibeditOutputBytesReceived (void *baton, const void *src, size_t src_
io_channel->OutWrite (bytes, src_len, NO_ASYNC);
}
#if !defined (_MSC_VER)
if (flush)
{
// Signal that we have finished all data up to the sync point.
IOLocker locker (io_channel->m_output_mutex);
io_channel->m_output_flushed = true;
pthread_cond_signal (&io_channel->m_output_cond);
io_channel->m_output_cond.notify_all();
}
#endif
}
IOChannel::LibeditGetInputResult
@ -367,22 +348,27 @@ 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.
{
IOLocker locker (m_output_mutex);
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);
fputc ('\0', m_editline_out);
while (!m_output_flushed && pthread_cond_wait (&m_output_cond, &m_output_mutex))
{ /* wait */ }
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);
@ -417,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);
@ -540,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));
}
@ -569,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;
@ -600,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);
@ -615,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);
@ -669,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);
}

View File

@ -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__) || defined(__NetBSD__)
#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,8 +122,8 @@ protected:
private:
pthread_mutex_t m_output_mutex;
pthread_cond_t m_output_cond;
std::recursive_mutex m_output_mutex;
std::condition_variable_any m_output_cond;
struct timeval m_enter_elgets_time;
Driver *m_driver;
@ -156,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_

View File

@ -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

View File

@ -0,0 +1,120 @@
//===-- 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 )
#define PRIu32 "u"
#define PRId64 "I64d"
#define PRIi64 "I64i"
#define PRIo64 "I64o"
#define PRIu64 "I64u"
#define PRIx64 "I64x"
#define PRIX64 "I64X"
// this will stop signal.h being included
#define _INC_SIGNAL
#include <io.h>
#include <eh.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
#if defined(__FreeBSD__)
#include <readline/readline.h>
#else
#include <editline/readline.h>
#endif
#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>
#endif
#endif // lldb_Platform_h_