📄 quincydebug.cpp
字号:
// --------- QuincyDebug.cpp
#include "stdafx.h"
#include "Quincy.h"
#include "textview.h"
#include "debugger.h"
#include "WatchDialog.h"
#include "mainfrm.h"
Debugger* Debugger::pDebugger;
Debugger::Debugger()
{
m_nDrive = _getdrive();
m_pOldPath = _getcwd(0, MAX_PATH);
std::string exe = theApp.GdbPath();
exe += "gdb.exe";
//#ifdef CYGWIN_GDB
// exe += "--args ";
//#endif
// exe += " -q ";
ConvertSlashes(exe);
CString strexe = theApp.Enquote(exe.c_str()) + " ";
gdb = new ConsoleApp(strexe, &Notify, &Collect);
pDebugger = this;
atprompt = false;
watching = false;
currentlineno = 0;
// previouslineno = 0;
infosent = false;
ExamineWnd = 0;
Expressions = 0;
watchct = 0;
watchiter = 0;
}
Debugger::~Debugger()
{
_chdrive(m_nDrive);
_chdir(m_pOldPath);
delete m_pOldPath;
delete gdb;
pDebugger = 0;
}
void Debugger::LoadDebugger(std::string cmdline)
{
try {
ConvertSlashes(cmdline);
CString strCmdLine(cmdline.c_str());
strRedirect.Empty();
CString strRedirectedStdin;
int index = strCmdLine.Find('<');
if (index != -1 && index < strCmdLine.GetLength()-1) {
theApp.ParseFileSpec(strRedirectedStdin, strCmdLine, index);
strRedirect = " < ";
strRedirect += strRedirectedStdin;
}
CString strRedirectedStdout;
index = strCmdLine.Find('>');
if (index != -1 && index < strCmdLine.GetLength()-1) {
bool append = false;
if (strCmdLine[index+1] == '>') {
index++;
append = true;
}
theApp.ParseFileSpec(strRedirectedStdout, strCmdLine, index);
strRedirect += append ? " >> " : " > ";
strRedirect += strRedirectedStdout;
}
strCmdLine = theApp.Enquote(strCmdLine);
gdb->Run(strCmdLine.GetBuffer(0));
// gdb->Run(cmdline.c_str());
theApp.ClearGdbConsole();
// theApp.DisplayGdbText(strCmdLine.GetBuffer(0));
theApp.DisplayGdbText("--- gdb started ---");
}
catch(...) {
AfxMessageBox("\nCannot execute GDB", MB_ICONSTOP);
}
}
void Debugger::ConvertSlashes(std::string& str)
{
for (int i = 0; i < str.size(); i++)
if (str[i] == '\\')
str[i] = '/';
}
void Debugger::SetTempBreakpoint(const Breakpoint& bp)
{
char lineno[20];
sprintf(lineno, "%d", bp.m_nLineNo);
std::string cmd("tbreak ");
cmd += theApp.GetFileName(bp.m_strFile);
cmd += ':';
cmd += lineno;
SendCommand(const_cast<char*>(cmd.c_str()));
}
void Debugger::StartProgram()
{
currentsrc = "";
currentlineno = 0;
atprompt = false;
Breakpoint bp = theApp.SteptoBreakpoint();
if (!bp.m_strFile.IsEmpty())
SetTempBreakpoint(bp);
if (theApp.IsConsoleApplication())
PostCommand("set new-console");
CString cmd("run");
cmd += strRedirect;
PostCommand(cmd.GetBuffer(0));
}
void Debugger::StepIntoProgram()
{
UndoWatchDisplays();
if (theApp.IsGUITarget())
PostCommand("tbreak WinMain");
else
PostCommand("tbreak main");
StartProgram();
}
void Debugger::SendCommand(char* cmd)
{
if (atprompt) {
strncpy(lastcommand,cmd,99);
gdb->WriteConsole(cmd);
theApp.DisplayGdbText(cmd);
atprompt = false;
}
else
PostCommand(cmd);
}
void Debugger::PostCommand(const char* cmd)
{
cmds.push(cmd);
}
void Debugger::Notify()
{
ASSERT(pDebugger != 0);
pDebugger->NotifyTermination();
}
void Debugger::NotifyTermination()
{
if (atprompt) {
theApp.DisplayGdbText(" ");
atprompt = false;
}
theApp.DisplayGdbText("--- gdb terminated ---");
currentsrc = "";
currentlineno = 0;
theApp.InvalidateAllViews();
UndoWatchDisplays();
theApp.StopDebugger(); // this function in CQuincyApp destroys the debugger object
// so don't do anything after this call except in the dtor
}
void Debugger::UndoWatchDisplays()
{
for (watchiter = 0; watchiter < watchct; watchiter++)
PostResponse("??");
watchiter = 0;
}
// --- called from the console application thread while compiling is going on
void Debugger::Collect(DWORD bufct)
{
ASSERT(pDebugger != 0);
pDebugger->CollectGdbMessages(bufct);
}
inline bool Debugger::IsText(const char* buf, const char* txt) const
{
return strnicmp(buf, txt, strlen(txt)-1) == 0;
}
void Debugger::SetWatchExpressions(CString* exp, int ct)
{
Expressions = exp;
watchct = ct;
SendCommand("");
}
void Debugger::PostResponse(const char* res)
{
CWatchDialog* watch = theApp.GetWatchDialog();
if (watch) {
CListBox* lb = watch->GetWatchListbox();
if (lb) {
std::string cmd(Expressions[watchiter]);
int sel = lb->FindString(-1, cmd.c_str());
if (sel != LB_ERR) {
lb->DeleteString(sel);
cmd += res;
lb->InsertString(sel, const_cast<char*>(cmd.c_str()));
}
}
}
}
void Debugger::IterateWatches()
{
if (watchct) {
watchiter++;
if (watchiter == watchct) {
watchiter = 0;
watching = false;
}
}
}
void Debugger::DisplayData(const char* cp)
{
if (ExamineWnd != 0) {
// examine response
if (::IsWindow(ExamineWnd->m_hWnd))
ExamineWnd->SetWindowText(cp);
ExamineWnd = 0;
}
else {
// watch response
if (Expressions != 0)
PostResponse(cp);
IterateWatches();
}
}
void Debugger::CollectGdbMessages(DWORD bufct)
{
char *buf = new char[bufct+1];
ASSERT(gdb != 0);
if (gdb->ReadConsole(buf, bufct) != 0) {
if (!IsText(buf, "Note:")) {
if (*buf != ' ' || (steppingsrc.size() == 0 && strstr(buf, " at ")))
theApp.DisplayGdbText(buf);
if (IsText(buf, "Stack level")) {
// --- keep track of frame to support Step out of main function
char *cp = strstr(buf, " at ");
if (cp) {
cp += 4;
frame = cp;
if (mainframe.length() == 0)
mainframe = frame;
}
// initiate collection of watches after info f data come in
watching = Expressions != 0;
}
// ---- parse the message from gdb to see what to do with it besides display it
else if (IsText(buf, "(gdb)")) {
// ---- this is the gdb prompt
atprompt = true;
if (strlen(buf) > 6) // old gdb behavior
// ---- error message from an Examine or Watch "print" command
// appended to (gdb) prompt
DisplayData("??");
else
// --- next display will be a command and will be appended
// to the prompt on the display in the gdb console window
theApp.AppendNextText();
if (cmds.size() != 0) {
// ---- command(s) posted to be sent at the prompt
SendCommand(const_cast<char*>(cmds.front().c_str()));
cmds.pop();
}
else {
theApp.SetStepping();
theApp.BringToTop();
if (!watching) {
if (!infosent) {
// ---- send the info f command to get the current stack level
SendCommand("info f");
infosent = true;
}
else
infosent = false; // prevents (gdb) after info f output
// is received from repeating info f
}
else if (watchiter < watchct) {
// get the data for the next watch in the watch window
std::string cmd("print ");
cmd += Expressions[watchiter].Left(Expressions[watchiter].GetLength()-4);
ASSERT(gdb);
gdb->WriteConsole(cmd.c_str());
theApp.DisplayGdbText(const_cast<char*>(cmd.c_str()));
}
}
}
else if (IsText(buf, "No symbol"))
DisplayData("??");
else if (*buf == '$') {
char *cp = strstr(buf, " = ");
if (cp)
// --- response to "print" command from Examine or Watch
DisplayData(cp+3);
}
else if (IsText(buf, "Program exited")) {
// ---- the program being debugged has exited on its own
if (strstr(buf, " with code 03."))
AfxMessageBox("assert failed", MB_ICONEXCLAMATION);
SendCommand("q");
}
else if (IsText(buf, "The program is running")) {
// --- user must be stopping a running program
atprompt = true;
if (AfxMessageBox("Terminate running program?", MB_YESNO | MB_ICONQUESTION) == IDYES) {
SendCommand("y");
frame = mainframe = "";
}
else
SendCommand("n");
}
else if (strstr(buf, " in end ()") || strstr(buf, " in __mingw_CRTStartup ()"))
// ---- this comes in after stepping out of main
PostCommand("c");
else if (IsText(buf, "0x")) {
if (buf[10] == ' ') {
if (isdigit(buf[11]))
// --- one of those oddball step responses with the hex address
// ahead of the line of code
steppinglineno = atoi(buf+11);
else {
goto testat;
}
}
}
else if (isdigit(*buf))
// ---- single step. The numerical value is the line number
steppinglineno = atoi(buf);
else {
testat:
char *cp = strstr(buf, " at ");
if (cp != 0) {
if (!IsText(cp+4, "0x") && !IsText(buf, "run till exit")) {
// breakpoint or stepping into a new file
cp += 4;
std::string src;
int lineno = 0;
if (IsText(cp, "//") && *(cp+3) == '/') {
// non-dos file specification e.g. //c/foobar
// convert to c:/foobar...
cp++;
*cp = *(cp+1);
*(cp+1) = ':';
}
while (*cp && !(*cp == ':' && isdigit(*(cp+1)))) {
// parse out file spec, ended with :<line#>
src += (*cp == '/' ? '\\' : *cp);
cp++;
}
if (*cp && *(cp+1))
// parse out line number
lineno = atoi(cp+1);
if (lineno == steppinglineno && src == steppingsrc)
// step or continue if this is a new file/lineno since last step or continue
SendCommand(lastcommand);
else {
// remember this step's file/lineno
// --- check for valid source code file extension, step out if not
int ndx = src.rfind('.');
if (ndx != -1) {
std::string ext = src.substr(ndx, src.length() - ndx);
if (ext != ".c" && ext != ".cpp" && ext != ".h")
StepOut();
else {
steppingsrc = src;
steppinglineno = lineno;
}
}
else
StepOut();
}
}
}
}
}
}
delete [] buf;
}
int Debugger::CurrentLineNo() const
{
return currentlineno;
}
const CString* Debugger::CurrentSrcFile() const
{
static CString str;
str = currentsrc.c_str();
return &str;
}
void Debugger::ShowPCCaret(bool bOnOff)
{
if (currentlineno != 0) {
CTextView* pView = theApp.GetTextView(currentsrc.c_str());
if (pView != 0)
if (bOnOff)
pView->DrawMargins();
else
pView->PadMargin();
}
}
void Debugger::SelectSteppingSourceLine()
{
ShowPCCaret(false);
if (theApp.SelectFileAndLine(steppingsrc.c_str(), steppinglineno)) {
currentsrc = steppingsrc;
currentlineno = steppinglineno;
}
ShowPCCaret(true);
}
void Debugger::Stop()
{
if (atprompt)
SendCommand("q");
else {
gdb->Stop();
atprompt = false;
frame = mainframe = "";
}
}
bool Debugger::StepTo(const CString& strFile, int nLineNo)
{
if (atprompt) {
SetTempBreakpoint(Breakpoint(strFile, nLineNo));
SendCommand("c");
return true;
}
return false;
}
bool Debugger::ExecuteRunningProgram()
{
if (atprompt) {
SendCommand("c");
atprompt = false;
return true;
}
return false;
}
// ------- the step command
bool Debugger::StepProgram()
{
if (atprompt) {
SendCommand("step");
atprompt = false;
return true;
}
return false;
}
// ----- the step over command
bool Debugger::StepOver()
{
if (atprompt) {
SendCommand("next");
atprompt = false;
return true;
}
return false;
}
// ----- the step out command
void Debugger::StepOut()
{
if (atprompt) {
if (AtStackLevelZero())
SendCommand("c");
}
else {
SendCommand("finish");
atprompt = false;
PostCommand("step");
}
}
void Debugger::SetBreakpoint(Breakpoint& bp)
{
char lineno[20];
sprintf(lineno, "%d", bp.m_nLineNo);
std::string cmd("break ");
cmd += theApp.GetFileName(bp.m_strFile);
cmd += ':';
cmd += lineno;
SendCommand(const_cast<char*>(cmd.c_str()));
}
void Debugger::ClearBreakpoint(Breakpoint& bp)
{
char lineno[20];
sprintf(lineno, "%d", bp.m_nLineNo);
std::string cmd("clear ");
cmd += theApp.GetFileName(bp.m_strFile);
cmd += ':';
cmd += lineno;
if (!atprompt)
PostCommand(const_cast<char*>(cmd.c_str()));
else
SendCommand(const_cast<char*>(cmd.c_str()));
}
void Debugger::GetVariableValue(const CString& strVarName, CWnd* wnd)
{
ExamineWnd = wnd;
std::string cmd("print ");
cmd += strVarName;
SendCommand(const_cast<char*>(cmd.c_str()));
}
void Debugger::SetVariableValue(const CString& strVarName, const CString& strVarValue)
{
std::string cmd("set ");
cmd += strVarName;
cmd += "=";
cmd += strVarValue;
SendCommand(const_cast<char*>(cmd.c_str()));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -