mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
802 lines
17 KiB
C++
802 lines
17 KiB
C++
#include "Debuggers.h"
|
|
|
|
#define METHOD_NAME UPP_METHOD_NAME("Gdb")
|
|
|
|
void Gdb::WatchMenu(Bar& bar)
|
|
{
|
|
bool b = !IdeIsDebugLock();
|
|
bar.Add(b, PdbKeys::AK_CLEARWATCHES, THISBACK(ClearWatches));
|
|
bar.Add(b, PdbKeys::AK_ADDWATCH, THISBACK(QuickWatch));
|
|
}
|
|
|
|
void Gdb::DebugBar(Bar& bar)
|
|
{
|
|
using namespace PdbKeys;
|
|
|
|
bar.Add("Stop debugging", DbgImg::StopDebug(), THISBACK(Stop))
|
|
.Key(K_SHIFT_F5);
|
|
bar.Separator();
|
|
bool b = !IdeIsDebugLock();
|
|
bar.Add(b, AK_STEPINTO, DbgImg::StepInto(), THISBACK1(Step, disas.HasFocus() ? "stepi"
|
|
: "step"));
|
|
bar.Add(b, AK_STEPOVER, DbgImg::StepOver(), THISBACK1(Step, disas.HasFocus() ? "nexti"
|
|
: "next"));
|
|
bar.Add(b, AK_STEPOUT, DbgImg::StepOut(), THISBACK1(Step, "finish"));
|
|
bar.Add(b, AK_RUNTO, DbgImg::RunTo(), THISBACK(DoRunTo));
|
|
bar.Add(b, AK_RUN, DbgImg::Run(), THISBACK(Run));
|
|
bar.Add(!b && pid, AK_BREAK, DbgImg::Stop(), THISBACK(BreakRunning));
|
|
bar.MenuSeparator();
|
|
bar.Add(b, AK_AUTOS, THISBACK1(SetTab, 0));
|
|
bar.Add(b, AK_LOCALS, THISBACK1(SetTab, 1));
|
|
bar.Add(b, AK_THISS, THISBACK1(SetTab, 2));
|
|
bar.Add(b, AK_WATCHES, THISBACK1(SetTab, 3));
|
|
WatchMenu(bar);
|
|
bar.Add(b, AK_CPU, THISBACK1(SetTab, 4));
|
|
bar.MenuSeparator();
|
|
bar.Add(b, "Copy backtrace", THISBACK(CopyStack));
|
|
bar.Add(b, "Copy backtrace of all threads", THISBACK(CopyStackAll));
|
|
bar.Add(b, "Copy dissassembly", THISBACK(CopyDisas));
|
|
}
|
|
|
|
String FirstLine(const String& s)
|
|
{
|
|
int q = s.Find('\r');
|
|
if(q < 0)
|
|
q = s.Find('\n');
|
|
return q >= 0 ? s.Mid(0, q) : s;
|
|
}
|
|
|
|
String FormatFrame(const char *s)
|
|
{
|
|
if(*s++ != '#')
|
|
return Null;
|
|
while(IsDigit(*s))
|
|
s++;
|
|
while(*s == ' ')
|
|
s++;
|
|
if(s[0] == '0' && ToUpper(s[1]) == 'X') {
|
|
s += 2;
|
|
while(IsXDigit(*s))
|
|
s++;
|
|
while(*s == ' ')
|
|
s++;
|
|
if(s[0] == 'i' && s[1] == 'n')
|
|
s += 2;
|
|
while(*s == ' ')
|
|
s++;
|
|
}
|
|
String result;
|
|
const char *w = strchr(s, '\r');
|
|
if(!w)
|
|
w = strchr(s, '\n');
|
|
if(w)
|
|
result = String(s, w);
|
|
else
|
|
result = s;
|
|
if(!IsAlpha(*s)) {
|
|
int q = result.ReverseFind(' ');
|
|
if(q >= 0)
|
|
result = result.Mid(q + 1);
|
|
}
|
|
return result.GetCount() > 2 ? result : Null;
|
|
}
|
|
|
|
void Gdb::CopyStack()
|
|
{
|
|
if(IdeIsDebugLock())
|
|
return;
|
|
DropFrames();
|
|
String s;
|
|
for(int i = 0; i < frame.GetCount(); i++)
|
|
s << frame.GetValue(i) << "\n";
|
|
WriteClipboardText(s);
|
|
}
|
|
|
|
void Gdb::CopyStackAll()
|
|
{
|
|
String s = FastCmd("info threads");
|
|
StringStream ss(s);
|
|
String r;
|
|
while(!ss.IsEof()) {
|
|
String s = ss.GetLine();
|
|
CParser p(s);
|
|
try {
|
|
p.Char('*');
|
|
if(p.IsNumber()) {
|
|
int id = p.ReadInt();
|
|
r << "----------------------------------\r\n"
|
|
<< "Thread: " << id << "\r\n\r\n";
|
|
|
|
FastCmd(Sprintf("thread %d", id));
|
|
|
|
int i = 0;
|
|
for(;;) {
|
|
String s = FormatFrame(FastCmd("frame " + AsString(i++)));
|
|
if(IsNull(s)) break;
|
|
r << s << "\r\n";
|
|
}
|
|
r << "\r\n";
|
|
}
|
|
}
|
|
catch(CParser::Error) {}
|
|
}
|
|
FastCmd("thread " + ~~threads);
|
|
WriteClipboardText(r);
|
|
}
|
|
|
|
void Gdb::CopyDisas()
|
|
{
|
|
if(IdeIsDebugLock())
|
|
return;
|
|
disas.WriteClipboard();
|
|
}
|
|
|
|
int CharFilterReSlash(int c)
|
|
{
|
|
return c == '\\' ? '/' : c;
|
|
}
|
|
|
|
String Bpoint(const String& file, int line)
|
|
{
|
|
return String().Cat() << Filter(NormalizePath(file), CharFilterReSlash) << ":" << line + 1;
|
|
}
|
|
|
|
bool Gdb::TryBreak(const char *text)
|
|
{
|
|
return FindTag(FastCmd(text), "Breakpoint");
|
|
}
|
|
|
|
bool Gdb::SetBreakpoint(const String& filename, int line, const String& bp)
|
|
{
|
|
if(IdeIsDebugLock()) {
|
|
BreakRunning();
|
|
bp_filename = filename;
|
|
bp_line = line;
|
|
bp_val = bp;
|
|
return true;
|
|
}
|
|
|
|
String bi = Bpoint(filename, line);
|
|
|
|
String command;
|
|
if(bp.IsEmpty())
|
|
command = "clear " + bi;
|
|
else if(bp[0]==0xe || bp == "1")
|
|
command = "b " + bi;
|
|
else
|
|
command = "b " + bi + " if " + bp;
|
|
return !FastCmd(command).IsEmpty();
|
|
}
|
|
|
|
void Gdb::SetDisas(const String& text)
|
|
{
|
|
disas.Clear();
|
|
StringStream ss(text);
|
|
while(!ss.IsEof()) {
|
|
String ln = ss.GetLine();
|
|
const char *s = ln;
|
|
while(*s && !IsDigit(*s))
|
|
s++;
|
|
adr_t adr = 0;
|
|
String code, args;
|
|
if(s[0] == '0' && ToLower(s[1]) == 'x')
|
|
adr = (adr_t)ScanInt64(s + 2, NULL, 16);
|
|
int q = nodebuginfo ? ln.Find(":\t") : ln.Find(">:");
|
|
if(q >= 0) {
|
|
s = ~ln + q + 2;
|
|
while(IsSpace(*s))
|
|
s++;
|
|
while(*s && !IsSpace(*s))
|
|
code.Cat(*s++);
|
|
while(IsSpace(*s))
|
|
s++;
|
|
args = s;
|
|
q = args.Find("0x");
|
|
if(q >= 0)
|
|
disas.AddT(ScanInt(~args + q + 2, NULL, 16));
|
|
disas.Add(adr, code, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Gdb::SyncDisas(bool fr)
|
|
{
|
|
if(!disas.IsVisible())
|
|
return;
|
|
if(!disas.InRange(addr))
|
|
SetDisas(FastCmd("disas"));
|
|
disas.SetCursor(addr);
|
|
disas.SetIp(addr, fr ? DbgImg::FrameLinePtr() : DbgImg::IpLinePtr());
|
|
}
|
|
|
|
bool ParsePos(const String& s, String& fn, int& line, adr_t & adr)
|
|
{
|
|
const char *q = FindTag(s, "\x1a\x1a");
|
|
if(!q) return false;
|
|
q += 2;
|
|
Vector<String> p = Split(q + 2, ':');
|
|
p.SetCount(5);
|
|
fn = String(q, q + 2) + p[0];
|
|
line = atoi(p[1]);
|
|
try {
|
|
CParser pa(p[4]);
|
|
pa.Char2('0', 'x');
|
|
if(pa.IsNumber(16))
|
|
adr = (adr_t)pa.ReadNumber64(16);
|
|
}
|
|
catch(CParser::Error) {}
|
|
return true;
|
|
}
|
|
|
|
void Gdb::CheckEnd(const char *s)
|
|
{
|
|
if(!dbg.IsRunning()) {
|
|
Stop();
|
|
return;
|
|
}
|
|
if(FindTag(s, "Program exited normally.")) {
|
|
Stop();
|
|
return;
|
|
}
|
|
const char *q = FindTag(s, "Program exited with code ");
|
|
if(q) {
|
|
PutConsole(q);
|
|
Stop();
|
|
return;
|
|
}
|
|
}
|
|
|
|
String Gdb::Cmdp(const char *cmdline, bool fr, bool setframe)
|
|
{
|
|
expression_cache.Clear();
|
|
IdeHidePtr();
|
|
String s = Cmd(cmdline);
|
|
exception.Clear();
|
|
if(!running_interrupted) {
|
|
int q = s.Find("received signal SIG");
|
|
if(q >= 0) {
|
|
int l = s.ReverseFind('\n', q);
|
|
if(l < 0)
|
|
l = 0;
|
|
int r = s.Find('\n', q);
|
|
if(r < 0)
|
|
r = s.GetCount();
|
|
if(l < r)
|
|
exception = s.Mid(l, r - l);
|
|
}
|
|
}
|
|
else {
|
|
running_interrupted = false;
|
|
}
|
|
|
|
if(ParsePos(s, file, line, addr)) {
|
|
IdeSetDebugPos(file, line - 1, fr ? DbgImg::FrameLinePtr()
|
|
: DbgImg::IpLinePtr(), 0);
|
|
IdeSetDebugPos(file, line - 1,
|
|
disas.HasFocus() ? fr ? DbgImg::FrameLinePtr() : DbgImg::IpLinePtr()
|
|
: Image(), 1);
|
|
SyncDisas(fr);
|
|
autoline.Clear();
|
|
for(int i = -4; i <= 4; i++)
|
|
autoline << ' ' << IdeGetLine(line + i);
|
|
}
|
|
else {
|
|
file = Null;
|
|
try {
|
|
int q = s.ReverseFind("0x");
|
|
if(q >= 0) {
|
|
CParser pa(~s + q + 2);
|
|
addr = (adr_t)pa.ReadNumber64(16);
|
|
SetDisas(FastCmd(String() << "disas 0x" << FormatHex((void *)addr) << ",+1024"));
|
|
disas.SetCursor(addr);
|
|
disas.SetIp(addr, DbgImg::IpLinePtr());
|
|
}
|
|
}
|
|
catch(CParser::Error) {}
|
|
}
|
|
|
|
if(setframe) {
|
|
frame.Clear();
|
|
String f = FastCmd("frame");
|
|
frame.Add(0, nodebuginfo ? FirstLine(f) : FormatFrame(f));
|
|
frame <<= 0;
|
|
SyncFrameButtons();
|
|
}
|
|
|
|
if (dbg.IsRunning()) {
|
|
if (IsProcessExitedNormally(s))
|
|
Stop();
|
|
else {
|
|
s = ObtainThreadsInfo();
|
|
|
|
ObtainData();
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
bool Gdb::IsProcessExitedNormally(const String& cmd_output) const
|
|
{
|
|
const auto phrase = String().Cat() << "(process " << pid << ") exited normally";
|
|
return cmd_output.Find(phrase) >= 0;
|
|
}
|
|
|
|
String Gdb::ObtainThreadsInfo()
|
|
{
|
|
threads.Clear();
|
|
String output = FastCmd("info threads");
|
|
StringStream ss(output);
|
|
int active_thread = -1;
|
|
bool main = true;
|
|
while(!ss.IsEof()) {
|
|
String s = ss.GetLine();
|
|
CParser p(s);
|
|
try {
|
|
bool is_active = p.Char('*');
|
|
if(!p.IsNumber())
|
|
continue;
|
|
|
|
int id = p.ReadInt();
|
|
|
|
String name;
|
|
while (!p.IsEof()) {
|
|
if (p.IsString()) {
|
|
name = p.ReadString();
|
|
break;
|
|
}
|
|
|
|
p.SkipTerm();
|
|
}
|
|
|
|
AttrText text(String() << "Thread " << id);
|
|
if (!name.IsEmpty())
|
|
text.Set(text.ToString() << " (" << name << ")");
|
|
if(is_active) {
|
|
active_thread = id;
|
|
text.Bold();
|
|
}
|
|
if(main) {
|
|
text.Underline();
|
|
main = false;
|
|
}
|
|
threads.Add(id, text);
|
|
threads.GoBegin();
|
|
}
|
|
catch(CParser::Error e) {
|
|
Loge() << METHOD_NAME << e;
|
|
}
|
|
}
|
|
|
|
if(active_thread >= 0)
|
|
threads <<= active_thread;
|
|
|
|
return output;
|
|
}
|
|
|
|
void Gdb::ShowException()
|
|
{
|
|
if(exception.GetCount())
|
|
ErrorOK(exception);
|
|
exception.Clear();
|
|
}
|
|
|
|
String Gdb::DoRun()
|
|
{
|
|
if(firstrun) {
|
|
firstrun = false;
|
|
nodebuginfo_bg.Hide();
|
|
nodebuginfo = false;
|
|
if(Cmd("start").Find("No symbol") >= 0) {
|
|
nodebuginfo_bg.Show();
|
|
nodebuginfo = true;
|
|
String t = Cmd("run");
|
|
int q = t.ReverseFind("exited normally");
|
|
if(t.GetLength() - q < 20) {
|
|
Stop();
|
|
return Null;
|
|
}
|
|
}
|
|
if(!IsFinished()) {
|
|
String s = Cmd("info inferior");
|
|
int q = s.FindAfter("process");
|
|
pid = atoi(~s + q);
|
|
}
|
|
IdeSetBar();
|
|
}
|
|
|
|
String s;
|
|
for(;;) {
|
|
ClearCtrls();
|
|
s = Cmdp("continue");
|
|
if(IsNull(bp_filename))
|
|
break;
|
|
if(!IdeIsDebugLock())
|
|
SetBreakpoint(bp_filename, bp_line, bp_val);
|
|
bp_filename.Clear();
|
|
}
|
|
ShowException();
|
|
return s;
|
|
}
|
|
|
|
bool Gdb::RunTo()
|
|
{
|
|
if(IdeIsDebugLock() || nodebuginfo)
|
|
return false;
|
|
String bi;
|
|
bool df = disas.HasFocus();
|
|
if(df) {
|
|
if(!disas.GetCursor())
|
|
return false;
|
|
bi = Sprintf("*0x%X", disas.GetCursor());
|
|
}
|
|
else
|
|
bi = Bpoint(IdeGetFileName(), IdeGetFileLine());
|
|
if(!TryBreak("b " + bi)) {
|
|
Exclamation("No code at chosen location!");
|
|
return false;
|
|
}
|
|
String e = DoRun();
|
|
FastCmd("clear " + bi);
|
|
if(df)
|
|
disas.SetFocus();
|
|
CheckEnd(e);
|
|
IdeActivateBottom();
|
|
return true;
|
|
}
|
|
|
|
void Gdb::BreakRunning()
|
|
{
|
|
Logd() << METHOD_NAME << "PID: " << pid << "\n";
|
|
|
|
auto error = gdb_utils->BreakRunning(pid);
|
|
if(!error.IsEmpty()) {
|
|
Loge() << METHOD_NAME << error;
|
|
ErrorOK(error);
|
|
return;
|
|
}
|
|
|
|
running_interrupted = true;
|
|
}
|
|
|
|
void Gdb::Run()
|
|
{
|
|
if(IdeIsDebugLock())
|
|
return;
|
|
String s = DoRun();
|
|
CheckEnd(s);
|
|
IdeActivateBottom();
|
|
}
|
|
|
|
void Gdb::Step(const char *cmd)
|
|
{
|
|
if(IdeIsDebugLock())
|
|
return;
|
|
bool b = disas.HasFocus();
|
|
String s = Cmdp(cmd);
|
|
if(b) disas.SetFocus();
|
|
CheckEnd(s);
|
|
IdeActivateBottom();
|
|
ShowException();
|
|
}
|
|
|
|
void Gdb::DisasCursor()
|
|
{
|
|
if(!disas.HasFocus())
|
|
return;
|
|
int line;
|
|
String file;
|
|
adr_t addr;
|
|
if(ParsePos(FastCmd(Sprintf("info line *0x%X", disas.GetCursor())), file, line, addr))
|
|
IdeSetDebugPos(file, line - 1, DbgImg::DisasPtr(), 1);
|
|
disas.SetFocus();
|
|
}
|
|
|
|
void Gdb::DisasFocus()
|
|
{
|
|
}
|
|
|
|
void Gdb::DropFrames()
|
|
{
|
|
if(frame.GetCount() < 2)
|
|
LoadFrames();
|
|
SyncFrameButtons();
|
|
}
|
|
|
|
void Gdb::LoadFrames()
|
|
{
|
|
if(frame.GetCount())
|
|
frame.Trim(frame.GetCount() - 1);
|
|
int i = frame.GetCount();
|
|
int n = 0;
|
|
for(;;) {
|
|
String f = FastCmd(Sprintf("frame %d", i));
|
|
String s;
|
|
if(nodebuginfo) {
|
|
s = FirstLine(f);
|
|
int q = s.Find("0x");
|
|
if(q < 0)
|
|
break;
|
|
try {
|
|
CParser p(~s + q + 2);
|
|
if(p.ReadNumber64(16) == 0)
|
|
break;
|
|
}
|
|
catch(CParser::Error) {
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
s = nodebuginfo ? FirstLine(f) : FormatFrame(f);
|
|
if(IsNull(s))
|
|
break;
|
|
if(n++ >= max_stack_trace_size) {
|
|
frame.Add(Null, Sprintf("<load more> (%d loaded)", frame.GetCount()));
|
|
break;
|
|
}
|
|
frame.Add(i++, s);
|
|
}
|
|
SyncFrameButtons();
|
|
}
|
|
|
|
void Gdb::SwitchFrame()
|
|
{
|
|
int i = ~frame;
|
|
if(IsNull(i)) {
|
|
i = frame.GetCount() - 1;
|
|
LoadFrames();
|
|
frame <<= i;
|
|
}
|
|
Cmdp("frame " + AsString(i), i, false);
|
|
SyncFrameButtons();
|
|
}
|
|
|
|
void Gdb::FrameUpDown(int dir)
|
|
{
|
|
if(frame.GetCount() < 2)
|
|
LoadFrames();
|
|
int q = frame.GetIndex() + dir;
|
|
if(q >= 0 && q < frame.GetCount()) {
|
|
frame.SetIndex(q);
|
|
SwitchFrame();
|
|
}
|
|
}
|
|
|
|
void Gdb::SwitchThread()
|
|
{
|
|
int i = ~threads;
|
|
Cmdp("thread " + AsString(i));
|
|
SyncFrameButtons();
|
|
}
|
|
|
|
void Gdb::ClearCtrls()
|
|
{
|
|
threads.Clear();
|
|
disas.Clear();
|
|
|
|
locals.Clear();
|
|
autos.Clear();
|
|
self.Clear();
|
|
cpu.Clear();
|
|
|
|
tree.Clear();
|
|
}
|
|
|
|
bool Gdb::Key(dword key, int count)
|
|
{
|
|
if(key >= 32 && key < 65535 && tab.Get() == 2) {
|
|
watches.DoInsertAfter();
|
|
Ctrl* f = GetFocusCtrl();
|
|
if(f && watches.HasChildDeep(f))
|
|
f->Key(key, count);
|
|
return true;
|
|
}
|
|
return Ctrl::Key(key, count);
|
|
}
|
|
|
|
bool Gdb::Create(Host& host, const String& exefile, const String& cmdline, bool console)
|
|
{
|
|
String gdb_command = GdbCommand(console) + NormalizeExePath(exefile);
|
|
|
|
#ifdef PLATFORM_POSIX
|
|
#ifndef PLATFORM_MACOS
|
|
IGNORE_RESULT(HostSys("setxkbmap -option grab:break_actions")); // to be able to recover capture in breakpoint
|
|
String xdotool_chk = ConfigFile("xdotool_chk");
|
|
String out;
|
|
if(!FileExists(xdotool_chk) && HostSys("xdotool key XF86Ungrab", out)) {
|
|
Exclamation("[* xdotool] utility is not installed or does not work properly.&"
|
|
"Debugger will be unable to ungrab debugee's mouse capture - "
|
|
"mouse might become unusable when debugee stops.");
|
|
SaveFile(xdotool_chk, "1");
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if(!host.StartProcess(dbg, gdb_command)) {
|
|
Loge() << METHOD_NAME << "Failed to launch gdb (\"" << gdb_command << "\").";
|
|
|
|
ErrorOK("Error while invoking gdb! For details check TheIDE logs.");
|
|
return false;
|
|
}
|
|
|
|
IdeSetBottom(*this);
|
|
IdeSetRight(disas);
|
|
|
|
disas.WhenCursor = THISBACK(DisasCursor);
|
|
disas.WhenFocus = THISBACK(DisasFocus);
|
|
frame.WhenDrop = THISBACK(DropFrames);
|
|
frame <<= THISBACK(SwitchFrame);
|
|
|
|
threads <<= THISBACK(SwitchThread);
|
|
|
|
watches.WhenAcceptEdit = THISBACK(ObtainData);
|
|
tab <<= THISBACK(ObtainData);
|
|
|
|
Cmd("set prompt " GDB_PROMPT);
|
|
Cmd("set disassembly-flavor intel");
|
|
Cmd("set exec-done-display off");
|
|
Cmd("set annotate 1");
|
|
Cmd("set height 0");
|
|
Cmd("set width 0");
|
|
Cmd("set confirm off");
|
|
Cmd("set print asm-demangle");
|
|
Cmd("set print static-members off");
|
|
Cmd("set print vtbl off");
|
|
Cmd("set print repeat 0");
|
|
Cmd("set print null-stop");
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
Cmd("set new-console on");
|
|
#endif
|
|
|
|
if(!IsNull(cmdline))
|
|
Cmd("set args " + cmdline);
|
|
|
|
firstrun = true;
|
|
running_interrupted = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Gdb::~Gdb()
|
|
{
|
|
StringStream ss;
|
|
Store(callback(this, &Gdb::SerializeSession), ss);
|
|
WorkspaceConfigData("gdb-debugger") = ss;
|
|
|
|
IdeRemoveBottom(*this);
|
|
IdeRemoveRight(disas);
|
|
KillDebugTTY();
|
|
}
|
|
|
|
void Gdb::Periodic()
|
|
{
|
|
if(TTYQuit())
|
|
Stop();
|
|
}
|
|
|
|
void Gdb::SerializeSession(Stream& s)
|
|
{
|
|
int version = 0;
|
|
s / version;
|
|
int n = watches.GetCount();
|
|
s / n;
|
|
if(n < 0)
|
|
s.LoadError();
|
|
for(int i = 0; i < n; i++) {
|
|
String w;
|
|
if(s.IsStoring())
|
|
w = watches.Get(i, 0);
|
|
s % w;
|
|
if(s.IsLoading())
|
|
watches.Add(w);
|
|
}
|
|
}
|
|
|
|
Gdb::Gdb()
|
|
: gdb_utils(GdbUtilsFactory().Create())
|
|
{
|
|
auto Mem = [=](Bar& bar, ArrayCtrl& h) {
|
|
String s = h.GetKey();
|
|
if(s.GetCount()) {
|
|
if(!IsAlpha(*s))
|
|
s = '(' + s + ')';
|
|
MemoryMenu(bar, s);
|
|
}
|
|
};
|
|
locals.NoHeader();
|
|
locals.AddColumn("", 1);
|
|
locals.AddColumn("", 6);
|
|
locals.EvenRowColor();
|
|
locals.WhenSel = THISBACK1(SetTree, &locals);
|
|
locals.WhenBar = [=](Bar& bar) { Mem(bar, locals); };
|
|
watches.NoHeader();
|
|
watches.AddColumn("", 1).Edit(watchedit);
|
|
watches.AddColumn("", 6);
|
|
watches.Inserting().Removing();
|
|
watches.EvenRowColor();
|
|
watches.WhenSel = THISBACK1(SetTree, &watches);
|
|
watches.WhenBar = [=](Bar& bar) { Mem(bar, watches); WatchMenu(bar); };
|
|
autos.NoHeader();
|
|
autos.AddColumn("", 1);
|
|
autos.AddColumn("", 6);
|
|
autos.EvenRowColor();
|
|
autos.WhenSel = THISBACK1(SetTree, &autos);
|
|
autos.WhenBar = [=](Bar& bar) { Mem(bar, autos); };
|
|
self.NoHeader();
|
|
self.AddColumn("", 1);
|
|
self.AddColumn("", 6);
|
|
self.EvenRowColor();
|
|
self.WhenSel = THISBACK1(SetTree, &self);
|
|
self.WhenBar = [=](Bar& bar) { Mem(bar, self); };
|
|
cpu.Columns(3);
|
|
cpu.ItemHeight(Courier(Ctrl::HorzLayoutZoom(12)).GetCy());
|
|
|
|
pane.Add(tab.SizePos());
|
|
tab.Add(autos.SizePos(), "Autos");
|
|
tab.Add(locals.SizePos(), "Locals");
|
|
tab.Add(watches.SizePos(), "Watches");
|
|
tab.Add(self.SizePos(), "this");
|
|
tab.Add(cpu.SizePos(), "CPU");
|
|
tab.Add(memory.SizePos(), "Memory");
|
|
pane.Add(threads.LeftPosZ(330, 150).TopPos(2));
|
|
|
|
memory.WhenGotoDlg = [=] { MemoryGoto(); };
|
|
|
|
int bcx = min(EditField::GetStdHeight(), DPI(16));
|
|
pane.Add(frame.HSizePos(Zx(484), 2 * bcx).TopPos(2));
|
|
pane.Add(frame_up.RightPos(bcx, bcx).TopPos(2, EditField::GetStdHeight()));
|
|
frame_up.SetImage(DbgImg::FrameUp());
|
|
frame_up << [=] { FrameUpDown(-1); };
|
|
frame_up.Tip("Previous Frame");
|
|
pane.Add(frame_down.RightPos(0, bcx).TopPos(2, EditField::GetStdHeight()));
|
|
frame_down.SetImage(DbgImg::FrameDown());
|
|
frame_down << [=] { FrameUpDown(1); };
|
|
frame_down.Tip("Next Frame");
|
|
|
|
split.Horz(pane, tree.SizePos());
|
|
split.SetPos(8000);
|
|
Add(split);
|
|
|
|
tree.WhenOpen = THISBACK(OnTreeExpand);
|
|
tree.WhenBar = THISBACK(OnTreeBar);
|
|
|
|
frame.Ctrl::Add(dlock.SizePos());
|
|
dlock = " Running..";
|
|
dlock.SetFrame(BlackFrame());
|
|
dlock.SetInk(Red);
|
|
dlock.NoTransparent();
|
|
dlock.Hide();
|
|
|
|
CtrlLayoutOKCancel(quickwatch, "Watch");
|
|
quickwatch.WhenClose = quickwatch.Breaker(IDCANCEL);
|
|
quickwatch.value.SetReadOnly();
|
|
quickwatch.value.SetFont(CourierZ(11));
|
|
quickwatch.Sizeable().Zoomable();
|
|
quickwatch.SetRect(0, 150, 300, 400);
|
|
|
|
Transparent();
|
|
|
|
periodic.Set(-50, THISBACK(Periodic));
|
|
|
|
StringStream ss(WorkspaceConfigData("gdb-debugger"));
|
|
Load(callback(this, &Gdb::SerializeSession), ss);
|
|
|
|
const char *text = "No symbolic information available";
|
|
Size sz = GetTextSize(text, StdFont().Italic().Bold());
|
|
nodebuginfo_bg.Background(Gray())
|
|
.RightPos(0, sz.cx + DPI(8))
|
|
.BottomPos(0, sz.cy + DPI(6))
|
|
.Add(nodebuginfo_text.SizePos());
|
|
nodebuginfo_text = text;
|
|
nodebuginfo_text.AlignCenter().SetInk(Yellow()).SetFont(StdFont().Italic().Bold());
|
|
|
|
pane.Add(nodebuginfo_bg);
|
|
}
|
|
|
|
One<Debugger> GdbCreate(Host& host, const String& exefile, const String& cmdline, bool console)
|
|
{
|
|
auto dbg = MakeOne<Gdb>();
|
|
if(!dbg->Create(host, exefile, cmdline, console))
|
|
return nullptr;
|
|
return pick(dbg); // CLANG does not like this without pick
|
|
}
|