📄 debuggergdb.cpp
字号:
if (!actualCompiler)
{
wxString msg;
msg.Printf(_("This %s is configured to use an invalid debugger.\nThe operation failed..."), target ? _("target") : _("project"));
wxMessageBox(msg, _("Error"), wxICON_ERROR);
return 9;
}
if (actualCompiler->GetPrograms().DBG.IsEmpty() ||
!wxFileExists(actualCompiler->GetMasterPath() + wxFileName::GetPathSeparator() + _T("bin") + wxFileName::GetPathSeparator() + actualCompiler->GetPrograms().DBG))
{
wxMessageBox(_("The debugger executable is not set.\n"
"To set it, go to \"Settings/Configure plugins/Compiler\", switch to the \"Programs\" tab\n"
"and select the debugger program."), _("Error"), wxICON_ERROR);
msgMan->Log(m_PageIndex, _("Aborted"));
return 4;
}
// make sure it is compiled
PluginsArray plugins = Manager::Get()->GetPluginManager()->GetCompilerOffers();
if (plugins.GetCount())
m_pCompiler = (cbCompilerPlugin*)plugins[0];
if (m_pCompiler)
{
msgMan->AppendLog(m_PageIndex, _("Compiling: "));
// is the compiler already running?
if (m_pCompiler->IsRunning())
{
msgMan->Log(m_PageIndex, _("compiler in use..."));
msgMan->Log(m_PageIndex, _("Aborting debugging session"));
return -1;
}
m_pCompiler->Compile(target);
while (m_pCompiler->IsRunning())
wxYield();
msgMan->SwitchTo(m_PageIndex);
if (m_pCompiler->GetExitCode() != 0)
{
msgMan->Log(m_PageIndex, _("failed"));
msgMan->Log(m_PageIndex, _("Aborting debugging session"));
return -1;
}
msgMan->Log(m_PageIndex, _("done"));
}
wxString cmdexe;
cmdexe = actualCompiler->GetPrograms().DBG;
cmdexe.Trim();
cmdexe.Trim(true);
if(cmdexe.IsEmpty())
{
msgMan->AppendLog(m_PageIndex,_("ERROR: You need to specify a debugger program in the compiler's settings."));
#ifdef __WXMSW__
msgMan->Log(m_PageIndex,_("\n(For MINGW compilers, it's 'gdb.exe' (without the quotes))"));
#else
msgMan->Log(m_PageIndex,_("\n(For GCC compilers, it's 'gdb' (without the quotes))"));
#endif
return -1;
}
wxString cmd;
wxString sep = wxFileName::GetPathSeparator();
cmd << actualCompiler->GetMasterPath() << sep << _T("bin") << sep << cmdexe << _T(" -nw -annotate=2 -silent");
wxLogNull ln; // we perform our own error handling and logging
m_pProcess = new PipedProcess((void**)&m_pProcess, this, idGDBProcess, true, project->GetBasePath());
msgMan->AppendLog(m_PageIndex, _("Starting debugger: "));
// msgMan->AppendLog(m_PageIndex, _(cmd));
m_Pid = wxExecute(cmd, wxEXEC_ASYNC, m_pProcess);
// m_Pid = m_pProcess->Launch(cmd);
if (!m_Pid)
{
delete m_pProcess;
m_pProcess = 0;
msgMan->Log(m_PageIndex, _("failed"));
return -1;
}
else if (!m_pProcess->GetOutputStream())
{
delete m_pProcess;
m_pProcess = 0;
msgMan->Log(m_PageIndex, _("failed (to get debugger's stdin)"));
return -2;
}
else if (!m_pProcess->GetInputStream())
{
delete m_pProcess;
m_pProcess = 0;
msgMan->Log(m_PageIndex, _("failed (to get debugger's stdout)"));
return -2;
}
else if (!m_pProcess->GetErrorStream())
{
delete m_pProcess;
m_pProcess = 0;
msgMan->Log(m_PageIndex, _("failed (to get debugger's stderr)"));
return -2;
}
else
msgMan->Log(m_PageIndex, _("done"));
wxString out;
m_TimerPollDebugger.Start(100);
if (ConfigManager::Get()->Read(_T("debugger_gdb/add_other_search_dirs"), 0L))
{
// add as include dirs all open project base dirs
ProjectsArray* projects = prjMan->GetProjects();
for (unsigned int i = 0; i < projects->GetCount(); ++i)
{
cbProject* it = projects->Item(i);
// skip if it's THE project (already added)
if (it == project)
continue;
AddSourceDir(it->GetBasePath());
}
}
// lastly, add THE project as source dir
AddSourceDir(project->GetBasePath());
cmd.Clear();
switch (target->GetTargetType())
{
case ttExecutable:
case ttConsoleOnly:
// "-async" option is not really supported, at least under Win32, as far as I know
out = UnixFilename(target->GetOutputFilename());
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
msgMan->Log(m_PageIndex, _("Adding file: %s"), out.c_str());
ConvertToGDBDirectory(out);
cmd << _T("file ") << out;
// dll debugging steps:
// gdb <hostapp>
// (gdb) add-symbol-file <dllname> (and any other dlls the same way)
SendCommand(cmd);
break;
case ttStaticLib:
case ttDynamicLib:
// check for hostapp
if (target->GetHostApplication().IsEmpty())
{
wxMessageBox(_("You must select a host application to \"run\" a library..."));
CmdStop();
return 4;
}
out = UnixFilename(target->GetHostApplication());
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
msgMan->Log(m_PageIndex, _("Adding file: %s"), out.c_str());
ConvertToGDBDirectory(out);
cmd << _T("file ") << out;
SendCommand(cmd);
if (target->GetTargetType() == ttDynamicLib)
{
wxString symbols;
out = UnixFilename(target->GetOutputFilename());
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
msgMan->Log(m_PageIndex, _("Adding symbol file: %s"), out.c_str());
ConvertToGDBDirectory(out);
symbols << _T("add-symbol-file ") << out;
SendCommand(symbols);
}
break;
default: break;
}
if (!target->GetExecutionParameters().IsEmpty())
SendCommand(wxString(_T("set args ")) + target->GetExecutionParameters());
// switch to output dir
// wxFileName dir(target->GetOutputFilename());
// wxString path = UnixFilename(dir.GetPath(wxPATH_GET_VOLUME));
wxString path = UnixFilename(target->GetWorkingDir());
if (!path.IsEmpty())
{
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(path); // apply env vars
cmd.Clear();
ConvertToGDBDirectory(path);
if (path != _T(".")) // avoid silly message "changing to ."
{
msgMan->Log(m_PageIndex, _("Changing directory to: %s"), path.c_str());
cmd << _T("cd ") << path;
SendCommand(cmd);
}
}
SetBreakpoints();
if (!m_Tbreak.IsEmpty())
{
SendCommand(m_Tbreak);
m_Tbreak.Clear();
}
// send built-in init commands
SendCommand(_T("set confirm off"));
#ifndef __WXMSW__
SendCommand(_T("set disassembly-flavor att"));
#else
if (target->GetTargetType() == ttConsoleOnly)
SendCommand(_T("set new-console on"));
SendCommand(_T("set disassembly-flavor intel"));
#endif
// pass user init-commands
wxString init = ConfigManager::Get()->Read(_T("debugger_gdb/init_commands"), _T(""));
wxArrayString initCmds = GetArrayFromString(init, _T('\n'));
for (unsigned int i = 0; i < initCmds.GetCount(); ++i)
{
SendCommand(initCmds[i]);
}
// finally, run the process
if (m_BreakOnEntry)
{
m_BreakOnEntry = false;
SendCommand(_T("start"));
}
else
SendCommand(_T("run"));
return 0;
}
void DebuggerGDB::StripQuotes(wxString& str)
{
if (str.GetChar(0) == _T('\"') && str.GetChar(str.Length() - 1) == _T('\"'))
str = str.Mid(1, str.Length() - 2);
}
void DebuggerGDB::AddSourceDir(const wxString& dir)
{
if (dir.IsEmpty())
return;
wxString filename = dir;
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(filename); // apply env vars
Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Adding source dir: %s"), filename.c_str());
ConvertToGDBDirectory(filename, _T(""), false);
SendCommand(_T("directory ") + filename);
}
void DebuggerGDB::ConvertToGDBFriendly(wxString& str)
{
if (str.IsEmpty())
return;
str = UnixFilename(str);
while (str.Replace(_T("\\"), _T("/")))
;
while (str.Replace(_T("//"), _T("/")))
;
// str.Replace("/", "//");
if (str.Find(_T(' ')) != -1 && str.GetChar(0) != _T('"'))
str = _T("\"") + str + _T("\"");
}
//if relative == false, try to leave as an absolute path
void DebuggerGDB::ConvertToGDBDirectory(wxString& str, wxString base, bool relative)
{
if (str.IsEmpty())
return;
ConvertToGDBFriendly(str);
ConvertToGDBFriendly(base);
StripQuotes(str);
StripQuotes(base);
#ifdef __WXMSW__
int ColonLocation = str.Find(_T(':'));
wxChar buf[255];
if(ColonLocation != -1)
{
//If can, get 8.3 name for path (Windows only)
GetShortPathName(str.c_str(), buf, 255);
str = buf;
}
else if(!base.IsEmpty() && str.GetChar(0) != _T('/'))
{
if(base.GetChar(base.Length()) == _T('/')) base = base.Mid(0, base.Length() - 2);
while(!str.IsEmpty())
{
base += _T("/") + str.BeforeFirst(_T('/'));
if(str.Find(_T('/')) != -1) str = str.AfterFirst(_T('/'));
else str.Clear();
}
GetShortPathName(base.c_str(), buf, 255);
str = buf;
}
if(ColonLocation == -1 || base.IsEmpty())
relative = false; //Can't do it
#else
if((str.GetChar(0) != _T('/') && str.GetChar(0) != _T('~')) || base.IsEmpty())
relative = false;
#endif
if(relative)
{
#ifdef __WXMSW__
if(str.Find(_T(':')) != -1) str = str.Mid(str.Find(_T(':')) + 2, str.Length());
if(base.Find(_T(':')) != -1) base = base.Mid(base.Find(_T(':')) + 2, base.Length());
#else
if(str.GetChar(0) == _T('/')) str = str.Mid(1, str.Length());
else if(str.GetChar(0) == _T('~')) str = str.Mid(2, str.Length());
if(base.GetChar(0) == _T('/')) base = base.Mid(1, base.Length());
else if(base.GetChar(0) == _T('~')) base = base.Mid(2, base.Length());
#endif
while(!base.IsEmpty() && !str.IsEmpty())
{
if(str.BeforeFirst(_T('/')) == base.BeforeFirst(_T('/')))
{
if(str.Find(_T('/')) == -1) str.Clear();
else str = str.AfterFirst(_T('/'));
if(base.Find(_T('/')) == -1) base.Clear();
else base = base.AfterFirst(_T('/'));
}
else break;
}
while (!base.IsEmpty())
{
str = _T("../") + str;
if(base.Find(_T('/')) == -1) base.Clear();
else base = base.AfterFirst(_T('/'));
}
}
ConvertToGDBFriendly(str);
}
void DebuggerGDB::SendCommand(const wxString& cmd)
{
if (!m_pProcess || !m_ProgramIsStopped)
return;
if (m_HasDebugLog)
Manager::Get()->GetMessageManager()->Log(m_DbgPageIndex, _T("> ") + cmd);
m_pProcess->SendString(cmd);
}
wxString DebuggerGDB::GetNextOutputLine(bool useStdErr)
{
if (!m_pProcess)
return wxEmptyString;
wxString bufferOut;
wxInputStream* m_pOut = useStdErr ? m_pProcess->GetErrorStream() : m_pProcess->GetInputStream();
while (useStdErr ? true : m_pProcess->IsInputOpened() &&
useStdErr ? m_pProcess->IsErrorAvailable() : m_pProcess->IsInputAvailable() &&
!m_pOut->Eof())
{
wxChar ch = m_pOut->GetC();
if (ch == _T('\n') || ch == _T('\r'))
{
while (useStdErr ? m_pProcess->IsErrorAvailable() : m_pProcess->IsInputAvailable() &&
!m_pOut->Eof() &&
(m_pOut->Peek() == _T('\n') || m_pOut->Peek() == _T('\r'))
)
ch = m_pOut->GetC();
break;
}
else
bufferOut << ch;
}
if (m_HasDebugLog)
{
if (!bufferOut.IsEmpty())
m_pDbgLog->AddLog(bufferOut);
}
return bufferOut;
}
wxString DebuggerGDB::GetNextOutputLineClean(bool useStdErr)
{
wxString line = GetNextOutputLine(useStdErr);
while (line.IsEmpty() || line.StartsWith(g_EscapeChars))
line = GetNextOutputLine(useStdErr);
return line;
}
void DebuggerGDB::RunCommand(int cmd)
{
if (!m_pProcess || !m_ProgramIsStopped)
return;
switch (cmd)
{
case CMD_CONTINUE:
ClearActiveMarkFromAllEditors();
Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Continuing..."));
SendCommand(_T("cont"));
break;
case CMD_STEP:
ClearActiveMarkFromAllEditors();
SendCommand(_T("next"));
break;
case CMD_STEPIN:
ClearActiveMarkFromAllEditors();
SendCommand(_T("step"));
break;
case CMD_STOP:
ClearActiveMarkFromAllEditors();
SendCommand(_T("quit"));
break;
case CMD_BACKTRACE:
// Manager::Get()->GetMessageManager()->Log(m_PageIndex, "Running back-trace...");
SendCommand(_T("bt"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -