mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-02 16:56:50 +00:00
8b1125f53d
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@10702 91177308-0d34-0410-b5e6-96231b3b80d8
306 lines
10 KiB
C++
306 lines
10 KiB
C++
//===-- CLIDebugger.cpp - Command Line Interface to the Debugger ----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file was developed by the LLVM research group and is distributed under
|
|
// the University of Illinois Open Source License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains the main implementation of the Command Line Interface to
|
|
// the debugger.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CLIDebugger.h"
|
|
#include "CLICommand.h"
|
|
#include "llvm/Debugger/SourceFile.h"
|
|
#include "Support/StringExtras.h"
|
|
#include <iostream>
|
|
using namespace llvm;
|
|
|
|
/// CLIDebugger constructor - This initializes the debugger to its default
|
|
/// state, and initializes the command table.
|
|
///
|
|
CLIDebugger::CLIDebugger()
|
|
: TheProgramInfo(0), TheRuntimeInfo(0), Prompt("(llvm-db) "), ListSize(10) {
|
|
// Initialize instance variables
|
|
CurrentFile = 0;
|
|
LineListedStart = 1;
|
|
LineListedEnd = 1;
|
|
LastCurrentFrame = 0;
|
|
CurrentLanguage = 0;
|
|
|
|
CLICommand *C;
|
|
//===--------------------------------------------------------------------===//
|
|
// Program startup and shutdown options
|
|
//
|
|
addCommand("file", new BuiltinCLICommand(
|
|
"Use specified file as the program to be debugged",
|
|
"The debugger looks in the current directory and the program $PATH for the"
|
|
" specified LLVM program. It then unloads the currently loaded program and"
|
|
" loads the specified program.\n",
|
|
&CLIDebugger::fileCommand));
|
|
|
|
addCommand("create", new BuiltinCLICommand(
|
|
"Start the program, halting its execution in main",
|
|
"This command creates an instance of the current program, but stops"
|
|
"\nexecution immediately.\n",
|
|
&CLIDebugger::createCommand));
|
|
|
|
addCommand("kill", new BuiltinCLICommand(
|
|
"Kills the execution of the current program being debugged", "",
|
|
&CLIDebugger::killCommand));
|
|
|
|
addCommand("quit", new BuiltinCLICommand(
|
|
"Exit the debugger", "",
|
|
&CLIDebugger::quitCommand));
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Program execution commands
|
|
//
|
|
addCommand("run", C = new BuiltinCLICommand(
|
|
"Start the program running from the beginning", "",
|
|
&CLIDebugger::runCommand));
|
|
addCommand("r", C);
|
|
|
|
addCommand("cont", C = new BuiltinCLICommand(
|
|
"Continue program being debugged until the next stop point", "",
|
|
&CLIDebugger::contCommand));
|
|
addCommand("c", C); addCommand("fg", C);
|
|
|
|
addCommand("step", C = new BuiltinCLICommand(
|
|
"Step program until it reaches a new source line", "",
|
|
&CLIDebugger::stepCommand));
|
|
addCommand("s", C);
|
|
|
|
addCommand("next", C = new BuiltinCLICommand(
|
|
"Step program until it reaches a new source line, stepping over calls", "",
|
|
&CLIDebugger::nextCommand));
|
|
addCommand("n", C);
|
|
|
|
addCommand("finish", new BuiltinCLICommand(
|
|
"Execute until the selected stack frame returns",
|
|
"Upon return, the value returned is printed and put in the value history.\n",
|
|
&CLIDebugger::finishCommand));
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Stack frame commands
|
|
//
|
|
addCommand("backtrace", C = new BuiltinCLICommand(
|
|
"Print backtrace of all stack frames, or innermost COUNT frames",
|
|
"FIXME: describe. Takes 'n', '-n' or 'full'\n",
|
|
&CLIDebugger::backtraceCommand));
|
|
addCommand("bt", C);
|
|
|
|
addCommand("up", new BuiltinCLICommand(
|
|
"Select and print stack frame that called this one",
|
|
"An argument says how many frames up to go.\n",
|
|
&CLIDebugger::upCommand));
|
|
|
|
addCommand("down", new BuiltinCLICommand(
|
|
"Select and print stack frame called by this one",
|
|
"An argument says how many frames down go.\n",
|
|
&CLIDebugger::downCommand));
|
|
|
|
addCommand("frame", C = new BuiltinCLICommand(
|
|
"Select and print a stack frame",
|
|
"With no argument, print the selected stack frame. (See also 'info frame').\n"
|
|
"An argument specifies the frame to select.\n",
|
|
&CLIDebugger::frameCommand));
|
|
addCommand("f", C);
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Breakpoint related commands
|
|
//
|
|
addCommand("break", C = new BuiltinCLICommand(
|
|
"Set breakpoint at specified line or function",
|
|
"FIXME: describe.\n",
|
|
&CLIDebugger::breakCommand));
|
|
addCommand("b", C);
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Miscellaneous commands
|
|
//
|
|
addCommand("info", new BuiltinCLICommand(
|
|
"Generic command for showing things about the program being debugged",
|
|
"FIXME: document\n",
|
|
&CLIDebugger::infoCommand));
|
|
|
|
addCommand("list", C = new BuiltinCLICommand(
|
|
"List specified function or line",
|
|
"FIXME: document\n",
|
|
&CLIDebugger::listCommand));
|
|
addCommand("l", C);
|
|
|
|
addCommand("set", new BuiltinCLICommand(
|
|
"Change program or debugger variable",
|
|
"FIXME: document\n",
|
|
&CLIDebugger::setCommand));
|
|
|
|
addCommand("show", new BuiltinCLICommand(
|
|
"Generic command for showing things about the debugger",
|
|
"FIXME: document\n",
|
|
&CLIDebugger::showCommand));
|
|
|
|
addCommand("help", C = new BuiltinCLICommand(
|
|
"Prints information about available commands", "",
|
|
&CLIDebugger::helpCommand));
|
|
addCommand("h", C);
|
|
}
|
|
|
|
|
|
/// addCommand - Add a command to the CommandTable, potentially displacing a
|
|
/// preexisting command.
|
|
void CLIDebugger::addCommand(const std::string &Option, CLICommand *Cmd) {
|
|
assert(Cmd && "Cannot set a null command!");
|
|
CLICommand *&CS = CommandTable[Option];
|
|
if (CS == Cmd) return; // noop
|
|
|
|
// If we already have a command, decrement the command's reference count.
|
|
if (CS) {
|
|
CS->removeOptionName(Option);
|
|
CS->dropRef();
|
|
}
|
|
CS = Cmd;
|
|
|
|
// Remember that we are using this command.
|
|
Cmd->addRef();
|
|
Cmd->addOptionName(Option);
|
|
}
|
|
|
|
static bool isValidPrefix(const std::string &Prefix, const std::string &Option){
|
|
return Prefix.size() <= Option.size() &&
|
|
Prefix == std::string(Option.begin(), Option.begin()+Prefix.size());
|
|
}
|
|
|
|
/// getCommand - This looks up the specified command using a fuzzy match.
|
|
/// If the string exactly matches a command or is an unambiguous prefix of a
|
|
/// command, it returns the command. Otherwise it throws an exception
|
|
/// indicating the possible ambiguous choices.
|
|
CLICommand *CLIDebugger::getCommand(const std::string &Command) {
|
|
|
|
// Look up the command in the table.
|
|
std::map<std::string, CLICommand*>::iterator CI =
|
|
CommandTable.lower_bound(Command);
|
|
|
|
if (Command == "") {
|
|
throw "Null command should not get here!";
|
|
} else if (CI == CommandTable.end() ||
|
|
!isValidPrefix(Command, CI->first)) {
|
|
// If this command has no relation to anything in the command table,
|
|
// print the error message.
|
|
throw "Unknown command: '" + Command +
|
|
"'. Use 'help' for list of commands.";
|
|
} else if (CI->first == Command) {
|
|
// We have an exact match on the command
|
|
return CI->second;
|
|
} else {
|
|
// Otherwise, we have a prefix match. Check to see if this is
|
|
// unambiguous, and if so, run it.
|
|
std::map<std::string, CLICommand*>::iterator CI2 = CI;
|
|
|
|
// If the next command is a valid completion of this one, we are
|
|
// ambiguous.
|
|
if (++CI2 != CommandTable.end() && isValidPrefix(Command, CI2->first)) {
|
|
std::string ErrorMsg =
|
|
"Ambiguous command '" + Command + "'. Options: " + CI->first;
|
|
for (++CI; CI != CommandTable.end() &&
|
|
isValidPrefix(Command, CI->first); ++CI)
|
|
ErrorMsg += ", " + CI->first;
|
|
throw ErrorMsg;
|
|
} else {
|
|
// It's an unambiguous prefix of a command, use it.
|
|
return CI->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// run - Start the debugger, returning when the user exits the debugger. This
|
|
/// starts the main event loop of the CLI debugger.
|
|
///
|
|
int CLIDebugger::run() {
|
|
std::string Command;
|
|
std::cout << Prompt;
|
|
|
|
// Keep track of the last command issued, so that we can reissue it if the
|
|
// user hits enter as the command.
|
|
CLICommand *LastCommand = 0;
|
|
std::string LastArgs;
|
|
|
|
// Continue reading commands until the end of file.
|
|
while (getline(std::cin, Command)) {
|
|
std::string Arguments = Command;
|
|
|
|
// Split off the command from the arguments to the command.
|
|
Command = getToken(Arguments, " \t\n\v\f\r\\/;.*&");
|
|
|
|
try {
|
|
CLICommand *CurCommand;
|
|
|
|
if (Command == "") {
|
|
CurCommand = LastCommand;
|
|
Arguments = LastArgs;
|
|
} else {
|
|
CurCommand = getCommand(Command);
|
|
}
|
|
|
|
// Save the command we are running in case the user wants us to repeat it
|
|
// next time.
|
|
LastCommand = CurCommand;
|
|
LastArgs = Arguments;
|
|
|
|
// Finally, execute the command.
|
|
if (CurCommand)
|
|
CurCommand->runCommand(*this, Arguments);
|
|
|
|
} catch (int RetVal) {
|
|
// The quit command exits the command loop by throwing an integer return
|
|
// code.
|
|
return RetVal;
|
|
} catch (const std::string &Error) {
|
|
std::cout << "Error: " << Error << "\n";
|
|
} catch (const char *Error) {
|
|
std::cout << "Error: " << Error << "\n";
|
|
} catch (const NonErrorException &E) {
|
|
std::cout << E.getMessage() << "\n";
|
|
} catch (...) {
|
|
std::cout << "ERROR: Debugger caught unexpected exception!\n";
|
|
// Attempt to continue.
|
|
}
|
|
|
|
// Write the prompt to get the next bit of user input
|
|
std::cout << Prompt;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/// askYesNo - Ask the user a question, and demand a yes/no response. If
|
|
/// the user says yes, return true.
|
|
///
|
|
bool CLIDebugger::askYesNo(const std::string &Message) const {
|
|
std::string Answer;
|
|
std::cout << Message << " (y or n) " << std::flush;
|
|
while (getline(std::cin, Answer)) {
|
|
std::string Val = getToken(Answer);
|
|
if (getToken(Answer).empty()) {
|
|
if (Val == "yes" || Val == "y" || Val == "YES" || Val == "Y" ||
|
|
Val == "Yes")
|
|
return true;
|
|
if (Val == "no" || Val == "n" || Val == "NO" || Val == "N" ||
|
|
Val == "No")
|
|
return false;
|
|
}
|
|
|
|
std::cout << "Please answer y or n.\n" << Message << " (y or n) "
|
|
<< std::flush;
|
|
}
|
|
|
|
// Ran out of input?
|
|
return false;
|
|
}
|