//===-- Commands.cpp - Implement various commands for the CLI -------------===//
-//
+//
// 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 is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
//===----------------------------------------------------------------------===//
-//
+//
// This file implements many builtin user commands.
//
//===----------------------------------------------------------------------===//
#include "llvm/Debugger/SourceLanguage.h"
#include "llvm/Debugger/SourceFile.h"
#include "llvm/Debugger/InferiorProcess.h"
-#include "Support/FileUtilities.h"
-#include "Support/StringExtras.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/ADT/StringExtras.h"
#include <iostream>
+#include <cstdlib>
using namespace llvm;
/// getCurrentLanguage - Return the current source language that the user is
eliminateRunInfo();
// If the program has been modified, reload it!
- std::string Program = Dbg.getProgramPath();
- if (TheProgramInfo->getProgramTimeStamp() != getFileTimestamp(Program)) {
+ sys::PathWithStatus Program(Dbg.getProgramPath());
+ std::string Err;
+ const sys::FileStatus *Status = Program.getFileStatus(false, &Err);
+ if (!Status)
+ throw Err;
+ if (TheProgramInfo->getProgramTimeStamp() != Status->getTimestamp()) {
std::cout << "'" << Program << "' has changed; re-reading program.\n";
// Unload an existing program. This kills the program if necessary.
TheProgramInfo = 0;
CurrentFile = 0;
- Dbg.loadProgram(Program);
+ Dbg.loadProgram(Program.toString());
TheProgramInfo = new ProgramInfo(Dbg.getProgram());
}
std::cout << " ->";
}
- std::cout << "\t" << std::string(LineStart, LineEnd) << "\n";
+ std::cout << "\t" << std::string(LineStart, LineEnd) << "\n";
return false;
}
std::cout << getProgramInfo().getFunction(FuncDesc).getSymbolicName();
else
std::cout << "<unknown function>";
-
+
CurrentFile = &FileDesc->getSourceText();
-
+
std::cout << " at " << CurrentFile->getFilename() << ":" << LineNo;
- if (ColNo) std::cout << ":" << ColNo << "\n";
+ if (ColNo) std::cout << ":" << ColNo;
+ std::cout << "\n";
}
if (printSourceLine(LineNo))
std::string Tok = getToken(Val);
if (Tok.empty() || (isOnlyOption && !getToken(Val).empty()))
throw std::string(Msg) + " expects an unsigned integer argument.";
-
+
char *EndPtr;
unsigned Result = strtoul(Tok.c_str(), &EndPtr, 0);
if (EndPtr != Tok.c_str()+Tok.size())
/// getOptionalUnsignedIntegerOption - This method is just like
/// getUnsignedIntegerOption, but if the argument value is not specified, a
/// default is returned instead of causing an error.
-static unsigned
+static unsigned
getOptionalUnsignedIntegerOption(const char *Msg, unsigned Default,
std::string &Val, bool isOnlyOption = true) {
// Check to see if the value was specified...
// And parse normally.
return getUnsignedIntegerOption(Msg, Val, isOnlyOption);
}
-
+
+
+/// parseProgramOptions - This method parses the Options string and loads it
+/// as options to be passed to the program. This is used by the run command
+/// and by 'set args'.
+void CLIDebugger::parseProgramOptions(std::string &Options) {
+ // FIXME: tokenizing by whitespace is clearly incorrect. Instead we should
+ // honor quotes and other things that a shell would. Also in the future we
+ // should support redirection of standard IO.
+
+ std::vector<std::string> Arguments;
+ for (std::string A = getToken(Options); !A.empty(); A = getToken(Options))
+ Arguments.push_back(A);
+ Dbg.setProgramArguments(Arguments.begin(), Arguments.end());
+}
+
//===----------------------------------------------------------------------===//
// Program startup and shutdown options
/// file command - If the user specifies an option, search the PATH for the
-/// specified program/bytecode file and load it. If the user does not specify
+/// specified program/bitcode file and load it. If the user does not specify
/// an option, unload the current program.
void CLIDebugger::fileCommand(std::string &Options) {
std::string Prog = getToken(Options);
assert(Dbg.isProgramLoaded() &&
"loadProgram succeeded, but not program loaded!");
TheProgramInfo = new ProgramInfo(Dbg.getProgram());
- std::cout << "success loading '" << Dbg.getProgramPath() << "'!\n";
+ std::cout << "successfully loaded '" << Dbg.getProgramPath() << "'!\n";
}
}
//===----------------------------------------------------------------------===//
void CLIDebugger::runCommand(std::string &Options) {
- if (!getToken(Options).empty()) throw "run arguments not supported yet.";
if (!Dbg.isProgramLoaded()) throw "No program loaded.";
if (Dbg.isProgramRunning() &&
!askYesNo("The program is already running. Restart from the beginning?"))
return;
+ // Parse all of the options to the run command, which specify program
+ // arguments to run with.
+ parseProgramOptions(Options);
+
eliminateRunInfo();
// Start the program running.
unsigned CurFrame = RI.getCurrentFrameIdx();
// Check to see if we can go up the specified number of frames.
- if (CurFrame < Num)
+ if (CurFrame < Num) {
if (Num == 1)
throw "Bottom (i.e., innermost) frame selected; you cannot go down.";
else
throw "Cannot go down " + utostr(Num) + " frames!";
+ }
RI.setCurrentFrameIdx(CurFrame-Num);
printProgramLocation();
//===----------------------------------------------------------------------===//
void CLIDebugger::breakCommand(std::string &Options) {
+ // Figure out where the user wants a breakpoint.
+ const SourceFile *File;
+ unsigned LineNo;
+
+ // Check to see if the user specified a line specifier.
+ std::string Option = getToken(Options); // strip whitespace
+ if (!Option.empty()) {
+ Options = Option + Options; // reconstruct string
+
+ // Parse the line specifier.
+ parseLineSpec(Options, File, LineNo);
+ } else {
+ // Build a line specifier for the current stack frame.
+ throw "FIXME: breaking at the current location is not implemented yet!";
+ }
+
+ if (!File) File = CurrentFile;
+ if (File == 0)
+ throw "Unknown file to place breakpoint!";
+
+ std::cerr << "Break: " << File->getFilename() << ":" << LineNo << "\n";
+
throw "breakpoints not implemented yet!";
}
void CLIDebugger::infoCommand(std::string &Options) {
std::string What = getToken(Options);
- if (What.empty() || !getToken(Options).empty())
- throw "info command expects exactly one argument.";
+ if (What.empty() || !getToken(Options).empty()){
+ std::string infoStr("info");
+ helpCommand(infoStr);
+ return;
+ }
if (What == "frame") {
} else if (What == "functions") {
<< SF.getLanguage().getSourceLanguageName() << "\n";
} else if (What == "sources") {
- const std::map<const GlobalVariable*, SourceFileInfo*> &SourceFiles =
+ const std::map<const GlobalVariable*, SourceFileInfo*> &SourceFiles =
getProgramInfo().getSourceFiles();
std::cout << "Source files for the program:\n";
for (std::map<const GlobalVariable*, SourceFileInfo*>::const_iterator I =
std::string Name = getToken(FirstPart);
if (!getToken(FirstPart).empty())
throw "Extra junk in line specifier after '" + Name + "'.";
- SourceFunctionInfo *SFI =
+ SourceFunctionInfo *SFI =
getCurrentLanguage().lookupFunction(Name, getProgramInfo(),
TheRuntimeInfo);
if (SFI == 0)
// Handle "list foo," correctly, by returning " " as the second token
Options += " ";
-
+
std::string FirstLineSpec = getToken(Options, ",");
std::string SecondLineSpec = getToken(Options, ",");
if (!getToken(Options, ",").empty())
assert(Tok == "-");
StartLine = LineListedStart-ListSize;
EndLine = LineListedStart;
- if ((int)StartLine < 0) StartLine = 1;
+ if ((int)StartLine <= 0) StartLine = 1;
}
} else {
// Must be a normal line specifier.
// ListSize lines centered at the specified line.
if (File != 0) CurrentFile = File;
StartLine = LineNo - (ListSize+1)/2;
- if ((int)StartLine < 0) StartLine = 1;
+ if ((int)StartLine <= 0) StartLine = 1;
EndLine = StartLine + ListSize;
}
} else {
- // Parse two line specifiers...
+ // Parse two line specifiers...
const SourceFile *StartFile, *EndFile;
unsigned StartLineNo, EndLineNo;
parseLineSpec(FirstLineSpec, StartFile, StartLineNo);
if (What.empty())
throw "set command expects at least two arguments.";
- if (What == "language") {
+ if (What == "args") {
+ parseProgramOptions(Options);
+ } else if (What == "language") {
std::string Lang = getToken(Options);
if (!getToken(Options).empty())
throw "set language expects one argument at most.";
if (What.empty() || !getToken(Options).empty())
throw "show command expects one argument.";
- if (What == "language") {
+ if (What == "args") {
+ std::cout << "Argument list to give program when started is \"";
+ // FIXME: This doesn't print stuff correctly if the arguments have spaces in
+ // them, but currently the only way to get that is to use the --args command
+ // line argument. This should really handle escaping all hard characters as
+ // needed.
+ for (unsigned i = 0, e = Dbg.getNumProgramArguments(); i != e; ++i)
+ std::cout << (i ? " " : "") << Dbg.getProgramArgument(i);
+ std::cout << "\"\n";
+
+ } else if (What == "language") {
std::cout << "The current source language is '";
if (CurrentLanguage)
std::cout << CurrentLanguage->getSourceLanguageName();