#include "Debuggers.h" #define KEYGROUPNAME "Debugger" #define KEYNAMESPACE PdbKeys #define KEYFILE #include #ifdef PLATFORM_WIN32 #include Vector GetChildProcessList(DWORD processId) { Vector child, all, parents; HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap == INVALID_HANDLE_VALUE) return child; PROCESSENTRY32 proc; proc.dwSize = sizeof(proc); if (!Process32First(hSnap, &proc)) { CloseHandle(hSnap); return child; } do { all << proc.th32ProcessID; parents << proc.th32ParentProcessID; } while(Process32Next(hSnap, &proc)); CloseHandle(hSnap); child << processId; int init = 0; while (true) { int count = child.GetCount(); if (init >= count) break; for (int cid = init; cid < count; ++cid) { for (int i = 0; i < all.GetCount(); ++i) { if (parents[i] == child[cid]) child << all[i]; } } init = count; } child.Remove(0); return child; } void TerminateChildProcesses(DWORD dwProcessId, UINT uExitCode) { Vector children = GetChildProcessList(dwProcessId); for (int i = 0; i < children.GetCount(); ++i) { HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, children[i]); TerminateProcess(hProcess, uExitCode); CloseHandle(hProcess); } } #pragma comment(lib, "DbgHelp.lib") #pragma comment(lib, "psapi.lib") #define LLOG(x) // LOG(x) using namespace PdbKeys; void Pdb::DebugBar(Bar& bar) { bar.Add(AK_STOP, DbgImg::StopDebug(), THISBACK(Stop)); bool b = !IdeIsDebugLock(); bar.Separator(); bar.Add(b, AK_STEPINTO, DbgImg::StepInto(), THISBACK1(Trace, false)); bar.Add(b, AK_STEPOVER, DbgImg::StepOver(), THISBACK1(Trace, true)); bar.Add(b, AK_STEPOUT, DbgImg::StepOut(), THISBACK(StepOut)); bar.Add(b, AK_RUNTO, DbgImg::RunTo(), THISBACK(DoRunTo)); bar.Add(b, AK_RUN, DbgImg::Run(), THISBACK(Run)); bar.Add(b, AK_SETIP, DbgImg::SetIp(), THISBACK(SetIp)); bar.Add(!b, 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)); bar.Add(b, AK_CLEARWATCHES, THISBACK(ClearWatches)); bar.Add(b, AK_ADDWATCH, [=] { AddWatch(); }); bar.Add(b, AK_CPU, THISBACK1(SetTab, 4)); bar.Add(b, AK_MEMORY, THISBACK1(SetTab, 5)); bar.Add(b, AK_BTS, THISBACK1(SetTab, 6)); bar.MenuSeparator(); bar.Add(b, "Copy backtrace", [=] { WriteClipboardText(CopyStack()); }); bar.Add(b, "Copy backtrace of all threads", [=] { WriteClipboardText(CopyStackAll()); }); bar.Add(b, "Copy dissassembly", THISBACK(CopyDisas)); bar.Add(b, "Copy modules", THISBACK(CopyModules)); } void Pdb::Tab() { switch(tab.Get()) { case TAB_AUTOS: autos.SetFocus(); break; case TAB_LOCALS: locals.SetFocus(); break; case TAB_THIS: self.SetFocus(); break; case TAB_WATCHES: watches.SetFocus(); break; case TAB_MEMORY: memory.SetFocus(); break; case TAB_BTS: bts.SetFocus(); break; } Data(); SyncTreeDisas(); } void Pdb::SyncTreeDisas() { bool d = tab.Get() == TAB_CPU || IsNull(tree_exp); disas.Show(d); tree.Show(!d); } bool Pdb::Key(dword key, int count) { if(key >= 32 && key < 65535 && tab.Get() == TAB_LOCALS) { watches.DoInsertAfter(); Ctrl* f = GetFocusCtrl(); if(f && watches.HasChildDeep(f)) f->Key(key, count); return true; } return Ctrl::Key(key, count); } #define CONFIGNAME "pdb debugger" void Pdb::Serialize(Stream& s) { int version = 0; s / version; memory.SerializeSettings(s); Splitter dummy; s % dummy; s % show_type; } INITBLOCK { RegisterGlobalConfig(CONFIGNAME); } bool Pdb::Create(Host& local, const String& exefile, const String& cmdline, bool clang_) { STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = 0; String cl; if(exefile.Find(' ') >= 0) cl << '\"' << exefile << '\"'; else cl << exefile; if(!IsNull(cmdline)) cl << ' ' << ToSystemCharset(cmdline); clang = clang_; Buffer cmd(cl.GetLength() + 1); memcpy(cmd, cl, cl.GetLength() + 1); PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); Buffer env(local.environment.GetCount() + 1); memcpy(env, ~local.environment, local.environment.GetCount() + 1); bool h = CreateProcess(exefile, cmd, NULL, NULL, TRUE, /*NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE|*/DEBUG_ONLY_THIS_PROCESS/*|DEBUG_PROCESS*/, local.environment.GetCount() ? ~env : NULL, NULL, &si, &pi); if(!h) { Exclamation("Error creating process&[* " + DeQtf(exefile) + "]&" + "Windows error: " + DeQtf(GetLastErrorMessage())); return false; } hProcess = pi.hProcess; mainThread = pi.hThread; hProcessId = pi.dwProcessId; mainThreadId = pi.dwThreadId; #ifdef CPU_64 BOOL _64; win64 = IsWow64Process(hProcess, &_64) && !_64; LLOG("Win64 app: " << win64); disas.Mode64(win64); #else win64 = false; #endif if(win64) memory.SetTotal(I64(0xffffffffffff)); else memory.SetTotal(0x80000000); CloseHandle(pi.hThread); IdeSetRight(rpane); IdeSetBottom(*this); SyncTreeDisas(); LoadFromGlobal(*this, CONFIGNAME); if(!SymInitialize(hProcess, 0, FALSE)) { Error("Failed to load symbols"); return false; } SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_UNDNAME|SYMOPT_NO_UNQUALIFIED_LOADS); lock = 0; stop = false; refreshmodules = true; terminated = false; running = true; break_running = false; RunToException(); return !terminated; } INITBLOCK { RegisterWorkspaceConfig("pdb-debugger"); } void Pdb::SerializeSession(Stream& s) { int version = 0; s / version; int n = watches.GetCount(); s / n; if(n < 0) LoadingError(); 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); } } struct CpuRegisterDisplay : Display { virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { Font fnt = Courier(Ctrl::HorzLayoutZoom(12)); static int cx1 = GetTextSize("EFLAGS12", fnt().Bold()).cx + GetTextSize("0000 0000 0000 0000", fnt).cx; String name; String value; String odd; SplitTo((String)q, '|', name, value, odd); w.DrawRect(r, odd != "1" || (style & CURSOR) ? paper : Blend(SColorMark, SColorPaper, 220)); int i = value.GetLength() - 4; while(i > 0) { value.Insert(i, ' '); i -= 4; } Size tsz = GetTextSize(value, fnt); int tt = r.top + max((r.Height() - tsz.cy) / 2, 0); w.DrawText(r.left, tt, name, fnt().Bold(), ink); w.DrawText(r.left + cx1 - tsz.cx, tt, value, fnt, ink); } }; Pdb::Pdb() : visual_display(this) { hWnd = NULL; hProcess = INVALID_HANDLE_VALUE; current_frame.Clear(); autos.NoHeader(); autos.AddColumn("", 1); autos.AddColumn("", 6).SetDisplay(visual_display); autos.WhenEnterRow = THISBACK1(SetTreeA, &autos); autos.WhenBar = [=](Bar& bar) { DataMenu(autos, bar); }; autos.EvenRowColor(); autos.WhenLeftDouble << [=] { AddWatch(autos.GetKey()); }; locals.NoHeader(); locals.AddColumn("", 1); locals.AddColumn("", 6).SetDisplay(visual_display); locals.WhenEnterRow = THISBACK1(SetTreeA, &locals); locals.WhenBar = [=](Bar& bar) { DataMenu(locals, bar); }; locals.EvenRowColor(); locals.WhenLeftDouble << [=] { AddWatch(locals.GetKey()); }; self.NoHeader(); self.AddColumn("", 1); self.AddColumn("", 6).SetDisplay(visual_display); self.WhenEnterRow = THISBACK1(SetTreeA, &self); self.WhenBar = [=](Bar& bar) { DataMenu(self, bar); }; self.EvenRowColor(); self.WhenLeftDouble << [=] { AddWatch(self.GetKey()); }; watches.NoHeader(); watches.AddColumn("", 1).Edit(watchedit); watches.AddColumn("", 6).SetDisplay(visual_display); watches.Moving(); watches.WhenEnterRow = THISBACK1(SetTreeA, &watches); watches.WhenBar = THISBACK(WatchesMenu); watches.WhenAcceptEdit = THISBACK(Data); watches.WhenDrop = THISBACK(DropWatch); watches.EvenRowColor(); tab.Add(autos.SizePos(), "Autos"); tab.Add(locals.SizePos(), "Locals"); tab.Add(self.SizePos(), "this"); tab.Add(watches.SizePos(), "Watches"); tab.Add(cpu.SizePos(), "CPU"); tab.Add(memory.SizePos(), "Memory"); tab.Add(bts.SizePos(), "Threads"); cpu.Columns(4); cpu.ItemHeight(Courier(Ctrl::HorzLayoutZoom(12)).GetCy()); cpu.SetDisplay(Single()); memory.pdb = this; dlock = " Running.."; dlock.SetFrame(BlackFrame()); dlock.SetInk(Red); dlock.NoTransparent(); dlock.Hide(); framelist.Ctrl::Add(dlock.SizePos()); pane.Add(tab.SizePos()); pane.Add(threadlist.LeftPosZ(370, 60).TopPos(2, EditField::GetStdHeight())); int bcx = min(EditField::GetStdHeight(), DPI(16)); pane.Add(framelist.HSizePos(Zx(434), 2 * bcx).TopPos(2, EditField::GetStdHeight())); 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"); Add(pane.SizePos()); disas.WhenCursor = THISBACK(DisasCursor); disas.WhenFocus = THISBACK(DisasFocus); memory.WhenGoto = THISBACK(MemoryGoto); tab <<= THISBACK(Tab); framelist << [=] { SetFrame(); }; threadlist <<= THISBACK(SetThread); tree.WhenOpen = THISBACK(TreeExpand); tree.WhenBar = THISFN(TreeMenu); tree.WhenLeftDouble = THISFN(TreeWatch); rpane.Add(disas.SizePos()); rpane.Add(tree.SizePos()); FileIn in(ConfigFile("TreeTypes.txt")); while(!in.IsEof()) { String type = in.GetLine(); String desc = in.GetLine(); treetype.Add(type, desc); } StringStream ss(WorkspaceConfigData("pdb-debugger")); Load(callback(this, &Pdb::SerializeSession), ss); LoadPrettyScripts(); } String Pdb::CopyStack() { String s; for(int i = 0; i < framelist.GetCount(); i++) { s << framelist.GetValue(i); FilePos fp = GetFilePos(frame[i].pc); if(fp) s << " at " << fp.path << " " << fp.line + 1; s << "\n"; } return s; } String Pdb::CopyStackAll() { String s; for(int i = 0; i < threads.GetCount(); i++) { int thid = threads.GetKey(i); s << "----------------------------------\r\n" << "Thread ID: " << Format("0x%x", thid) << "\r\n\r\n"; for(const auto& f : Backtrace(threads[i])) s << f.text << "\r\n"; s << "\r\n"; } return s; } void Pdb::CopyDisas() { disas.WriteClipboard(); } void Pdb::CopyModules() { String s; for(const ModuleInfo& f : module) s << f.path << ", base 0x" << FormatIntHex((void *)f.base) << ", size: 0x" << FormatIntHex(f.size) << "\n"; WriteClipboardText(s); } void Pdb::Stop() { if(!terminated) { terminated = true; SaveTree(); String fn = ConfigFile("TreeTypes.txt"); FileOut out(fn); for(int i = 0; i < treetype.GetCount(); i++) out << treetype.GetKey(i) << "\r\n" << treetype[i] << "\r\n"; StringStream ss; Store(callback(this, &Pdb::SerializeSession), ss); WorkspaceConfigData("pdb-debugger") = ss; if(hProcess != INVALID_HANDLE_VALUE) { for(int i = 0; i < 10; i++) { if(DebugActiveProcessStop(processid)) break; Sleep(100); } TerminateChildProcesses(hProcessId, 0); TerminateProcess(hProcess, 0); dword exitcode = STILL_ACTIVE; int start = msecs(); while(GetExitCodeProcess(hProcess, &exitcode) && exitcode == STILL_ACTIVE && msecs(start) < 1000) Sleep(100); if(exitcode == STILL_ACTIVE) Exclamation("Unable to kill debugee. Please restart theide."); while(threads.GetCount()) RemoveThread(threads.GetKey(0)); // To CloseHandle UnloadModuleSymbols(); SymCleanup(hProcess); CloseHandle(hProcess); } StoreToGlobal(*this, CONFIGNAME); IdeRemoveBottom(*this); IdeRemoveRight(rpane); } } bool Pdb::IsFinished() { return terminated; } Pdb::~Pdb() { Stop(); } One PdbCreate(Host& host, const String& exefile, const String& cmdline, bool clang) { One dbg; if(!dbg.Create().Create(host, exefile, cmdline, clang)) dbg.Clear(); return dbg; } #define LAYOUTFILE #include #define TOPICFILE #include struct PDBExpressionDlg : WithEditPDBExpressionLayout { Pdb *pdb; void Sync(); typedef PDBExpressionDlg CLASSNAME; PDBExpressionDlg(const char *title, String& brk, Pdb *pdb); }; void PDBExpressionDlg::Sync() { if(pdb) value <<= RawPickToValue(pick(pdb->Visualise(~text))); } PDBExpressionDlg::PDBExpressionDlg(const char *title, String& brk, Pdb *pdb) : pdb(pdb) { CtrlLayoutOKCancel(*this, title); help.SetQTF(GetTopic("ide/Debuggers/app/PDBExpressions_en-us")); help.Background(White()); help.SetFrame(ViewFrame()); text <<= brk; text <<= THISBACK(Sync); value.SetDisplay(pdb->visual_display); value.Show(pdb); value_lbl.Show(pdb); Sync(); } bool EditPDBExpression(const char *title, String& brk, Pdb *pdb) { PDBExpressionDlg dlg(title, brk, pdb); if(dlg.Execute() != IDOK) return false; brk = ~dlg.text; return true; } #endif