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"
# 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-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 ) ;
}
static lldb : : OptionDefinition g_options [ ] =
{
2010-06-29 05:30:43 +08:00
{ LLDB_OPT_SET_1 , true , " help " , ' h ' , no_argument , NULL , NULL , NULL ,
2010-06-09 00:52:24 +08:00
" Prints out the usage information for the LLDB debugger. " } ,
2010-06-29 05:30:43 +08:00
{ LLDB_OPT_SET_2 , true , " version " , ' v ' , no_argument , NULL , NULL , NULL ,
2010-06-09 00:52:24 +08:00
" Prints out the current version number of the LLDB debugger. " } ,
2010-06-29 05:30:43 +08:00
{ LLDB_OPT_SET_3 , true , " arch " , ' a ' , required_argument , NULL , NULL , " <architecture> " ,
2010-06-09 00:52:24 +08:00
" 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. " } ,
2010-06-16 02:47:14 +08:00
{ LLDB_OPT_SET_3 | LLDB_OPT_SET_4 , false , " script-language " , ' l ' , required_argument , NULL , NULL , " <scripting-language> " ,
2010-06-09 00:52:24 +08:00
" 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. " } ,
2010-06-16 02:47:14 +08:00
{ LLDB_OPT_SET_3 | LLDB_OPT_SET_4 , false , " debug " , ' d ' , no_argument , NULL , NULL , NULL ,
2010-06-09 00:52:24 +08:00
" Tells the debugger to print out extra information for debugging itself. " } ,
2010-06-16 02:47:14 +08:00
{ LLDB_OPT_SET_3 | LLDB_OPT_SET_4 , false , " source " , ' s ' , required_argument , NULL , NULL , " <file> " ,
2010-06-09 00:52:24 +08:00
" Tells the debugger to read in and execute the file <file>, which should contain lldb commands. " } ,
2010-06-29 05:30:43 +08:00
{ LLDB_OPT_SET_3 , true , " file " , ' f ' , required_argument , NULL , NULL , " <filename> " ,
2010-06-16 02:47:14 +08:00
" Tells the debugger to use the file <filename> as the program to be debugged. " } ,
2010-09-02 08:18:39 +08:00
{ LLDB_OPT_SET_ALL , false , " editor " , ' e ' , no_argument , NULL , NULL , NULL ,
2010-08-31 03:44:40 +08:00
" Tells the debugger to open source files using the host's \" external editor \" mechanism. " } ,
2010-06-29 05:30:43 +08:00
// { LLDB_OPT_SET_4, true, "crash-log", 'c', required_argument, NULL, NULL, "<file>",
// "Load executable images from a crash log for symbolication." },
2010-06-09 00:52:24 +08:00
{ 0 , false , NULL , 0 , 0 , NULL , NULL , NULL , NULL }
} ;
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-06-09 00:52:24 +08:00
}
Driver : : ~ Driver ( )
{
}
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
ShowUsage ( FILE * out , lldb : : OptionDefinition * option_table , Driver : : OptionData data )
{
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 )
{
if ( option_table [ i ] . required )
{
if ( option_table [ i ] . option_has_arg = = required_argument )
fprintf ( out , " -%c %s " , option_table [ i ] . short_option , option_table [ i ] . argument_name ) ;
else if ( option_table [ i ] . option_has_arg = = optional_argument )
fprintf ( out , " -%c [%s] " , option_table [ i ] . short_option , option_table [ i ] . argument_name ) ;
else
fprintf ( out , " -%c " , option_table [ i ] . short_option ) ;
}
else
{
if ( option_table [ i ] . option_has_arg = = required_argument )
fprintf ( out , " [-%c %s] " , option_table [ i ] . short_option , option_table [ i ] . argument_name ) ;
else if ( option_table [ i ] . option_has_arg = = optional_argument )
fprintf ( out , " [-%c [%s]] " , option_table [ i ] . short_option , option_table [ i ] . argument_name ) ;
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 ( ) )
{
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-06-09 00:52:24 +08:00
if ( option_table [ i ] . argument_name ! = NULL )
fprintf ( out , " %s " , option_table [ i ] . argument_name ) ;
fprintf ( out , " \n " ) ;
2010-07-10 04:39:50 +08:00
fprintf ( out , " %*s--%s " , indent_level , " " , option_table [ i ] . long_option ) ;
2010-06-09 00:52:24 +08:00
if ( option_table [ i ] . argument_name ! = NULL )
fprintf ( out , " %s " , option_table [ i ] . argument_name ) ;
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
2010-07-10 04:39:50 +08:00
BuildGetOptTable ( lldb : : OptionDefinition * expanded_option_table , struct option * * getopt_table , 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 ;
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-06-09 00:52:24 +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 ;
option_seen . set ( short_opt ) ;
+ + j ;
2010-07-10 04:39:50 +08:00
}
}
2010-06-09 00:52:24 +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-23 09:19:29 +08:00
Driver : : OptionData : : OptionData ( ) :
m_filename ( ) ,
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 ) ,
2010-08-31 03:44:40 +08:00
m_seen_options ( ) ,
m_use_external_editor ( false )
2010-06-23 09:19:29 +08:00
{
}
Driver : : OptionData : : ~ OptionData ( )
{
}
void
Driver : : OptionData : : Clear ( )
{
m_filename . clear ( ) ;
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
{
if ( m_option_data . m_filename . empty ( ) )
return NULL ;
return m_option_data . m_filename . c_str ( ) ;
}
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-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 ;
}
long_options = ( struct option * ) malloc ( ( num_options + 1 ) * sizeof ( struct option ) ) ;
BuildGetOptTable ( g_options , & long_options , num_options ) ;
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-08-31 03:44:40 +08:00
case ' e ' :
m_option_data . m_use_external_editor = true ;
break ;
2010-06-23 09:19:29 +08:00
case ' f ' :
{
SBFileSpec file ( optarg ) ;
if ( file . Exists ( ) )
m_option_data . m_filename = 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 ) ;
m_option_data . m_filename = path ;
}
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 ( ) )
return error ;
}
2010-06-09 00:52:24 +08:00
}
2010-06-16 02:47:14 +08:00
// If there is a trailing argument, it is the filename.
if ( optind = = argc - 1 )
2010-06-09 00:52:24 +08:00
{
2010-06-16 02:47:14 +08:00
if ( m_option_data . m_filename . empty ( ) )
2010-06-09 00:52:24 +08:00
{
2010-06-16 02:47:14 +08:00
m_option_data . m_filename = argv [ optind ] ;
}
else
{
2010-06-23 09:19:29 +08:00
error . SetErrorStringWithFormat ( " error: don't provide a file both on in the -f option and as an argument. " ) ;
2010-06-09 00:52:24 +08:00
}
2010-06-16 02:47:14 +08:00
}
else if ( optind < argc - 1 )
{
// Trailing extra arguments...
2010-06-23 09:19:29 +08:00
error . SetErrorStringWithFormat ( " error: trailing extra arguments - only one the filename is allowed. " ) ;
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 ) ;
}
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
{
// All other combinations are valid; do nothing more here.
}
2010-06-23 09:19:29 +08:00
return error ;
2010-06-09 00:52:24 +08:00
}
void
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-08-27 05:32:51 +08:00
while ( ( len = m_debugger . GetSelectedTarget ( ) . GetProcess ( ) . GetSTDOUT ( stdio_buffer , sizeof ( stdio_buffer ) ) ) > 0 )
2010-06-09 00:52:24 +08:00
m_io_channel_ap - > OutWrite ( stdio_buffer , len ) ;
}
void
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-08-27 05:32:51 +08:00
while ( ( len = m_debugger . GetSelectedTarget ( ) . GetProcess ( ) . GetSTDERR ( stdio_buffer , sizeof ( stdio_buffer ) ) ) > 0 )
2010-06-09 00:52:24 +08:00
m_io_channel_ap - > ErrWrite ( stdio_buffer , len ) ;
}
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.
GetProcessSTDOUT ( ) ;
}
else if ( event_type & SBProcess : : eBroadcastBitSTDERR )
{
// The process has stderr available, get it and write it out to the
// appropriate place.
GetProcessSTDERR ( ) ;
}
else if ( event_type & SBProcess : : eBroadcastBitStateChanged )
{
// Drain all stout and stderr so we don't see any output come after
// we print our prompts
GetProcessSTDOUT ( ) ;
GetProcessSTDERR ( ) ;
// 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 :
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 ) ) ;
2010-06-09 00:52:24 +08:00
m_io_channel_ap - > OutWrite ( message , message_len ) ;
}
break ;
case eStateRunning :
// Don't be chatty when we run...
break ;
case eStateExited :
2010-06-23 09:19:29 +08:00
m_debugger . HandleCommand ( " process status " ) ;
2010-06-09 00:52:24 +08:00
m_io_channel_ap - > RefreshPrompt ( ) ;
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 ( ) ) ;
m_io_channel_ap - > OutWrite ( message , message_len ) ;
}
else
{
2010-08-27 05:32:51 +08:00
UpdateSelectedThread ( ) ;
2010-06-23 09:19:29 +08:00
m_debugger . HandleCommand ( " process status " ) ;
2010-06-09 00:52:24 +08:00
m_io_channel_ap - > RefreshPrompt ( ) ;
}
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 ;
2010-06-23 09:19:29 +08:00
if ( m_debugger . GetCommandInterpreter ( ) . HandleCommand ( command_string , result , true ) ! = lldb : : eReturnStatusQuit )
2010-06-09 00:52:24 +08:00
{
m_io_channel_ap - > ErrWrite ( result . GetError ( ) , result . GetErrorSize ( ) ) ;
m_io_channel_ap - > OutWrite ( result . GetOutput ( ) , result . GetOutputSize ( ) ) ;
}
// 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 ;
}
//struct CrashImageInfo
//{
// std::string path;
// VMRange text_range;
// UUID uuid;
//};
//
//void
//Driver::ParseCrashLog (const char *crash_log)
//{
// printf("Parsing crash log: %s\n", crash_log);
//
// char image_path[PATH_MAX];
// std::vector<CrashImageInfo> crash_infos;
// if (crash_log && crash_log[0])
// {
// FileSpec crash_log_file (crash_log);
// STLStringArray crash_log_lines;
// if (crash_log_file.ReadFileLines (crash_log_lines))
// {
// const size_t num_crash_log_lines = crash_log_lines.size();
// size_t i;
// for (i=0; i<num_crash_log_lines; ++i)
// {
// const char *line = crash_log_lines[i].c_str();
// if (strstr (line, "Code Type:"))
// {
// char arch_string[256];
// if (sscanf(line, "%s", arch_string))
// {
// if (strcmp(arch_string, "X86-64"))
// lldb::GetDefaultArchitecture ().SetArch ("x86_64");
// else if (strcmp(arch_string, "X86"))
// lldb::GetDefaultArchitecture ().SetArch ("i386");
// else
// {
// ArchSpec arch(arch_string);
// if (arch.IsValid ())
// lldb::GetDefaultArchitecture () = arch;
// else
// fprintf(stderr, "Unrecognized architecture: %s\n", arch_string);
// }
// }
// }
// else
// if (strstr(line, "Path:"))
// {
// const char *p = line + strlen("Path:");
// while (isspace(*p))
// ++p;
//
// m_option_data.m_filename.assign (p);
// }
// else
// if (strstr(line, "Binary Images:"))
// {
// while (++i < num_crash_log_lines)
// {
// if (crash_log_lines[i].empty())
// break;
//
// line = crash_log_lines[i].c_str();
// uint64_t text_start_addr;
// uint64_t text_end_addr;
// char uuid_cstr[64];
// int bytes_consumed_before_uuid = 0;
// int bytes_consumed_after_uuid = 0;
//
// int items_parsed = ::sscanf (line,
// "%llx - %llx %*s %*s %*s %n%s %n",
// &text_start_addr,
// &text_end_addr,
// &bytes_consumed_before_uuid,
// uuid_cstr,
// &bytes_consumed_after_uuid);
//
// if (items_parsed == 3)
// {
//
// CrashImageInfo info;
// info.text_range.SetBaseAddress(text_start_addr);
// info.text_range.SetEndAddress(text_end_addr);
//
// if (uuid_cstr[0] == '<')
// {
// if (info.uuid.SetfromCString (&uuid_cstr[1]) == 0)
// info.uuid.Clear();
//
// ::strncpy (image_path, line + bytes_consumed_after_uuid, sizeof(image_path));
// }
// else
// {
// ::strncpy (image_path, line + bytes_consumed_before_uuid, sizeof(image_path));
// }
//
// info.path = image_path;
//
// crash_infos.push_back (info);
//
// info.uuid.GetAsCString(uuid_cstr, sizeof(uuid_cstr));
//
// printf("0x%16.16llx - 0x%16.16llx <%s> %s\n",
// text_start_addr,
// text_end_addr,
// uuid_cstr,
// image_path);
// }
// }
// }
// }
// }
//
// if (crash_infos.size())
// {
2010-06-23 09:19:29 +08:00
// SBTarget target (m_debugger.CreateTarget (crash_infos.front().path.c_str(),
2010-06-09 00:52:24 +08:00
// lldb::GetDefaultArchitecture().AsCString (),
// false));
// if (target.IsValid())
// {
//
// }
// }
// }
//}
//
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 ;
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 ) ;
}
}
// 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.
SBCommunication master_out_comm ( " driver.editline " ) ;
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
m_io_channel_ap . reset ( new IOChannel ( m_editline_slave_fh , stdout , stderr , this ) ) ;
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 ( ) ,
SBCommandInterpreter : : eBroadcastBitQuitCommandReceived ) ;
// 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
}
}
}
if ( ! m_option_data . m_filename . empty ( ) )
{
char arch_name [ 64 ] ;
2010-06-23 09:19:29 +08:00
if ( m_debugger . GetDefaultArchitecture ( arch_name , sizeof ( arch_name ) ) )
2010-06-09 00:52:24 +08:00
: : snprintf ( command_string , sizeof ( command_string ) , " file --arch=%s '%s' " , arch_name ,
m_option_data . m_filename . c_str ( ) ) ;
else
: : snprintf ( command_string , sizeof ( command_string ) , " file '%s' " , m_option_data . m_filename . c_str ( ) ) ;
2010-06-23 09:19:29 +08:00
m_debugger . HandleCommand ( command_string ) ;
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 ;
}
}
}
}
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-06-09 00:52:24 +08:00
int
main ( int argc , char const * argv [ ] )
{
SBDebugger : : Initialize ( ) ;
SBHostOS : : ThreadCreated ( " [main] " ) ;
2010-09-10 01:45:09 +08:00
signal ( SIGWINCH , sigwinch_handler ) ;
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 ;
}