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"
|
|
|
|
|
2013-10-15 23:46:40 +08:00
|
|
|
#include <stdio.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>
|
|
|
|
|
2013-10-15 23:46:40 +08:00
|
|
|
#include <thread>
|
2012-02-08 13:23:15 +08:00
|
|
|
#include "lldb/API/SBBreakpoint.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"
|
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 ();
|
2012-02-03 03:28:31 +08:00
|
|
|
static bool g_old_stdin_termios_is_valid = false;
|
2010-06-09 00:52:24 +08:00
|
|
|
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 ()
|
|
|
|
{
|
2012-02-03 03:28:31 +08:00
|
|
|
if (g_old_stdin_termios_is_valid)
|
|
|
|
{
|
|
|
|
g_old_stdin_termios_is_valid = false;
|
|
|
|
::tcsetattr (STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
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.
|
2012-12-04 08:32:51 +08:00
|
|
|
int short_option; // Single character for this option.
|
2011-03-25 05:19:54 +08:00
|
|
|
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;
|
|
|
|
|
2011-09-14 07:25:31 +08:00
|
|
|
#define LLDB_3_TO_5 LLDB_OPT_SET_3|LLDB_OPT_SET_4|LLDB_OPT_SET_5
|
|
|
|
#define LLDB_4_TO_5 LLDB_OPT_SET_4|LLDB_OPT_SET_5
|
|
|
|
|
2011-03-25 05:19:54 +08:00
|
|
|
static OptionDefinition g_options[] =
|
|
|
|
{
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_OPT_SET_1, true , "help" , 'h', no_argument , 0, eArgTypeNone,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Prints out the usage information for the LLDB debugger." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_OPT_SET_2, true , "version" , 'v', no_argument , 0, eArgTypeNone,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Prints out the current version number of the LLDB debugger." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_OPT_SET_3, true , "arch" , 'a', required_argument, 0, eArgTypeArchitecture,
|
2011-09-16 05:30:02 +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." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_OPT_SET_3, true , "file" , 'f', required_argument, 0, eArgTypeFilename,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Tells the debugger to use the file <filename> as the program to be debugged." },
|
2012-10-24 11:29:40 +08:00
|
|
|
{ LLDB_OPT_SET_3, false, "core" , 'c', required_argument, 0, eArgTypeFilename,
|
2012-08-16 06:10:42 +08:00
|
|
|
"Tells the debugger to use the fullpath to <path> as the core file." },
|
2014-02-06 05:35:09 +08:00
|
|
|
{ LLDB_OPT_SET_5, true , "attach-pid" , 'p', required_argument, 0, eArgTypePid,
|
|
|
|
"Tells the debugger to attach to a process with the given pid." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_OPT_SET_4, true , "attach-name" , 'n', required_argument, 0, eArgTypeProcessName,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Tells the debugger to attach to a process with the given name." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_OPT_SET_4, true , "wait-for" , 'w', no_argument , 0, eArgTypeNone,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Tells the debugger to wait for a process with the given pid or name to launch before attaching." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_3_TO_5, false, "source" , 's', required_argument, 0, eArgTypeFilename,
|
2013-09-17 09:53:35 +08:00
|
|
|
"Tells the debugger to read in and execute the lldb commands in the given file, after any file provided on the command line has been loaded." },
|
2013-09-14 08:20:24 +08:00
|
|
|
{ LLDB_3_TO_5, false, "one-line" , 'o', required_argument, 0, eArgTypeNone,
|
2013-09-17 09:53:35 +08:00
|
|
|
"Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded." },
|
2013-09-14 08:20:24 +08:00
|
|
|
{ LLDB_3_TO_5, false, "source-before-file" , 'S', required_argument, 0, eArgTypeFilename,
|
2013-09-17 09:53:35 +08:00
|
|
|
"Tells the debugger to read in and execute the lldb commands in the given file, before any file provided on the command line has been loaded." },
|
2013-09-14 08:20:24 +08:00
|
|
|
{ LLDB_3_TO_5, false, "one-line-before-file" , 'O', required_argument, 0, eArgTypeNone,
|
2013-09-17 09:53:35 +08:00
|
|
|
"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded." },
|
2014-02-06 05:35:09 +08:00
|
|
|
{ LLDB_3_TO_5, false, "source-quietly" , 'Q', no_argument , 0, eArgTypeNone,
|
|
|
|
"Tells the debugger suppress output from commands provided in the -s, -S, -O and -o commands." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_3_TO_5, false, "editor" , 'e', no_argument , 0, eArgTypeNone,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Tells the debugger to open source files using the host's \"external editor\" mechanism." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ LLDB_3_TO_5, false, "no-lldbinit" , 'x', no_argument , 0, eArgTypeNone,
|
2011-09-16 05:30:02 +08:00
|
|
|
"Do not automatically parse any '.lldbinit' files." },
|
2013-09-14 08:20:24 +08:00
|
|
|
{ LLDB_3_TO_5, false, "no-use-colors" , 'X', no_argument , 0, eArgTypeNone,
|
2013-05-24 04:47:45 +08:00
|
|
|
"Do not use colors." },
|
|
|
|
{ LLDB_OPT_SET_6, true , "python-path" , 'P', no_argument , 0, eArgTypeNone,
|
2012-12-22 06:22:26 +08:00
|
|
|
"Prints out the path to the lldb.py file for this version of lldb." },
|
2014-02-06 05:35:09 +08:00
|
|
|
{ LLDB_3_TO_5, false, "script-language", 'l', required_argument, 0, 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_3_TO_5, false, "debug" , 'd', no_argument , 0, eArgTypeNone,
|
|
|
|
"Tells the debugger to print out extra information for debugging itself." },
|
2012-09-12 02:11:16 +08:00
|
|
|
{ 0, false, NULL , 0 , 0 , 0, eArgTypeNone, NULL }
|
2010-06-09 00:52:24 +08:00
|
|
|
};
|
|
|
|
|
2011-09-14 07:25:31 +08:00
|
|
|
static const uint32_t last_option_set_with_args = 2;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
Driver::Driver () :
|
|
|
|
SBBroadcaster ("Driver"),
|
2011-08-13 08:22:20 +08:00
|
|
|
m_debugger (SBDebugger::Create(false)),
|
2014-01-28 07:43:24 +08:00
|
|
|
m_option_data ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-05-29 12:06:55 +08:00
|
|
|
// We want to be able to handle CTRL+D in the terminal to have it terminate
|
|
|
|
// certain input
|
|
|
|
m_debugger.SetCloseInputOnEOF (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
|
|
|
}
|
|
|
|
|
|
|
|
|
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, "\nUsage:\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);
|
2011-08-17 07:15:02 +08:00
|
|
|
bool is_help_line = false;
|
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);
|
2011-08-17 07:15:02 +08:00
|
|
|
// This is a bit of a hack, but there's no way to say certain options don't have arguments yet...
|
|
|
|
// so we do it by hand here.
|
|
|
|
if (option_table[i].short_option == 'h')
|
|
|
|
is_help_line = true;
|
|
|
|
|
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
|
|
|
}
|
2011-09-14 07:25:31 +08:00
|
|
|
if (!is_help_line && (opt_set <= last_option_set_with_args))
|
2011-08-17 07:15:02 +08:00
|
|
|
fprintf (out, " [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]");
|
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;
|
2014-02-06 05:35:09 +08:00
|
|
|
|
|
|
|
fprintf (out, "\n%*sNotes:\n",
|
|
|
|
indent_level, "");
|
|
|
|
indent_level += 5;
|
|
|
|
|
2013-09-17 09:53:35 +08:00
|
|
|
fprintf (out, "\n%*sMultiple \"-s\" and \"-o\" options can be provided. They will be processed from left to right in order, "
|
|
|
|
"\n%*swith the source files and commands interleaved. The same is true of the \"-S\" and \"-O\" options."
|
|
|
|
"\n%*sThe before file and after file sets can intermixed freely, the command parser will sort them out."
|
|
|
|
"\n%*sThe order of the file specifiers (\"-c\", \"-f\", etc.) is not significant in this regard.\n\n",
|
|
|
|
indent_level, "",
|
|
|
|
indent_level, "",
|
|
|
|
indent_level, "",
|
|
|
|
indent_level, "");
|
|
|
|
|
2014-02-06 05:35:09 +08:00
|
|
|
fprintf (out, "\n%*sIf you don't provide -f then the first argument will be the file to be debugged"
|
|
|
|
"\n%*swhich means that '%s -- <filename> [<ARG1> [<ARG2>]]' also works."
|
|
|
|
"\n%*sBut remember to end the options with \"--\" if any of your arguments have a \"-\" in them.\n\n",
|
2011-08-17 07:57:58 +08:00
|
|
|
indent_level, "",
|
|
|
|
indent_level, "",
|
|
|
|
name,
|
|
|
|
indent_level, "");
|
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),
|
2012-08-16 06:10:42 +08:00
|
|
|
m_core_file (),
|
2010-07-10 04:39:50 +08:00
|
|
|
m_crash_log (),
|
2013-09-14 08:20:24 +08:00
|
|
|
m_initial_commands (),
|
|
|
|
m_after_file_commands (),
|
2010-06-23 09:19:29 +08:00
|
|
|
m_debug_mode (false),
|
2013-09-14 08:20:24 +08:00
|
|
|
m_source_quietly(false),
|
2010-07-10 04:39:50 +08:00
|
|
|
m_print_version (false),
|
2012-12-22 06:22:26 +08:00
|
|
|
m_print_python_path (false),
|
2010-06-23 09:19:29 +08:00
|
|
|
m_print_help (false),
|
2011-09-14 07:25:31 +08:00
|
|
|
m_wait_for(false),
|
|
|
|
m_process_name(),
|
|
|
|
m_process_pid(LLDB_INVALID_PROCESS_ID),
|
2011-11-01 06:50:49 +08:00
|
|
|
m_use_external_editor(false),
|
2011-04-12 03:41:40 +08:00
|
|
|
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;
|
2013-09-14 08:20:24 +08:00
|
|
|
m_initial_commands.clear ();
|
|
|
|
m_after_file_commands.clear ();
|
2010-06-23 09:19:29 +08:00
|
|
|
m_debug_mode = false;
|
2013-09-14 08:20:24 +08:00
|
|
|
m_source_quietly = false;
|
2010-06-23 09:19:29 +08:00
|
|
|
m_print_help = false;
|
|
|
|
m_print_version = false;
|
2012-12-22 06:22:26 +08:00
|
|
|
m_print_python_path = false;
|
2010-08-31 03:44:40 +08:00
|
|
|
m_use_external_editor = false;
|
2011-09-14 07:25:31 +08:00
|
|
|
m_wait_for = false;
|
|
|
|
m_process_name.erase();
|
|
|
|
m_process_pid = LLDB_INVALID_PROCESS_ID;
|
2010-06-23 09:19:29 +08:00
|
|
|
}
|
|
|
|
|
2013-09-14 08:20:24 +08:00
|
|
|
void
|
|
|
|
Driver::OptionData::AddInitialCommand (const char *command, bool before_file, bool is_file, SBError &error)
|
|
|
|
{
|
|
|
|
std::vector<std::pair<bool, std::string> > *command_set;
|
|
|
|
if (before_file)
|
|
|
|
command_set = &(m_initial_commands);
|
|
|
|
else
|
|
|
|
command_set = &(m_after_file_commands);
|
|
|
|
|
|
|
|
if (is_file)
|
|
|
|
{
|
|
|
|
SBFileSpec file(command);
|
|
|
|
if (file.Exists())
|
|
|
|
command_set->push_back (std::pair<bool, std::string> (true, optarg));
|
|
|
|
else if (file.ResolveExecutableLocation())
|
|
|
|
{
|
|
|
|
char final_path[PATH_MAX];
|
|
|
|
file.GetPath (final_path, sizeof(final_path));
|
|
|
|
std::string path_str (final_path);
|
|
|
|
command_set->push_back (std::pair<bool, std::string> (true, path_str));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", optarg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
command_set->push_back (std::pair<bool, std::string> (false, optarg));
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-09-14 08:20:24 +08:00
|
|
|
void
|
2014-07-31 01:38:47 +08:00
|
|
|
Driver::WriteInitialCommands (bool before_file, SBStream &strm)
|
2010-06-23 09:19:29 +08:00
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
std::vector<std::pair<bool, std::string> > &command_set = before_file ? m_option_data.m_initial_commands :
|
|
|
|
m_option_data.m_after_file_commands;
|
2013-09-14 08:20:24 +08:00
|
|
|
|
2014-07-31 01:38:47 +08:00
|
|
|
for (const auto &command_pair : command_set)
|
2013-09-14 08:20:24 +08:00
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
const char *command = command_pair.second.c_str();
|
|
|
|
if (command_pair.first)
|
|
|
|
strm.Printf("command source -s %i '%s'\n", m_option_data.m_source_quietly, command);
|
|
|
|
else
|
|
|
|
strm.Printf("%s\n", command);
|
2013-09-14 08:20:24 +08:00
|
|
|
}
|
2010-06-23 09:19:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2013-10-15 23:46:40 +08:00
|
|
|
Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
|
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;
|
|
|
|
}
|
|
|
|
|
2013-04-05 04:35:24 +08:00
|
|
|
// Build the option_string argument for call to getopt_long_only.
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-13 08:22:20 +08:00
|
|
|
// This is kind of a pain, but since we make the debugger in the Driver's constructor, we can't
|
|
|
|
// know at that point whether we should read in init files yet. So we don't read them in in the
|
|
|
|
// Driver constructor, then set the flags back to "read them in" here, and then if we see the
|
|
|
|
// "-n" flag, we'll turn it off again. Finally we have to read them in by hand later in the
|
|
|
|
// main loop.
|
|
|
|
|
|
|
|
m_debugger.SkipLLDBInitFiles (false);
|
|
|
|
m_debugger.SkipAppInitFiles (false);
|
|
|
|
|
2013-04-05 04:35:24 +08:00
|
|
|
// Prepare for & make calls to getopt_long_only.
|
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;
|
2013-04-05 04:35:24 +08:00
|
|
|
val = ::getopt_long_only (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)
|
|
|
|
{
|
2012-12-04 08:32:51 +08:00
|
|
|
const int short_option = 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
|
|
|
|
2012-12-22 06:22:26 +08:00
|
|
|
case 'P':
|
|
|
|
m_option_data.m_print_python_path = true;
|
|
|
|
break;
|
|
|
|
|
2010-06-23 09:19:29 +08:00
|
|
|
case 'c':
|
2012-08-16 06:10:42 +08:00
|
|
|
{
|
|
|
|
SBFileSpec file(optarg);
|
|
|
|
if (file.Exists())
|
|
|
|
{
|
|
|
|
m_option_data.m_core_file = optarg;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
error.SetErrorStringWithFormat("file specified in --core (-c) option doesn't exist: '%s'", optarg);
|
|
|
|
}
|
2010-06-23 09:19:29 +08:00
|
|
|
break;
|
2012-08-16 06:10:42 +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
|
|
|
|
2011-09-14 07:25:31 +08:00
|
|
|
case 'x':
|
2010-10-11 09:05:37 +08:00
|
|
|
m_debugger.SkipLLDBInitFiles (true);
|
2011-08-13 08:22:20 +08:00
|
|
|
m_debugger.SkipAppInitFiles (true);
|
2010-10-11 09:05:37 +08:00
|
|
|
break;
|
|
|
|
|
2013-09-14 08:20:24 +08:00
|
|
|
case 'X':
|
2013-05-24 04:47:45 +08:00
|
|
|
m_debugger.SetUseColor (false);
|
|
|
|
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];
|
2011-08-11 06:06:24 +08:00
|
|
|
file.GetPath (path, sizeof(path));
|
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
|
|
|
|
2014-02-06 05:35:09 +08:00
|
|
|
case 'Q':
|
2013-09-14 08:20:24 +08:00
|
|
|
m_option_data.m_source_quietly = true;
|
|
|
|
break;
|
|
|
|
|
2011-09-14 07:25:31 +08:00
|
|
|
case 'n':
|
|
|
|
m_option_data.m_process_name = optarg;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'w':
|
|
|
|
m_option_data.m_wait_for = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
{
|
|
|
|
char *remainder;
|
|
|
|
m_option_data.m_process_pid = strtol (optarg, &remainder, 0);
|
|
|
|
if (remainder == optarg || *remainder != '\0')
|
|
|
|
error.SetErrorStringWithFormat ("Could not convert process PID: \"%s\" into a pid.",
|
|
|
|
optarg);
|
|
|
|
}
|
|
|
|
break;
|
2010-06-23 09:19:29 +08:00
|
|
|
case 's':
|
2013-09-14 08:20:24 +08:00
|
|
|
m_option_data.AddInitialCommand(optarg, false, true, error);
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
m_option_data.AddInitialCommand(optarg, false, false, error);
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
m_option_data.AddInitialCommand(optarg, true, true, error);
|
|
|
|
break;
|
|
|
|
case 'O':
|
|
|
|
m_option_data.AddInitialCommand(optarg, true, false, error);
|
2010-06-23 09:19:29 +08:00
|
|
|
break;
|
|
|
|
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);
|
2013-10-15 23:46:40 +08:00
|
|
|
exiting = 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());
|
2013-10-15 23:46:40 +08:00
|
|
|
exiting = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2012-12-22 06:22:26 +08:00
|
|
|
else if (m_option_data.m_print_python_path)
|
|
|
|
{
|
|
|
|
SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();
|
|
|
|
if (python_file_spec.IsValid())
|
|
|
|
{
|
|
|
|
char python_path[PATH_MAX];
|
|
|
|
size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX);
|
|
|
|
if (num_chars < PATH_MAX)
|
|
|
|
{
|
|
|
|
::fprintf (out_fh, "%s\n", python_path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
::fprintf (out_fh, "<PATH TOO LONG>\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
::fprintf (out_fh, "<COULD NOT FIND PATH>\n");
|
2013-10-15 23:46:40 +08:00
|
|
|
exiting = true;
|
2012-12-22 06:22:26 +08:00
|
|
|
}
|
2011-09-14 07:25:31 +08:00
|
|
|
else if (m_option_data.m_process_name.empty() && m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
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.
|
|
|
|
|
2013-04-05 04:35:24 +08:00
|
|
|
// Skip any options we consumed with getopt_long_only
|
2010-12-09 06:23:24 +08:00
|
|
|
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
|
|
|
}
|
2011-09-14 07:25:31 +08:00
|
|
|
else
|
|
|
|
{
|
2013-04-05 04:35:24 +08:00
|
|
|
// Skip any options we consumed with getopt_long_only
|
2011-09-14 07:25:31 +08:00
|
|
|
argc -= optind;
|
2012-07-17 11:23:13 +08:00
|
|
|
//argv += optind; // Commented out to keep static analyzer happy
|
2011-09-14 07:25:31 +08:00
|
|
|
|
|
|
|
if (argc > 0)
|
|
|
|
::fprintf (out_fh, "Warning: program arguments are ignored when attaching.\n");
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Driver::MainLoop ()
|
|
|
|
{
|
|
|
|
if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0)
|
2012-02-03 03:28:31 +08:00
|
|
|
{
|
|
|
|
g_old_stdin_termios_is_valid = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
atexit (reset_stdin_termios);
|
2012-02-03 03:28:31 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
::setbuf (stdin, NULL);
|
|
|
|
::setbuf (stdout, NULL);
|
|
|
|
|
2010-06-23 09:19:29 +08:00
|
|
|
m_debugger.SetErrorFileHandle (stderr, false);
|
|
|
|
m_debugger.SetOutputFileHandle (stdout, false);
|
2014-07-31 01:38:47 +08:00
|
|
|
m_debugger.SetInputFileHandle (stdin, false); // Don't take ownership of STDIN yet...
|
|
|
|
|
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
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-01-28 07:43:24 +08:00
|
|
|
SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2014-01-28 07:43: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-09 00:52:24 +08:00
|
|
|
{
|
2014-01-28 07:43:24 +08:00
|
|
|
result.PutError (m_debugger.GetErrorFileHandle());
|
|
|
|
result.PutOutput (m_debugger.GetOutputFileHandle());
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2014-01-28 07:43:24 +08:00
|
|
|
// Now we handle options we got from the command line
|
2014-07-31 03:26:11 +08:00
|
|
|
SBStream commands_stream;
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2014-07-31 01:38:47 +08:00
|
|
|
// First source in the commands specified to be run before the file arguments are processed.
|
|
|
|
WriteInitialCommands(true, commands_stream);
|
|
|
|
|
2014-01-28 07:43:24 +08:00
|
|
|
const size_t num_args = m_option_data.m_args.size();
|
|
|
|
if (num_args > 0)
|
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
commands_stream.Printf("target create \"%s\"", m_option_data.m_args[0].c_str());
|
|
|
|
if (!m_option_data.m_core_file.empty())
|
|
|
|
{
|
|
|
|
commands_stream.Printf(" --core \"%s\"", m_option_data.m_core_file.c_str());
|
|
|
|
}
|
|
|
|
commands_stream.Printf("\n");
|
2014-01-28 07:43:24 +08:00
|
|
|
|
|
|
|
if (num_args > 1)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
commands_stream.Printf ("settings set -- target.run-args ");
|
2014-01-28 07:43:24 +08:00
|
|
|
for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
const char *arg_cstr = m_option_data.m_args[arg_idx].c_str();
|
|
|
|
if (strchr(arg_cstr, '"') == NULL)
|
|
|
|
commands_stream.Printf(" \"%s\"", arg_cstr);
|
|
|
|
else
|
|
|
|
commands_stream.Printf(" '%s'", arg_cstr);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2014-07-31 01:38:47 +08:00
|
|
|
commands_stream.Printf("\n");
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
2014-07-31 01:38:47 +08:00
|
|
|
else if (!m_option_data.m_core_file.empty())
|
2014-01-28 07:43:24 +08:00
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
commands_stream.Printf("target create --core \"%s\"\n", m_option_data.m_core_file.c_str());
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2014-02-07 02:22:44 +08:00
|
|
|
else if (!m_option_data.m_process_name.empty())
|
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
commands_stream.Printf ("process attach --name \"%s\"", m_option_data.m_process_name.c_str());
|
|
|
|
|
|
|
|
if (m_option_data.m_wait_for)
|
|
|
|
commands_stream.Printf(" --waitfor");
|
|
|
|
|
|
|
|
commands_stream.Printf("\n");
|
|
|
|
|
2014-02-07 02:22:44 +08:00
|
|
|
}
|
|
|
|
else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid)
|
|
|
|
{
|
2014-07-31 01:38:47 +08:00
|
|
|
commands_stream.Printf ("process attach --pid %" PRIu64 "\n", m_option_data.m_process_pid);
|
2014-02-07 02:22:44 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2014-07-31 01:38:47 +08:00
|
|
|
WriteInitialCommands(false, commands_stream);
|
|
|
|
|
2014-01-28 07:43: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-09 00:52:24 +08:00
|
|
|
{
|
2014-01-28 07:43:24 +08:00
|
|
|
result.PutError(m_debugger.GetErrorFileHandle());
|
|
|
|
result.PutOutput(m_debugger.GetOutputFileHandle());
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
|
|
|
|
bool handle_events = true;
|
|
|
|
bool spawn_thread = false;
|
2014-07-31 01:38:47 +08:00
|
|
|
|
|
|
|
// Check if we have any data in the commands stream, and if so, save it to a temp file
|
|
|
|
// so we can then run the command interpreter using the file contents.
|
|
|
|
if (commands_stream.GetData() && commands_stream.GetSize())
|
|
|
|
{
|
|
|
|
char lldb_cmds_file[PATH_MAX];
|
|
|
|
SBFileSpec lldb_temp_dir_spec = SBHostOS::GetLLDBPath (lldb::ePathTypeLLDBTempSystemDir);
|
|
|
|
lldb_temp_dir_spec.SetFilename("lldb-cmds.XXXXXX");
|
|
|
|
|
|
|
|
if (lldb_temp_dir_spec.GetPath(lldb_cmds_file, sizeof(lldb_cmds_file)))
|
|
|
|
{
|
|
|
|
int fd = mkstemp(lldb_cmds_file);
|
|
|
|
if (fd == -1)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "error: can't create temporary file for LLDB commands\n");
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
FILE *file = fdopen(fd, "r+");
|
|
|
|
if (file == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "error: fdopen(%i, \"r+\") failed (errno = %i)\n", fd, errno);
|
|
|
|
exit (2);
|
|
|
|
}
|
|
|
|
// Redirect the stream to a file and it will save its temp buffer out to the file on disk
|
|
|
|
commands_stream.RedirectToFileHandle(file, true);
|
|
|
|
|
|
|
|
// Close the stream which will close the file and flush it to disk
|
|
|
|
commands_stream.Clear();
|
|
|
|
|
|
|
|
// Now re-open the file so we can use it as an input file handle for the real
|
|
|
|
// command interpreter
|
|
|
|
FILE *commands_file = ::fopen(lldb_cmds_file, "r");
|
|
|
|
if (commands_file)
|
|
|
|
{
|
|
|
|
// Hand ownership over to the debugger for "commands_file".
|
|
|
|
m_debugger.SetInputFileHandle (commands_file, true);
|
|
|
|
m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "error: fopen(\"%s\", \"r\") failed (errno = %i) when trying to open LLDB commands file\n", lldb_cmds_file, errno);
|
|
|
|
exit (3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now set the input file handle to STDIN and run the command
|
|
|
|
// interpreter again in interactive mode and let the debugger
|
|
|
|
// take ownership of stdin
|
|
|
|
m_debugger.SetInputFileHandle (stdin, true);
|
2014-01-28 07:43:24 +08:00
|
|
|
m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
|
|
|
|
|
|
|
|
reset_stdin_termios();
|
|
|
|
fclose (stdin);
|
|
|
|
|
|
|
|
SBDebugger::Destroy (m_debugger);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2013-02-23 06:56:55 +08:00
|
|
|
void
|
|
|
|
Driver::ResizeWindow (unsigned short col)
|
|
|
|
{
|
|
|
|
GetDebugger().SetTerminalWidth (col);
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
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)
|
|
|
|
{
|
2012-04-27 05:39:32 +08:00
|
|
|
if ((window_size.ws_col > 0) && g_driver != NULL)
|
2010-09-10 01:45:09 +08:00
|
|
|
{
|
2013-02-23 06:56:55 +08:00
|
|
|
g_driver->ResizeWindow (window_size.ws_col);
|
2010-09-10 01:45:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-12-01 04:23:19 +08:00
|
|
|
void
|
|
|
|
sigtstp_handler (int signo)
|
|
|
|
{
|
|
|
|
g_driver->GetDebugger().SaveInputTerminalState();
|
|
|
|
signal (signo, SIG_DFL);
|
|
|
|
kill (getpid(), signo);
|
|
|
|
signal (signo, sigtstp_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sigcont_handler (int signo)
|
|
|
|
{
|
|
|
|
g_driver->GetDebugger().RestoreInputTerminalState();
|
|
|
|
signal (signo, SIG_DFL);
|
|
|
|
kill (getpid(), signo);
|
|
|
|
signal (signo, sigcont_handler);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2014-03-03 23:39:47 +08:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
// disable buffering on windows
|
|
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
setvbuf(stdin , NULL, _IONBF, 0);
|
|
|
|
#endif
|
|
|
|
|
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);
|
2012-12-01 04:23:19 +08:00
|
|
|
signal (SIGTSTP, sigtstp_handler);
|
|
|
|
signal (SIGCONT, sigcont_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;
|
|
|
|
|
2013-10-15 23:46:40 +08:00
|
|
|
bool exiting = false;
|
|
|
|
SBError error (driver.ParseArgs (argc, argv, stdout, exiting));
|
2010-06-23 09:19:29 +08:00
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
const char *error_cstr = error.GetCString ();
|
|
|
|
if (error_cstr)
|
|
|
|
::fprintf (stderr, "error: %s\n", error_cstr);
|
|
|
|
}
|
2013-10-15 23:46:40 +08:00
|
|
|
else if (!exiting)
|
2010-06-23 09:19:29 +08:00
|
|
|
{
|
|
|
|
driver.MainLoop ();
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SBDebugger::Terminate();
|
|
|
|
return 0;
|
|
|
|
}
|