2010-06-09 00:52:24 +08:00
//===-- Driver.cpp ----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
# include "Driver.h"
# include <getopt.h>
# include <libgen.h>
# include <sys/ioctl.h>
# include <termios.h>
# include <unistd.h>
2010-06-09 17:50:17 +08:00
# include <string.h>
# include <stdlib.h>
# include <limits.h>
2010-06-10 03:11:30 +08:00
# include <fcntl.h>
2010-06-09 00:52:24 +08:00
# include <string>
# include "IOChannel.h"
2010-06-09 17:50:17 +08:00
# include "lldb/API/SBCommandInterpreter.h"
# include "lldb/API/SBCommandReturnObject.h"
# include "lldb/API/SBCommunication.h"
# include "lldb/API/SBDebugger.h"
# include "lldb/API/SBEvent.h"
# include "lldb/API/SBHostOS.h"
# include "lldb/API/SBListener.h"
# include "lldb/API/SBSourceManager.h"
2011-02-19 10:53:09 +08:00
# include "lldb/API/SBStream.h"
2010-06-09 17:50:17 +08:00
# include "lldb/API/SBTarget.h"
# include "lldb/API/SBThread.h"
# include "lldb/API/SBProcess.h"
2010-06-09 00:52:24 +08:00
using namespace lldb ;
static void reset_stdin_termios ( ) ;
static struct termios g_old_stdin_termios ;
2010-09-10 01:45:09 +08:00
static char * g_debugger_name = ( char * ) " " ;
2010-11-20 04:47:54 +08:00
static Driver * g_driver = NULL ;
2010-09-10 01:45:09 +08:00
2010-06-09 00:52:24 +08:00
// In the Driver::MainLoop, we change the terminal settings. This function is
// added as an atexit handler to make sure we clean them up.
static void
reset_stdin_termios ( )
{
: : tcsetattr ( STDIN_FILENO , TCSANOW , & g_old_stdin_termios ) ;
}
2011-03-25 05:19:54 +08:00
typedef struct
2010-06-09 00:52:24 +08:00
{
2011-03-25 05:19:54 +08:00
uint32_t usage_mask ; // Used to mark options that can be used together. If (1 << n & usage_mask) != 0
// then this option belongs to option set n.
bool required ; // This option is required (in the current usage level)
const char * long_option ; // Full name for this option.
char short_option ; // Single character for this option.
int option_has_arg ; // no_argument, required_argument or optional_argument
2011-04-14 06:47:15 +08:00
uint32_t completion_type ; // Cookie the option class can use to do define the argument completion.
2011-03-25 05:19:54 +08:00
lldb : : CommandArgumentType argument_type ; // Type of argument this option takes
const char * usage_text ; // Full text explaining what this options does and what (if any) argument to
// pass it.
} OptionDefinition ;
static OptionDefinition g_options [ ] =
{
{ LLDB_OPT_SET_1 , true , " help " , ' h ' , no_argument , NULL , eArgTypeNone , " Prints out the usage information for the LLDB debugger. " } ,
{ LLDB_OPT_SET_2 , true , " version " , ' v ' , no_argument , NULL , eArgTypeNone , " Prints out the current version number of the LLDB debugger. " } ,
{ LLDB_OPT_SET_3 , true , " arch " , ' a ' , required_argument , NULL , eArgTypeArchitecture , " Tells the debugger to use the specified architecture when starting and running the program. <architecture> must be one of the architectures for which the program was compiled. " } ,
{ LLDB_OPT_SET_3 | LLDB_OPT_SET_4 , false , " script-language " , ' l ' , required_argument , NULL , eArgTypeScriptLang , " Tells the debugger to use the specified scripting language for user-defined scripts, rather than the default. Valid scripting languages that can be specified include Python, Perl, Ruby and Tcl. Currently only the Python extensions have been implemented. " } ,
{ LLDB_OPT_SET_3 | LLDB_OPT_SET_4 , false , " debug " , ' d ' , no_argument , NULL , eArgTypeNone , " Tells the debugger to print out extra information for debugging itself. " } ,
{ LLDB_OPT_SET_3 | LLDB_OPT_SET_4 , false , " source " , ' s ' , required_argument , NULL , eArgTypeFilename , " Tells the debugger to read in and execute the file <file>, which should contain lldb commands. " } ,
{ LLDB_OPT_SET_3 , true , " file " , ' f ' , required_argument , NULL , eArgTypeFilename , " Tells the debugger to use the file <filename> as the program to be debugged. " } ,
{ LLDB_OPT_SET_ALL , false , " editor " , ' e ' , no_argument , NULL , eArgTypeNone , " Tells the debugger to open source files using the host's \" external editor \" mechanism. " } ,
{ LLDB_OPT_SET_ALL , false , " no-lldbinit " , ' n ' , no_argument , NULL , eArgTypeNone , " Do not automatically parse any '.lldbinit' files. " } ,
{ 0 , false , NULL , 0 , 0 , NULL , eArgTypeNone , NULL }
2010-06-09 00:52:24 +08:00
} ;
Driver : : Driver ( ) :
SBBroadcaster ( " Driver " ) ,
2010-06-23 09:19:29 +08:00
m_debugger ( SBDebugger : : Create ( ) ) ,
2010-06-09 00:52:24 +08:00
m_editline_pty ( ) ,
m_editline_slave_fh ( NULL ) ,
m_editline_reader ( ) ,
m_io_channel_ap ( ) ,
m_option_data ( ) ,
m_waiting_for_command ( false )
{
2010-09-10 01:45:09 +08:00
g_debugger_name = ( char * ) m_debugger . GetInstanceName ( ) ;
if ( g_debugger_name = = NULL )
g_debugger_name = ( char * ) " " ;
2010-11-20 04:47:54 +08:00
g_driver = this ;
2010-06-09 00:52:24 +08:00
}
Driver : : ~ Driver ( )
{
2010-11-20 04:47:54 +08:00
g_driver = NULL ;
g_debugger_name = NULL ;
2010-06-09 00:52:24 +08:00
}
void
Driver : : CloseIOChannelFile ( )
{
// Write and 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 ) ) ;
m_editline_pty . CloseMasterFileDescriptor ( ) ;
if ( m_editline_slave_fh )
{
: : fclose ( m_editline_slave_fh ) ;
m_editline_slave_fh = NULL ;
}
}
2010-07-10 04:39:50 +08:00
// This function takes INDENT, which tells how many spaces to output at the front
// of each line; TEXT, which is the text that is to be output. It outputs the
// text, on multiple lines if necessary, to RESULT, with INDENT spaces at the
// front of each line. It breaks lines on spaces, tabs or newlines, shortening
// the line if necessary to not break in the middle of a word. It assumes that
// each output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.
2010-06-09 00:52:24 +08:00
void
2010-07-10 04:39:50 +08:00
OutputFormattedUsageText ( FILE * out , int indent , const char * text , int output_max_columns )
2010-06-09 00:52:24 +08:00
{
int len = strlen ( text ) ;
std : : string text_string ( text ) ;
// Force indentation to be reasonable.
if ( indent > = output_max_columns )
indent = 0 ;
// Will it all fit on one line?
if ( len + indent < output_max_columns )
// Output as a single line
2010-07-10 04:39:50 +08:00
fprintf ( out , " %*s%s \n " , indent , " " , text ) ;
2010-06-09 00:52:24 +08:00
else
{
// We need to break it up into multiple lines.
int text_width = output_max_columns - indent - 1 ;
int start = 0 ;
int end = start ;
int final_end = len ;
int sub_len ;
while ( end < final_end )
{
// Dont start the 'text' on a space, since we're already outputting the indentation.
while ( ( start < final_end ) & & ( text [ start ] = = ' ' ) )
start + + ;
end = start + text_width ;
if ( end > final_end )
end = final_end ;
else
{
// If we're not at the end of the text, make sure we break the line on white space.
while ( end > start
& & text [ end ] ! = ' ' & & text [ end ] ! = ' \t ' & & text [ end ] ! = ' \n ' )
end - - ;
}
sub_len = end - start ;
std : : string substring = text_string . substr ( start , sub_len ) ;
2010-07-10 04:39:50 +08:00
fprintf ( out , " %*s%s \n " , indent , " " , substring . c_str ( ) ) ;
2010-06-09 00:52:24 +08:00
start = end + 1 ;
}
}
}
void
2011-03-25 05:19:54 +08:00
ShowUsage ( FILE * out , OptionDefinition * option_table , Driver : : OptionData data )
2010-06-09 00:52:24 +08:00
{
uint32_t screen_width = 80 ;
uint32_t indent_level = 0 ;
const char * name = " lldb " ;
2010-06-16 02:47:14 +08:00
2010-06-09 00:52:24 +08:00
fprintf ( out , " \n Usage: \n \n " ) ;
indent_level + = 2 ;
// First, show each usage level set of options, e.g. <cmd> [options-for-level-0]
// <cmd> [options-for-level-1]
// etc.
uint32_t num_options ;
2010-06-16 02:47:14 +08:00
uint32_t num_option_sets = 0 ;
for ( num_options = 0 ; option_table [ num_options ] . long_option ! = NULL ; + + num_options )
2010-06-09 00:52:24 +08:00
{
2010-06-16 02:47:14 +08:00
uint32_t this_usage_mask = option_table [ num_options ] . usage_mask ;
if ( this_usage_mask = = LLDB_OPT_SET_ALL )
2010-06-09 00:52:24 +08:00
{
2010-06-16 02:47:14 +08:00
if ( num_option_sets = = 0 )
num_option_sets = 1 ;
2010-06-09 00:52:24 +08:00
}
2010-06-16 02:47:14 +08:00
else
2010-06-09 00:52:24 +08:00
{
2010-07-10 04:39:50 +08:00
for ( uint32_t j = 0 ; j < LLDB_MAX_NUM_OPTION_SETS ; j + + )
2010-06-16 02:47:14 +08:00
{
if ( this_usage_mask & 1 < < j )
{
if ( num_option_sets < = j )
num_option_sets = j + 1 ;
}
}
2010-06-09 00:52:24 +08:00
}
2010-06-16 02:47:14 +08:00
}
for ( uint32_t opt_set = 0 ; opt_set < num_option_sets ; opt_set + + )
{
uint32_t opt_set_mask ;
opt_set_mask = 1 < < opt_set ;
if ( opt_set > 0 )
fprintf ( out , " \n " ) ;
2010-07-10 04:39:50 +08:00
fprintf ( out , " %*s%s " , indent_level , " " , name ) ;
2010-06-16 02:47:14 +08:00
for ( uint32_t i = 0 ; i < num_options ; + + i )
2010-06-09 00:52:24 +08:00
{
2010-06-16 02:47:14 +08:00
if ( option_table [ i ] . usage_mask & opt_set_mask )
{
2010-10-02 03:59:14 +08:00
CommandArgumentType arg_type = option_table [ i ] . argument_type ;
2011-02-20 10:15:07 +08:00
const char * arg_name = SBCommandInterpreter : : GetArgumentTypeAsCString ( arg_type ) ;
2010-06-16 02:47:14 +08:00
if ( option_table [ i ] . required )
{
if ( option_table [ i ] . option_has_arg = = required_argument )
2011-02-20 10:15:07 +08:00
fprintf ( out , " -%c <%s> " , option_table [ i ] . short_option , arg_name ) ;
2010-06-16 02:47:14 +08:00
else if ( option_table [ i ] . option_has_arg = = optional_argument )
2011-02-20 10:15:07 +08:00
fprintf ( out , " -%c [<%s>] " , option_table [ i ] . short_option , arg_name ) ;
2010-06-16 02:47:14 +08:00
else
fprintf ( out , " -%c " , option_table [ i ] . short_option ) ;
}
else
{
if ( option_table [ i ] . option_has_arg = = required_argument )
2011-02-20 10:15:07 +08:00
fprintf ( out , " [-%c <%s>] " , option_table [ i ] . short_option , arg_name ) ;
2010-06-16 02:47:14 +08:00
else if ( option_table [ i ] . option_has_arg = = optional_argument )
2011-02-20 10:15:07 +08:00
fprintf ( out , " [-%c [<%s>]] " , option_table [ i ] . short_option , arg_name ) ;
2010-06-16 02:47:14 +08:00
else
fprintf ( out , " [-%c] " , option_table [ i ] . short_option ) ;
}
}
2010-06-09 00:52:24 +08:00
}
}
fprintf ( out , " \n \n " ) ;
// Now print out all the detailed information about the various options: long form, short form and help text:
// -- long_name <argument>
// - short <argument>
// help text
// This variable is used to keep track of which options' info we've printed out, because some options can be in
// more than one usage level, but we only want to print the long form of its information once.
Driver : : OptionData : : OptionSet options_seen ;
Driver : : OptionData : : OptionSet : : iterator pos ;
indent_level + = 5 ;
2010-06-16 02:47:14 +08:00
for ( uint32_t i = 0 ; i < num_options ; + + i )
2010-06-09 00:52:24 +08:00
{
// Only print this option if we haven't already seen it.
pos = options_seen . find ( option_table [ i ] . short_option ) ;
if ( pos = = options_seen . end ( ) )
{
2010-10-02 03:59:14 +08:00
CommandArgumentType arg_type = option_table [ i ] . argument_type ;
2011-02-20 10:15:07 +08:00
const char * arg_name = SBCommandInterpreter : : GetArgumentTypeAsCString ( arg_type ) ;
2010-10-02 03:59:14 +08:00
2010-06-09 00:52:24 +08:00
options_seen . insert ( option_table [ i ] . short_option ) ;
2010-07-10 04:39:50 +08:00
fprintf ( out , " %*s-%c " , indent_level , " " , option_table [ i ] . short_option ) ;
2010-10-02 03:59:14 +08:00
if ( arg_type ! = eArgTypeNone )
2011-02-20 10:15:07 +08:00
fprintf ( out , " <%s> " , arg_name ) ;
2010-06-09 00:52:24 +08:00
fprintf ( out , " \n " ) ;
2010-07-10 04:39:50 +08:00
fprintf ( out , " %*s--%s " , indent_level , " " , option_table [ i ] . long_option ) ;
2010-10-02 03:59:14 +08:00
if ( arg_type ! = eArgTypeNone )
2011-02-20 10:15:07 +08:00
fprintf ( out , " <%s> " , arg_name ) ;
2010-06-09 00:52:24 +08:00
fprintf ( out , " \n " ) ;
indent_level + = 5 ;
2010-07-10 04:39:50 +08:00
OutputFormattedUsageText ( out , indent_level , option_table [ i ] . usage_text , screen_width ) ;
2010-06-09 00:52:24 +08:00
indent_level - = 5 ;
fprintf ( out , " \n " ) ;
}
}
indent_level - = 5 ;
2010-07-10 04:39:50 +08:00
fprintf ( out , " \n %*s('%s <filename>' also works, to specify the file to be debugged.) \n \n " ,
indent_level , " " , name ) ;
2010-06-09 00:52:24 +08:00
}
void
2011-03-25 05:19:54 +08:00
BuildGetOptTable ( OptionDefinition * expanded_option_table , std : : vector < struct option > & getopt_table ,
2010-10-13 05:57:09 +08:00
uint32_t num_options )
2010-06-09 00:52:24 +08:00
{
if ( num_options = = 0 )
return ;
uint32_t i ;
uint32_t j ;
std : : bitset < 256 > option_seen ;
2010-10-13 05:57:09 +08:00
getopt_table . resize ( num_options + 1 ) ;
2010-06-09 00:52:24 +08:00
for ( i = 0 , j = 0 ; i < num_options ; + + i )
2010-07-10 04:39:50 +08:00
{
2010-06-09 00:52:24 +08:00
char short_opt = expanded_option_table [ i ] . short_option ;
2010-07-10 04:39:50 +08:00
2010-06-09 00:52:24 +08:00
if ( option_seen . test ( short_opt ) = = false )
2010-07-10 04:39:50 +08:00
{
2010-10-13 05:57:09 +08:00
getopt_table [ j ] . name = expanded_option_table [ i ] . long_option ;
getopt_table [ j ] . has_arg = expanded_option_table [ i ] . option_has_arg ;
getopt_table [ j ] . flag = NULL ;
getopt_table [ j ] . val = expanded_option_table [ i ] . short_option ;
2010-06-09 00:52:24 +08:00
option_seen . set ( short_opt ) ;
+ + j ;
2010-07-10 04:39:50 +08:00
}
}
2010-06-09 00:52:24 +08:00
2010-10-13 05:57:09 +08:00
getopt_table [ j ] . name = NULL ;
getopt_table [ j ] . has_arg = 0 ;
getopt_table [ j ] . flag = NULL ;
getopt_table [ j ] . val = 0 ;
2010-06-09 00:52:24 +08:00
}
2010-06-23 09:19:29 +08:00
Driver : : OptionData : : OptionData ( ) :
2010-12-09 06:23:24 +08:00
m_args ( ) ,
2010-06-23 09:19:29 +08:00
m_script_lang ( lldb : : eScriptLanguageDefault ) ,
2010-07-10 04:39:50 +08:00
m_crash_log ( ) ,
2010-06-23 09:19:29 +08:00
m_source_command_files ( ) ,
m_debug_mode ( false ) ,
2010-07-10 04:39:50 +08:00
m_print_version ( false ) ,
2010-06-23 09:19:29 +08:00
m_print_help ( false ) ,
2011-04-12 03:41:40 +08:00
m_use_external_editor ( false ) ,
m_seen_options ( )
2010-06-23 09:19:29 +08:00
{
}
Driver : : OptionData : : ~ OptionData ( )
{
}
void
Driver : : OptionData : : Clear ( )
{
2010-12-09 06:23:24 +08:00
m_args . clear ( ) ;
2010-06-23 09:19:29 +08:00
m_script_lang = lldb : : eScriptLanguageDefault ;
m_source_command_files . clear ( ) ;
m_debug_mode = false ;
m_print_help = false ;
m_print_version = false ;
2010-08-31 03:44:40 +08:00
m_use_external_editor = false ;
2010-06-23 09:19:29 +08:00
}
void
Driver : : ResetOptionValues ( )
{
m_option_data . Clear ( ) ;
}
const char *
Driver : : GetFilename ( ) const
{
2010-12-09 06:23:24 +08:00
if ( m_option_data . m_args . empty ( ) )
2010-06-23 09:19:29 +08:00
return NULL ;
2010-12-09 06:23:24 +08:00
return m_option_data . m_args . front ( ) . c_str ( ) ;
2010-06-23 09:19:29 +08:00
}
const char *
Driver : : GetCrashLogFilename ( ) const
{
if ( m_option_data . m_crash_log . empty ( ) )
return NULL ;
return m_option_data . m_crash_log . c_str ( ) ;
}
lldb : : ScriptLanguage
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
{
if ( idx < m_option_data . m_source_command_files . size ( ) )
return m_option_data . m_source_command_files [ idx ] . c_str ( ) ;
return NULL ;
}
bool
Driver : : GetDebugMode ( ) const
{
return m_option_data . m_debug_mode ;
}
// Check the arguments that were passed to this program to make sure they are valid and to get their
// argument values (if any). Return a boolean value indicating whether or not to start up the full
// debugger (i.e. the Command Interpreter) or not. Return FALSE if the arguments were invalid OR
// if the user only wanted help or version information.
2010-06-09 00:52:24 +08:00
SBError
2010-06-23 09:19:29 +08:00
Driver : : ParseArgs ( int argc , const char * argv [ ] , FILE * out_fh , bool & exit )
2010-06-09 00:52:24 +08:00
{
2010-06-23 09:19:29 +08:00
ResetOptionValues ( ) ;
SBCommandReturnObject result ;
2010-06-09 00:52:24 +08:00
SBError error ;
std : : string option_string ;
struct option * long_options = NULL ;
2010-10-13 05:57:09 +08:00
std : : vector < struct option > long_options_vector ;
2010-07-10 04:39:50 +08:00
uint32_t num_options ;
2010-06-09 00:52:24 +08:00
2010-07-10 04:39:50 +08:00
for ( num_options = 0 ; g_options [ num_options ] . long_option ! = NULL ; + + num_options )
/* Do Nothing. */ ;
2010-06-09 00:52:24 +08:00
if ( num_options = = 0 )
{
if ( argc > 1 )
error . SetErrorStringWithFormat ( " invalid number of options " ) ;
return error ;
}
2010-10-13 05:57:09 +08:00
BuildGetOptTable ( g_options , long_options_vector , num_options ) ;
2010-06-09 00:52:24 +08:00
2010-10-13 05:57:09 +08:00
if ( long_options_vector . empty ( ) )
long_options = NULL ;
else
long_options = & long_options_vector . front ( ) ;
2010-06-09 00:52:24 +08:00
if ( long_options = = NULL )
{
error . SetErrorStringWithFormat ( " invalid long options " ) ;
return error ;
}
// Build the option_string argument for call to getopt_long.
for ( int i = 0 ; long_options [ i ] . name ! = NULL ; + + i )
{
if ( long_options [ i ] . flag = = NULL )
{
option_string . push_back ( ( char ) long_options [ i ] . val ) ;
switch ( long_options [ i ] . has_arg )
{
default :
case no_argument :
break ;
case required_argument :
option_string . push_back ( ' : ' ) ;
break ;
case optional_argument :
option_string . append ( " :: " ) ;
break ;
}
}
}
// Prepare for & make calls to getopt_long.
2010-06-14 03:18:49 +08:00
# if __GLIBC__
optind = 0 ;
# else
2010-06-09 00:52:24 +08:00
optreset = 1 ;
optind = 1 ;
2010-06-14 03:18:49 +08:00
# endif
2010-06-09 00:52:24 +08:00
int val ;
while ( 1 )
{
int long_options_index = - 1 ;
2010-07-10 04:39:50 +08:00
val = : : getopt_long ( argc , const_cast < char * * > ( argv ) , option_string . c_str ( ) , long_options , & long_options_index ) ;
2010-06-09 00:52:24 +08:00
if ( val = = - 1 )
break ;
else if ( val = = ' ? ' )
{
2010-06-23 09:19:29 +08:00
m_option_data . m_print_help = true ;
2010-06-09 00:52:24 +08:00
error . SetErrorStringWithFormat ( " unknown or ambiguous option " ) ;
break ;
}
else if ( val = = 0 )
continue ;
else
{
2010-06-23 09:19:29 +08:00
m_option_data . m_seen_options . insert ( ( char ) val ) ;
2010-06-09 00:52:24 +08:00
if ( long_options_index = = - 1 )
{
for ( int i = 0 ;
long_options [ i ] . name | | long_options [ i ] . has_arg | | long_options [ i ] . flag | | long_options [ i ] . val ;
+ + i )
{
if ( long_options [ i ] . val = = val )
{
long_options_index = i ;
break ;
}
}
}
if ( long_options_index > = 0 )
{
2010-06-23 09:19:29 +08:00
const char short_option = ( char ) g_options [ long_options_index ] . short_option ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
switch ( short_option )
{
case ' h ' :
m_option_data . m_print_help = true ;
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
case ' v ' :
m_option_data . m_print_version = true ;
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
case ' c ' :
m_option_data . m_crash_log = optarg ;
break ;
2010-10-11 09:05:37 +08:00
2010-08-31 03:44:40 +08:00
case ' e ' :
m_option_data . m_use_external_editor = true ;
break ;
2010-10-11 09:05:37 +08:00
case ' n ' :
m_debugger . SkipLLDBInitFiles ( true ) ;
break ;
2010-06-23 09:19:29 +08:00
case ' f ' :
{
SBFileSpec file ( optarg ) ;
if ( file . Exists ( ) )
2010-12-09 06:23:24 +08:00
{
m_option_data . m_args . push_back ( optarg ) ;
}
2010-09-10 12:48:55 +08:00
else if ( file . ResolveExecutableLocation ( ) )
{
char path [ PATH_MAX ] ;
int path_len ;
file . GetPath ( path , path_len ) ;
2010-12-09 06:23:24 +08:00
m_option_data . m_args . push_back ( path ) ;
2010-09-10 12:48:55 +08:00
}
2010-06-23 09:19:29 +08:00
else
error . SetErrorStringWithFormat ( " file specified in --file (-f) option doesn't exist: '%s' " , optarg ) ;
}
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
case ' a ' :
if ( ! m_debugger . SetDefaultArchitecture ( optarg ) )
error . SetErrorStringWithFormat ( " invalid architecture in the -a or --arch option: '%s' " , optarg ) ;
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
case ' l ' :
m_option_data . m_script_lang = m_debugger . GetScriptingLanguage ( optarg ) ;
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
case ' d ' :
m_option_data . m_debug_mode = true ;
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
case ' s ' :
{
SBFileSpec file ( optarg ) ;
if ( file . Exists ( ) )
m_option_data . m_source_command_files . push_back ( optarg ) ;
2010-09-10 12:48:55 +08:00
else if ( file . ResolveExecutableLocation ( ) )
{
char final_path [ PATH_MAX ] ;
size_t path_len ;
file . GetPath ( final_path , path_len ) ;
std : : string path_str ( final_path ) ;
m_option_data . m_source_command_files . push_back ( path_str ) ;
}
2010-06-23 09:19:29 +08:00
else
error . SetErrorStringWithFormat ( " file specified in --source (-s) option doesn't exist: '%s' " , optarg ) ;
}
break ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
default :
m_option_data . m_print_help = true ;
error . SetErrorStringWithFormat ( " unrecognized option %c " , short_option ) ;
break ;
}
2010-06-09 00:52:24 +08:00
}
2010-06-23 09:19:29 +08:00
else
2010-06-09 00:52:24 +08:00
{
2010-06-23 09:19:29 +08:00
error . SetErrorStringWithFormat ( " invalid option with value %i " , val ) ;
2010-06-09 00:52:24 +08:00
}
2010-06-23 09:19:29 +08:00
if ( error . Fail ( ) )
2010-10-13 05:57:09 +08:00
{
2010-06-23 09:19:29 +08:00
return error ;
2010-10-13 05:57:09 +08:00
}
2010-06-23 09:19:29 +08:00
}
2010-06-09 00:52:24 +08:00
}
2010-06-16 02:47:14 +08:00
2010-06-23 09:19:29 +08:00
if ( error . Fail ( ) | | m_option_data . m_print_help )
2010-06-09 00:52:24 +08:00
{
ShowUsage ( out_fh , g_options , m_option_data ) ;
2010-10-11 09:13:37 +08:00
exit = true ;
2010-06-09 00:52:24 +08:00
}
else if ( m_option_data . m_print_version )
{
2010-06-23 09:19:29 +08:00
: : fprintf ( out_fh , " %s \n " , m_debugger . GetVersionString ( ) ) ;
exit = true ;
2010-06-09 00:52:24 +08:00
}
else if ( ! m_option_data . m_crash_log . empty ( ) )
{
// Handle crash log stuff here.
}
else
{
2010-12-09 06:23:24 +08:00
// Any arguments that are left over after option parsing are for
// the program. If a file was specified with -f then the filename
// is already in the m_option_data.m_args array, and any remaining args
// are arguments for the inferior program. If no file was specified with
// -f, then what is left is the program name followed by any arguments.
// Skip any options we consumed with getopt_long
argc - = optind ;
argv + = optind ;
if ( argc > 0 )
{
for ( int arg_idx = 0 ; arg_idx < argc ; + + arg_idx )
{
const char * arg = argv [ arg_idx ] ;
if ( arg )
m_option_data . m_args . push_back ( arg ) ;
}
}
2010-06-09 00:52:24 +08:00
}
2010-06-23 09:19:29 +08:00
return error ;
2010-06-09 00:52:24 +08:00
}
2010-09-30 02:35:42 +08:00
size_t
2010-06-09 00:52:24 +08:00
Driver : : GetProcessSTDOUT ( )
{
// The process has stuff waiting for stdout; get it and write it out to the appropriate place.
char stdio_buffer [ 1024 ] ;
size_t len ;
2010-09-30 02:35:42 +08:00
size_t total_bytes = 0 ;
2010-08-27 05:32:51 +08:00
while ( ( len = m_debugger . GetSelectedTarget ( ) . GetProcess ( ) . GetSTDOUT ( stdio_buffer , sizeof ( stdio_buffer ) ) ) > 0 )
2010-09-30 02:35:42 +08:00
{
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap - > OutWrite ( stdio_buffer , len , ASYNC ) ;
2010-09-30 02:35:42 +08:00
total_bytes + = len ;
}
return total_bytes ;
2010-06-09 00:52:24 +08:00
}
2010-09-30 02:35:42 +08:00
size_t
2010-06-09 00:52:24 +08:00
Driver : : GetProcessSTDERR ( )
{
// The process has stuff waiting for stderr; get it and write it out to the appropriate place.
char stdio_buffer [ 1024 ] ;
size_t len ;
2010-09-30 02:35:42 +08:00
size_t total_bytes = 0 ;
2010-08-27 05:32:51 +08:00
while ( ( len = m_debugger . GetSelectedTarget ( ) . GetProcess ( ) . GetSTDERR ( stdio_buffer , sizeof ( stdio_buffer ) ) ) > 0 )
2010-09-30 02:35:42 +08:00
{
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap - > ErrWrite ( stdio_buffer , len , ASYNC ) ;
2010-09-30 02:35:42 +08:00
total_bytes + = len ;
}
return total_bytes ;
2010-06-09 00:52:24 +08:00
}
void
2010-08-27 05:32:51 +08:00
Driver : : UpdateSelectedThread ( )
2010-06-09 00:52:24 +08:00
{
using namespace lldb ;
2010-08-27 05:32:51 +08:00
SBProcess process ( m_debugger . GetSelectedTarget ( ) . GetProcess ( ) ) ;
2010-06-09 00:52:24 +08:00
if ( process . IsValid ( ) )
{
2010-08-27 05:32:51 +08:00
SBThread curr_thread ( process . GetSelectedThread ( ) ) ;
2010-06-09 00:52:24 +08:00
SBThread thread ;
StopReason curr_thread_stop_reason = eStopReasonInvalid ;
curr_thread_stop_reason = curr_thread . GetStopReason ( ) ;
if ( ! curr_thread . IsValid ( ) | |
curr_thread_stop_reason = = eStopReasonInvalid | |
curr_thread_stop_reason = = eStopReasonNone )
{
// Prefer a thread that has just completed its plan over another thread as current thread.
SBThread plan_thread ;
SBThread other_thread ;
const size_t num_threads = process . GetNumThreads ( ) ;
size_t i ;
for ( i = 0 ; i < num_threads ; + + i )
{
thread = process . GetThreadAtIndex ( i ) ;
StopReason thread_stop_reason = thread . GetStopReason ( ) ;
switch ( thread_stop_reason )
{
default :
case eStopReasonInvalid :
case eStopReasonNone :
break ;
case eStopReasonTrace :
case eStopReasonBreakpoint :
case eStopReasonWatchpoint :
case eStopReasonSignal :
case eStopReasonException :
if ( ! other_thread . IsValid ( ) )
other_thread = thread ;
break ;
case eStopReasonPlanComplete :
if ( ! plan_thread . IsValid ( ) )
plan_thread = thread ;
break ;
}
}
if ( plan_thread . IsValid ( ) )
2010-08-27 05:32:51 +08:00
process . SetSelectedThread ( plan_thread ) ;
2010-06-09 00:52:24 +08:00
else if ( other_thread . IsValid ( ) )
2010-08-27 05:32:51 +08:00
process . SetSelectedThread ( other_thread ) ;
2010-06-09 00:52:24 +08:00
else
{
if ( curr_thread . IsValid ( ) )
thread = curr_thread ;
else
thread = process . GetThreadAtIndex ( 0 ) ;
if ( thread . IsValid ( ) )
2010-08-27 05:32:51 +08:00
process . SetSelectedThread ( thread ) ;
2010-06-09 00:52:24 +08:00
}
}
}
}
// This function handles events that were broadcast by the process.
void
Driver : : HandleProcessEvent ( const SBEvent & event )
{
using namespace lldb ;
const uint32_t event_type = event . GetType ( ) ;
if ( event_type & SBProcess : : eBroadcastBitSTDOUT )
{
// The process has stdout available, get it and write it out to the
// appropriate place.
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
GetProcessSTDOUT ( ) ;
2010-06-09 00:52:24 +08:00
}
else if ( event_type & SBProcess : : eBroadcastBitSTDERR )
{
// The process has stderr available, get it and write it out to the
// appropriate place.
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
GetProcessSTDERR ( ) ;
2010-06-09 00:52:24 +08:00
}
else if ( event_type & SBProcess : : eBroadcastBitStateChanged )
{
// Drain all stout and stderr so we don't see any output come after
// we print our prompts
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
GetProcessSTDOUT ( ) ;
GetProcessSTDERR ( ) ;
2010-06-09 00:52:24 +08:00
// Something changed in the process; get the event and report the process's current status and location to
// the user.
StateType event_state = SBProcess : : GetStateFromEvent ( event ) ;
if ( event_state = = eStateInvalid )
return ;
SBProcess process ( SBProcess : : GetProcessFromEvent ( event ) ) ;
assert ( process . IsValid ( ) ) ;
switch ( event_state )
{
case eStateInvalid :
case eStateUnloaded :
2011-02-04 09:58:07 +08:00
case eStateConnected :
2010-06-09 00:52:24 +08:00
case eStateAttaching :
case eStateLaunching :
case eStateStepping :
case eStateDetached :
{
char message [ 1024 ] ;
int message_len = : : snprintf ( message , sizeof ( message ) , " Process %d %s \n " , process . GetProcessID ( ) ,
2010-06-23 09:19:29 +08:00
m_debugger . StateAsCString ( event_state ) ) ;
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap - > OutWrite ( message , message_len , ASYNC ) ;
2010-06-09 00:52:24 +08:00
}
break ;
case eStateRunning :
// Don't be chatty when we run...
break ;
case eStateExited :
2010-09-30 02:35:42 +08:00
{
SBCommandReturnObject result ;
m_debugger . GetCommandInterpreter ( ) . HandleCommand ( " process status " , result , false ) ;
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap - > ErrWrite ( result . GetError ( ) , result . GetErrorSize ( ) , ASYNC ) ;
m_io_channel_ap - > OutWrite ( result . GetOutput ( ) , result . GetOutputSize ( ) , ASYNC ) ;
2010-09-30 02:35:42 +08:00
}
2010-06-09 00:52:24 +08:00
break ;
case eStateStopped :
case eStateCrashed :
case eStateSuspended :
// Make sure the program hasn't been auto-restarted:
if ( SBProcess : : GetRestartedFromEvent ( event ) )
{
// FIXME: Do we want to report this, or would that just be annoyingly chatty?
char message [ 1024 ] ;
int message_len = : : snprintf ( message , sizeof ( message ) , " Process %d stopped and was programmatically restarted. \n " ,
process . GetProcessID ( ) ) ;
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap - > OutWrite ( message , message_len , ASYNC ) ;
2010-06-09 00:52:24 +08:00
}
else
{
2010-09-30 02:35:42 +08:00
SBCommandReturnObject result ;
2010-08-27 05:32:51 +08:00
UpdateSelectedThread ( ) ;
2010-09-30 02:35:42 +08:00
m_debugger . GetCommandInterpreter ( ) . HandleCommand ( " process status " , result , false ) ;
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap - > ErrWrite ( result . GetError ( ) , result . GetErrorSize ( ) , ASYNC ) ;
m_io_channel_ap - > OutWrite ( result . GetOutput ( ) , result . GetOutputSize ( ) , ASYNC ) ;
2010-06-09 00:52:24 +08:00
}
break ;
}
}
}
// This function handles events broadcast by the IOChannel (HasInput, UserInterrupt, or ThreadShouldExit).
bool
Driver : : HandleIOEvent ( const SBEvent & event )
{
bool quit = false ;
const uint32_t event_type = event . GetType ( ) ;
if ( event_type & IOChannel : : eBroadcastBitHasUserInput )
{
// We got some input (i.e. a command string) from the user; pass it off to the command interpreter for
// handling.
const char * command_string = SBEvent : : GetCStringFromEvent ( event ) ;
if ( command_string = = NULL )
2010-07-10 04:39:50 +08:00
command_string = " " ;
2010-06-09 00:52:24 +08:00
SBCommandReturnObject result ;
2011-02-19 10:53:09 +08:00
result . SetImmediateOutputFile ( m_debugger . GetOutputFileHandle ( ) ) ;
result . SetImmediateErrorFile ( m_debugger . GetErrorFileHandle ( ) ) ;
// We've set the result to dump immediately.
m_debugger . GetCommandInterpreter ( ) . HandleCommand ( command_string , result , true ) ;
2010-06-09 00:52:24 +08:00
// We are done getting and running our command, we can now clear the
// m_waiting_for_command so we can get another one.
m_waiting_for_command = false ;
// If our editline input reader is active, it means another input reader
// got pushed onto the input reader and caused us to become deactivated.
// When the input reader above us gets popped, we will get re-activated
// and our prompt will refresh in our callback
if ( m_editline_reader . IsActive ( ) )
{
ReadyForCommand ( ) ;
}
}
else if ( event_type & IOChannel : : eBroadcastBitUserInterrupt )
{
// This is here to handle control-c interrupts from the user. It has not yet really been implemented.
// TO BE DONE: PROPERLY HANDLE CONTROL-C FROM USER
//m_io_channel_ap->CancelInput();
// Anything else? Send Interrupt to process?
}
else if ( ( event_type & IOChannel : : eBroadcastBitThreadShouldExit ) | |
( event_type & IOChannel : : eBroadcastBitThreadDidExit ) )
{
// If the IOChannel thread is trying to go away, then it is definitely
// time to end the debugging session.
quit = true ;
}
return quit ;
}
void
Driver : : MasterThreadBytesReceived ( void * baton , const void * src , size_t src_len )
{
Driver * driver = ( Driver * ) baton ;
driver - > GetFromMaster ( ( const char * ) src , src_len ) ;
}
void
Driver : : GetFromMaster ( const char * src , size_t src_len )
{
// Echo the characters back to the Debugger's stdout, that way if you
// type characters while a command is running, you'll see what you've typed.
2010-06-23 09:19:29 +08:00
FILE * out_fh = m_debugger . GetOutputFileHandle ( ) ;
2010-06-09 00:52:24 +08:00
if ( out_fh )
: : fwrite ( src , 1 , src_len , out_fh ) ;
}
size_t
Driver : : EditLineInputReaderCallback
(
void * baton ,
SBInputReader * reader ,
InputReaderAction notification ,
const char * bytes ,
size_t bytes_len
)
{
Driver * driver = ( Driver * ) baton ;
switch ( notification )
{
case eInputReaderActivate :
break ;
case eInputReaderReactivate :
driver - > ReadyForCommand ( ) ;
break ;
case eInputReaderDeactivate :
break ;
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
case eInputReaderAsynchronousOutputWritten :
if ( driver - > m_io_channel_ap . get ( ) ! = NULL )
driver - > m_io_channel_ap - > RefreshPrompt ( ) ;
break ;
2010-06-09 00:52:24 +08:00
2010-11-20 04:47:54 +08:00
case eInputReaderInterrupt :
if ( driver - > m_io_channel_ap . get ( ) ! = NULL )
{
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
driver - > m_io_channel_ap - > OutWrite ( " ^C \n " , 3 , NO_ASYNC ) ;
2010-11-20 04:47:54 +08:00
driver - > m_io_channel_ap - > RefreshPrompt ( ) ;
}
break ;
case eInputReaderEndOfFile :
if ( driver - > m_io_channel_ap . get ( ) ! = NULL )
{
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
driver - > m_io_channel_ap - > OutWrite ( " ^D \n " , 3 , NO_ASYNC ) ;
2010-11-20 04:47:54 +08:00
driver - > m_io_channel_ap - > RefreshPrompt ( ) ;
}
write ( driver - > m_editline_pty . GetMasterFileDescriptor ( ) , " quit \n " , 5 ) ;
break ;
2010-06-09 00:52:24 +08:00
case eInputReaderGotToken :
write ( driver - > m_editline_pty . GetMasterFileDescriptor ( ) , bytes , bytes_len ) ;
break ;
case eInputReaderDone :
break ;
}
return bytes_len ;
}
void
Driver : : MainLoop ( )
{
char error_str [ 1024 ] ;
if ( m_editline_pty . OpenFirstAvailableMaster ( O_RDWR | O_NOCTTY , error_str , sizeof ( error_str ) ) = = false )
{
: : fprintf ( stderr , " error: failed to open driver pseudo terminal : %s " , error_str ) ;
exit ( 1 ) ;
}
else
{
const char * driver_slave_name = m_editline_pty . GetSlaveName ( error_str , sizeof ( error_str ) ) ;
if ( driver_slave_name = = NULL )
{
: : fprintf ( stderr , " error: failed to get slave name for driver pseudo terminal : %s " , error_str ) ;
exit ( 2 ) ;
}
else
{
m_editline_slave_fh = : : fopen ( driver_slave_name , " r+ " ) ;
if ( m_editline_slave_fh = = NULL )
{
SBError error ;
error . SetErrorToErrno ( ) ;
: : fprintf ( stderr , " error: failed to get open slave for driver pseudo terminal : %s " ,
error . GetCString ( ) ) ;
exit ( 3 ) ;
}
: : setbuf ( m_editline_slave_fh , NULL ) ;
}
}
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
lldb_utility : : PseudoTerminal editline_output_pty ;
FILE * editline_output_slave_fh = NULL ;
if ( editline_output_pty . OpenFirstAvailableMaster ( O_RDWR | O_NOCTTY , error_str , sizeof ( error_str ) ) = = false )
{
: : fprintf ( stderr , " error: failed to open output pseudo terminal : %s " , error_str ) ;
exit ( 1 ) ;
}
else
{
const char * output_slave_name = editline_output_pty . GetSlaveName ( error_str , sizeof ( error_str ) ) ;
if ( output_slave_name = = NULL )
{
: : fprintf ( stderr , " error: failed to get slave name for output pseudo terminal : %s " , error_str ) ;
exit ( 2 ) ;
}
else
{
editline_output_slave_fh = : : fopen ( output_slave_name , " r+ " ) ;
if ( editline_output_slave_fh = = NULL )
{
SBError error ;
error . SetErrorToErrno ( ) ;
: : fprintf ( stderr , " error: failed to get open slave for output pseudo terminal : %s " ,
error . GetCString ( ) ) ;
exit ( 3 ) ;
}
: : setbuf ( editline_output_slave_fh , NULL ) ;
}
}
2010-06-09 00:52:24 +08:00
// struct termios stdin_termios;
if ( : : tcgetattr ( STDIN_FILENO , & g_old_stdin_termios ) = = 0 )
atexit ( reset_stdin_termios ) ;
: : setbuf ( stdin , NULL ) ;
: : setbuf ( stdout , NULL ) ;
2010-06-23 09:19:29 +08:00
m_debugger . SetErrorFileHandle ( stderr , false ) ;
m_debugger . SetOutputFileHandle ( stdout , false ) ;
m_debugger . SetInputFileHandle ( stdin , true ) ;
2010-08-31 03:44:40 +08:00
m_debugger . SetUseExternalEditor ( m_option_data . m_use_external_editor ) ;
2010-06-09 00:52:24 +08:00
// You have to drain anything that comes to the master side of the PTY. master_out_comm is
// for that purpose. The reason you need to do this is a curious reason... editline will echo
// characters to the PTY when it gets characters while el_gets is not running, and then when
// you call el_gets (or el_getc) it will try to reset the terminal back to raw mode which blocks
// if there are unconsumed characters in the out buffer.
// However, you don't need to do anything with the characters, since editline will dump these
// unconsumed characters after printing the prompt again in el_gets.
2010-12-04 10:39:47 +08:00
SBCommunication master_out_comm ( " driver.editline " ) ;
master_out_comm . SetCloseOnEOF ( false ) ;
2010-06-09 00:52:24 +08:00
master_out_comm . AdoptFileDesriptor ( m_editline_pty . GetMasterFileDescriptor ( ) , false ) ;
master_out_comm . SetReadThreadBytesReceivedCallback ( Driver : : MasterThreadBytesReceived , this ) ;
if ( master_out_comm . ReadThreadStart ( ) = = false )
{
: : fprintf ( stderr , " error: failed to start master out read thread " ) ;
exit ( 5 ) ;
}
// const char *crash_log = GetCrashLogFilename();
// if (crash_log)
// {
// ParseCrashLog (crash_log);
// }
//
2010-06-23 09:19:29 +08:00
SBCommandInterpreter sb_interpreter = m_debugger . GetCommandInterpreter ( ) ;
2010-06-09 00:52:24 +08:00
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
m_io_channel_ap . reset ( new IOChannel ( m_editline_slave_fh , editline_output_slave_fh , stdout , stderr , this ) ) ;
SBCommunication out_comm_2 ( " driver.editline_output " ) ;
out_comm_2 . SetCloseOnEOF ( false ) ;
out_comm_2 . AdoptFileDesriptor ( editline_output_pty . GetMasterFileDescriptor ( ) , false ) ;
out_comm_2 . SetReadThreadBytesReceivedCallback ( IOChannel : : LibeditOutputBytesReceived , m_io_channel_ap . get ( ) ) ;
if ( out_comm_2 . ReadThreadStart ( ) = = false )
{
: : fprintf ( stderr , " error: failed to start libedit output read thread " ) ;
exit ( 5 ) ;
}
2010-06-09 00:52:24 +08:00
struct winsize window_size ;
if ( isatty ( STDIN_FILENO )
& & : : ioctl ( STDIN_FILENO , TIOCGWINSZ , & window_size ) = = 0 )
{
2010-09-04 08:03:46 +08:00
if ( window_size . ws_col > 0 )
2010-09-18 09:14:36 +08:00
m_debugger . SetTerminalWidth ( window_size . ws_col ) ;
2010-06-09 00:52:24 +08:00
}
// Since input can be redirected by the debugger, we must insert our editline
// input reader in the queue so we know when our reader should be active
// and so we can receive bytes only when we are supposed to.
2010-06-23 09:19:29 +08:00
SBError err ( m_editline_reader . Initialize ( m_debugger ,
Driver : : EditLineInputReaderCallback , // callback
2010-06-09 00:52:24 +08:00
this , // baton
eInputReaderGranularityByte , // token_size
NULL , // end token - NULL means never done
NULL , // prompt - taken care of elsewhere
false ) ) ; // echo input - don't need Debugger
// to do this, we handle it elsewhere
if ( err . Fail ( ) )
{
: : fprintf ( stderr , " error: %s " , err . GetCString ( ) ) ;
exit ( 6 ) ;
}
2010-06-23 09:19:29 +08:00
m_debugger . PushInputReader ( m_editline_reader ) ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
SBListener listener ( m_debugger . GetListener ( ) ) ;
2010-06-09 00:52:24 +08:00
if ( listener . IsValid ( ) )
{
listener . StartListeningForEvents ( * m_io_channel_ap ,
IOChannel : : eBroadcastBitHasUserInput |
IOChannel : : eBroadcastBitUserInterrupt |
IOChannel : : eBroadcastBitThreadShouldExit |
IOChannel : : eBroadcastBitThreadDidStart |
IOChannel : : eBroadcastBitThreadDidExit ) ;
if ( m_io_channel_ap - > Start ( ) )
{
bool iochannel_thread_exited = false ;
listener . StartListeningForEvents ( sb_interpreter . GetBroadcaster ( ) ,
2011-05-04 04:53:11 +08:00
SBCommandInterpreter : : eBroadcastBitQuitCommandReceived |
SBCommandInterpreter : : eBroadcastBitAsynchronousOutputData |
SBCommandInterpreter : : eBroadcastBitAsynchronousErrorData ) ;
2010-06-09 00:52:24 +08:00
// Before we handle any options from the command line, we parse the
// .lldbinit file in the user's home directory.
SBCommandReturnObject result ;
sb_interpreter . SourceInitFileInHomeDirectory ( result ) ;
if ( GetDebugMode ( ) )
{
2010-06-23 09:19:29 +08:00
result . PutError ( m_debugger . GetErrorFileHandle ( ) ) ;
result . PutOutput ( m_debugger . GetOutputFileHandle ( ) ) ;
2010-06-09 00:52:24 +08:00
}
// Now we handle options we got from the command line
char command_string [ PATH_MAX * 2 ] ;
const size_t num_source_command_files = GetNumSourceCommandFiles ( ) ;
if ( num_source_command_files > 0 )
{
for ( size_t i = 0 ; i < num_source_command_files ; + + i )
{
const char * command_file = GetSourceCommandFileAtIndex ( i ) ;
2010-07-29 05:16:11 +08:00
: : snprintf ( command_string , sizeof ( command_string ) , " command source '%s' " , command_file ) ;
2010-06-23 09:19:29 +08:00
m_debugger . GetCommandInterpreter ( ) . HandleCommand ( command_string , result , false ) ;
2010-06-09 00:52:24 +08:00
if ( GetDebugMode ( ) )
{
2010-06-23 09:19:29 +08:00
result . PutError ( m_debugger . GetErrorFileHandle ( ) ) ;
result . PutOutput ( m_debugger . GetOutputFileHandle ( ) ) ;
2010-06-09 00:52:24 +08:00
}
}
}
2010-12-09 06:23:24 +08:00
const size_t num_args = m_option_data . m_args . size ( ) ;
if ( num_args > 0 )
2010-06-09 00:52:24 +08:00
{
char arch_name [ 64 ] ;
2010-06-23 09:19:29 +08:00
if ( m_debugger . GetDefaultArchitecture ( arch_name , sizeof ( arch_name ) ) )
2010-12-09 06:23:24 +08:00
: : snprintf ( command_string ,
sizeof ( command_string ) ,
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 16:33:37 +08:00
" target create --arch=%s '%s' " ,
2010-12-09 06:23:24 +08:00
arch_name ,
m_option_data . m_args [ 0 ] . c_str ( ) ) ;
2010-06-09 00:52:24 +08:00
else
2010-12-09 06:23:24 +08:00
: : snprintf ( command_string ,
sizeof ( command_string ) ,
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 16:33:37 +08:00
" target create '%s' " ,
2010-12-09 06:23:24 +08:00
m_option_data . m_args [ 0 ] . c_str ( ) ) ;
2010-06-09 00:52:24 +08:00
2010-06-23 09:19:29 +08:00
m_debugger . HandleCommand ( command_string ) ;
2010-12-09 06:23:24 +08:00
if ( num_args > 1 )
{
m_debugger . HandleCommand ( " settings clear target.process.run-args " ) ;
char arg_cstr [ 1024 ] ;
for ( size_t arg_idx = 1 ; arg_idx < num_args ; + + arg_idx )
{
: : snprintf ( arg_cstr , sizeof ( arg_cstr ) , " settings append target.process.run-args \" %s \" " , m_option_data . m_args [ arg_idx ] . c_str ( ) ) ;
m_debugger . HandleCommand ( arg_cstr ) ;
}
}
2010-06-09 00:52:24 +08:00
}
// Now that all option parsing is done, we try and parse the .lldbinit
// file in the current working directory
sb_interpreter . SourceInitFileInCurrentWorkingDirectory ( result ) ;
if ( GetDebugMode ( ) )
{
2010-06-23 09:19:29 +08:00
result . PutError ( m_debugger . GetErrorFileHandle ( ) ) ;
result . PutOutput ( m_debugger . GetOutputFileHandle ( ) ) ;
2010-06-09 00:52:24 +08:00
}
SBEvent event ;
// Make sure the IO channel is started up before we try to tell it we
// are ready for input
listener . WaitForEventForBroadcasterWithType ( UINT32_MAX ,
* m_io_channel_ap ,
IOChannel : : eBroadcastBitThreadDidStart ,
event ) ;
ReadyForCommand ( ) ;
bool done = false ;
while ( ! done )
{
listener . WaitForEvent ( UINT32_MAX , event ) ;
if ( event . IsValid ( ) )
{
if ( event . GetBroadcaster ( ) . IsValid ( ) )
{
uint32_t event_type = event . GetType ( ) ;
if ( event . BroadcasterMatchesRef ( * m_io_channel_ap ) )
{
if ( ( event_type & IOChannel : : eBroadcastBitThreadShouldExit ) | |
( event_type & IOChannel : : eBroadcastBitThreadDidExit ) )
{
done = true ;
if ( event_type & IOChannel : : eBroadcastBitThreadDidExit )
iochannel_thread_exited = true ;
break ;
}
else
done = HandleIOEvent ( event ) ;
}
2010-08-27 05:32:51 +08:00
else if ( event . BroadcasterMatchesRef ( m_debugger . GetSelectedTarget ( ) . GetProcess ( ) . GetBroadcaster ( ) ) )
2010-06-09 00:52:24 +08:00
{
HandleProcessEvent ( event ) ;
}
else if ( event . BroadcasterMatchesRef ( sb_interpreter . GetBroadcaster ( ) ) )
{
if ( event_type & SBCommandInterpreter : : eBroadcastBitQuitCommandReceived )
done = true ;
2011-05-04 04:53:11 +08:00
else if ( event_type & SBCommandInterpreter : : eBroadcastBitAsynchronousErrorData )
{
const char * data = SBEvent : : GetCStringFromEvent ( event ) ;
m_io_channel_ap - > ErrWrite ( data , strlen ( data ) , ASYNC ) ;
}
else if ( event_type & SBCommandInterpreter : : eBroadcastBitAsynchronousOutputData )
{
const char * data = SBEvent : : GetCStringFromEvent ( event ) ;
m_io_channel_ap - > OutWrite ( data , strlen ( data ) , ASYNC ) ;
}
2010-06-09 00:52:24 +08:00
}
}
}
}
reset_stdin_termios ( ) ;
CloseIOChannelFile ( ) ;
if ( ! iochannel_thread_exited )
{
2010-07-14 08:18:15 +08:00
event . Clear ( ) ;
2010-06-09 00:52:24 +08:00
listener . GetNextEventForBroadcasterWithType ( * m_io_channel_ap ,
IOChannel : : eBroadcastBitThreadDidExit ,
event ) ;
if ( ! event . IsValid ( ) )
{
// Send end EOF to the driver file descriptor
m_io_channel_ap - > Stop ( ) ;
}
}
2010-08-27 05:32:51 +08:00
SBProcess process = m_debugger . GetSelectedTarget ( ) . GetProcess ( ) ;
2010-06-09 00:52:24 +08:00
if ( process . IsValid ( ) )
process . Destroy ( ) ;
}
}
}
void
Driver : : ReadyForCommand ( )
{
if ( m_waiting_for_command = = false )
{
m_waiting_for_command = true ;
BroadcastEventByType ( Driver : : eBroadcastBitReadyForInput , true ) ;
}
}
2010-09-10 01:45:09 +08:00
void
sigwinch_handler ( int signo )
{
struct winsize window_size ;
if ( isatty ( STDIN_FILENO )
& & : : ioctl ( STDIN_FILENO , TIOCGWINSZ , & window_size ) = = 0 )
{
if ( ( window_size . ws_col > 0 ) & & ( strlen ( g_debugger_name ) > 0 ) )
{
char width_str_buffer [ 25 ] ;
: : sprintf ( width_str_buffer , " %d " , window_size . ws_col ) ;
SBDebugger : : SetInternalVariable ( " term-width " , width_str_buffer , g_debugger_name ) ;
}
}
}
2010-11-20 04:47:54 +08:00
void
sigint_handler ( int signo )
{
static bool g_interrupt_sent = false ;
if ( g_driver )
{
if ( ! g_interrupt_sent )
{
g_interrupt_sent = true ;
g_driver - > GetDebugger ( ) . DispatchInputInterrupt ( ) ;
g_interrupt_sent = false ;
return ;
}
}
exit ( signo ) ;
}
2010-06-09 00:52:24 +08:00
int
2011-01-28 04:15:39 +08:00
main ( int argc , char const * argv [ ] , const char * envp [ ] )
2010-06-09 00:52:24 +08:00
{
SBDebugger : : Initialize ( ) ;
2010-11-08 05:02:03 +08:00
SBHostOS : : ThreadCreated ( " <lldb.driver.main-thread> " ) ;
2010-06-09 00:52:24 +08:00
2010-10-19 11:25:40 +08:00
signal ( SIGPIPE , SIG_IGN ) ;
2010-09-10 01:45:09 +08:00
signal ( SIGWINCH , sigwinch_handler ) ;
2010-11-20 04:47:54 +08:00
signal ( SIGINT , sigint_handler ) ;
2010-09-10 01:45:09 +08:00
2010-06-23 09:19:29 +08:00
// Create a scope for driver so that the driver object will destroy itself
// before SBDebugger::Terminate() is called.
2010-06-09 00:52:24 +08:00
{
2010-06-23 09:19:29 +08:00
Driver driver ;
bool exit = false ;
SBError error ( driver . ParseArgs ( argc , argv , stdout , exit ) ) ;
if ( error . Fail ( ) )
{
const char * error_cstr = error . GetCString ( ) ;
if ( error_cstr )
: : fprintf ( stderr , " error: %s \n " , error_cstr ) ;
}
else if ( ! exit )
{
driver . MainLoop ( ) ;
}
2010-06-09 00:52:24 +08:00
}
SBDebugger : : Terminate ( ) ;
return 0 ;
}