Gdb.cpp
1 |
#include "Debuggers.h" |
---|---|
2 |
|
3 |
#define PROMPT "<\xcd\xeb>" |
4 |
|
5 |
bool Gdb::Result(String& result, const String& s) |
6 |
{ |
7 |
int l = result.GetLength();
|
8 |
result.Cat(s); |
9 |
const char *q = FindTag(~result + max(l - 5, 0), PROMPT); |
10 |
if(q) {
|
11 |
result.Trim((int)(q - ~result));
|
12 |
return true; |
13 |
} |
14 |
return false; |
15 |
} |
16 |
|
17 |
void Gdb::DebugBar(Bar& bar)
|
18 |
{ |
19 |
bar.Add("Stop debugging", DbgImg::StopDebug(), THISBACK(Stop))
|
20 |
.Key(K_SHIFT_F5); |
21 |
bar.Separator(); |
22 |
bool b = !IdeIsDebugLock();
|
23 |
bar.Add(b, "Step into", DbgImg::StepInto(), THISBACK1(Step, disas.HasFocus() ? "stepi" |
24 |
: "step"))
|
25 |
.Key(K_F11); |
26 |
bar.Add(b, "Step over", DbgImg::StepOver(), THISBACK1(Step, disas.HasFocus() ? "nexti" |
27 |
: "next"))
|
28 |
.Key(K_F10); |
29 |
bar.Add(b, "Step out", DbgImg::StepOut(), THISBACK1(Step, "finish")) |
30 |
.Key(K_SHIFT_F11); |
31 |
bar.Add(b, "Run to", DbgImg::RunTo(), THISBACK(DoRunTo))
|
32 |
.Key(K_CTRL_F10); |
33 |
bar.Add(b, "Run", DbgImg::Run(), THISBACK(Run))
|
34 |
.Key(K_F5); |
35 |
bar.MenuSeparator(); |
36 |
bar.Add(b, "Quick watch", THISBACK(QuickWatch))
|
37 |
.Key(K_CTRL_Q); |
38 |
bar.MenuSeparator(); |
39 |
bar.Add(b, "Copy backtrace", THISBACK(CopyStack));
|
40 |
bar.Add(b, "Copy dissassembly", THISBACK(CopyDisas));
|
41 |
} |
42 |
|
43 |
void Gdb::CopyStack()
|
44 |
{ |
45 |
DropFrames(); |
46 |
String s; |
47 |
for(int i = 0; i < frame.GetCount(); i++) |
48 |
s << frame.GetValue(i) << "\n";
|
49 |
WriteClipboardText(s); |
50 |
} |
51 |
|
52 |
void Gdb::CopyDisas()
|
53 |
{ |
54 |
disas.WriteClipboard(); |
55 |
} |
56 |
|
57 |
int CharFilterReSlash(int c) |
58 |
{ |
59 |
return c == '\\' ? '/' : c; |
60 |
} |
61 |
|
62 |
String Bpoint(Host& host, const String& file, int line) |
63 |
{ |
64 |
return String().Cat() << Filter(host.GetHostPath(NormalizePath(file)), CharFilterReSlash) << ":" << line + 1; |
65 |
} |
66 |
|
67 |
bool Gdb::TryBreak(const char *text) |
68 |
{ |
69 |
return FindTag(FastCmd(text), "Breakpoint"); |
70 |
} |
71 |
|
72 |
bool Gdb::SetBreakpoint(const String& filename, int line, const String& bp) |
73 |
{ |
74 |
String bi = Bpoint(*host, filename, line); |
75 |
if(bp.IsEmpty())
|
76 |
FastCmd("clear " + bi);
|
77 |
else if(bp[0]==0xe) |
78 |
FastCmd("b " + bi);
|
79 |
else
|
80 |
FastCmd("b " + bi + " if " + bp); |
81 |
return true; |
82 |
} |
83 |
void Gdb::SetDisas(const String& text) |
84 |
{ |
85 |
disas.Clear(); |
86 |
StringStream ss(text); |
87 |
while(!ss.IsEof()) {
|
88 |
String ln = ss.GetLine(); |
89 |
CParser p(ln); |
90 |
if(p.Char2('0', 'x')) { |
91 |
dword adr = p.IsNumber(16) ? p.ReadNumber(16) : 0; |
92 |
String code; |
93 |
String args; |
94 |
int level = 0; |
95 |
while(!p.IsEof()) {
|
96 |
if(p.Char(':') && level == 0) |
97 |
break;
|
98 |
else
|
99 |
if(p.Char('<')) |
100 |
level++; |
101 |
else
|
102 |
if(p.Char('>')) |
103 |
level--; |
104 |
else
|
105 |
p.GetChar(); |
106 |
} |
107 |
p.Spaces(); |
108 |
if(p.IsId()) {
|
109 |
code = p.ReadId(); |
110 |
for(;;) {
|
111 |
if(p.Spaces())
|
112 |
args.Cat(' ');
|
113 |
if(p.IsEof())
|
114 |
break;
|
115 |
if(p.Char2('0', 'x')) { |
116 |
dword adr = 0;
|
117 |
if(p.IsNumber(16)) |
118 |
adr = p.ReadNumber(16);
|
119 |
String fname; |
120 |
bool usefname = false; |
121 |
if(p.Char('<')) { |
122 |
const char *b = p.GetPtr(); |
123 |
int level = 1; |
124 |
usefname = true;
|
125 |
while(!p.IsEof()) {
|
126 |
if(p.Char('>') && --level == 0) { |
127 |
fname = String(b, p.GetPtr() - 1);
|
128 |
break;
|
129 |
} |
130 |
if(p.Char('<')) |
131 |
level++; |
132 |
if(p.Char('+')) |
133 |
usefname = false;
|
134 |
else {
|
135 |
p.GetChar(); |
136 |
p.Spaces(); |
137 |
} |
138 |
} |
139 |
} |
140 |
args << (usefname ? fname : Sprintf("0x%X", adr));
|
141 |
disas.AddT(adr); |
142 |
} |
143 |
else
|
144 |
if(p.Id("DWORD")) |
145 |
args.Cat("dword ");
|
146 |
else
|
147 |
if(p.Id("WORD")) |
148 |
args.Cat("word ");
|
149 |
else
|
150 |
if(p.Id("BYTE")) |
151 |
args.Cat("byte ");
|
152 |
else
|
153 |
if(p.Id("PTR")) |
154 |
args.Cat("ptr ");
|
155 |
else
|
156 |
args.Cat(p.GetChar()); |
157 |
} |
158 |
} |
159 |
disas.Add(adr, code, args); |
160 |
} |
161 |
} |
162 |
} |
163 |
|
164 |
void Gdb::SyncDisas(bool fr) |
165 |
{ |
166 |
if(!disas.IsVisible())
|
167 |
return;
|
168 |
if(!disas.InRange(addr))
|
169 |
SetDisas(FastCmd("disas"));
|
170 |
disas.SetCursor(addr); |
171 |
disas.SetIp(addr, fr ? DbgImg::FrameLinePtr() : DbgImg::IpLinePtr()); |
172 |
String s = FastCmd("info registers");
|
173 |
StringStream ss(s); |
174 |
for(int i = 0; i < reglbl.GetCount(); i++) |
175 |
reglbl[i]->SetInk(SColorText); |
176 |
while(!ss.IsEof()) {
|
177 |
String ln = ss.GetLine(); |
178 |
CParser p(ln); |
179 |
String name; |
180 |
if(p.IsId()) {
|
181 |
name = p.ReadId(); |
182 |
if(p.Char2('0', 'x')) { |
183 |
String n = Sprintf("%08X", p.ReadNumber(16)); |
184 |
for(int i = 0; i < reglbl.GetCount(); i++) { |
185 |
if(regname[i] == name) {
|
186 |
if(reglbl[i]->GetText() != n) {
|
187 |
reglbl[i]->SetLabel(n); |
188 |
reglbl[i]->SetInk(LtRed); |
189 |
} |
190 |
} |
191 |
} |
192 |
} |
193 |
} |
194 |
} |
195 |
} |
196 |
|
197 |
String FormatFrame(const char *s) |
198 |
{ |
199 |
if(*s++ != '#') |
200 |
return Null;
|
201 |
while(IsDigit(*s))
|
202 |
s++; |
203 |
while(*s == ' ') |
204 |
s++; |
205 |
if(s[0] == '0' && ToUpper(s[1]) == 'X') { |
206 |
s += 2;
|
207 |
while(IsXDigit(*s))
|
208 |
s++; |
209 |
while(*s == ' ') |
210 |
s++; |
211 |
if(s[0] != 'i' && s[1] != 'n') |
212 |
return Null;
|
213 |
s += 2;
|
214 |
while(*s == ' ') |
215 |
s++; |
216 |
} |
217 |
if(!IsAlpha(*s))
|
218 |
return Null;
|
219 |
const char *w = strchr(s, '\r'); |
220 |
if(w)
|
221 |
return String(s, w);
|
222 |
w = strchr(s, '\n');
|
223 |
if(w)
|
224 |
return String(s, w);
|
225 |
return s;
|
226 |
} |
227 |
|
228 |
bool ParsePos(const String& s, String& fn, int& line, adr_t & adr) |
229 |
{ |
230 |
const char *q = FindTag(s, "\x1a\x1a"); |
231 |
if(!q) return false; |
232 |
q += 2;
|
233 |
Vector<String> p = Split(q + 2, ':'); |
234 |
p.SetCount(5);
|
235 |
fn = String(q, q + 2) + p[0]; |
236 |
line = atoi(p[1]);
|
237 |
CParser pa(p[4]);
|
238 |
pa.Char2('0', 'x'); |
239 |
if(pa.IsNumber(16)) |
240 |
adr = pa.ReadNumber(16);
|
241 |
return true; |
242 |
} |
243 |
|
244 |
void Gdb::CheckEnd(const char *s) |
245 |
{ |
246 |
if(!dbg) {
|
247 |
Stop(); |
248 |
return;
|
249 |
} |
250 |
if(FindTag(s, "Program exited normally.")) { |
251 |
Stop(); |
252 |
return;
|
253 |
} |
254 |
const char *q = FindTag(s, "Program exited with code "); |
255 |
if(q) {
|
256 |
PutConsole(q); |
257 |
Stop(); |
258 |
return;
|
259 |
} |
260 |
} |
261 |
|
262 |
String Gdb::Cmdp(const char *cmdline, bool fr) |
263 |
{ |
264 |
String s = Cmd(cmdline); |
265 |
if(ParsePos(s, file, line, addr)) {
|
266 |
IdeSetDebugPos(GetLocalPath(file), line - 1, fr ? DbgImg::FrameLinePtr()
|
267 |
: DbgImg::IpLinePtr(), 0);
|
268 |
IdeSetDebugPos(GetLocalPath(file), line - 1,
|
269 |
disas.HasFocus() ? fr ? DbgImg::FrameLinePtr() : DbgImg::IpLinePtr() |
270 |
: Image(), 1);
|
271 |
SyncDisas(fr); |
272 |
autoline = IdeGetLine(line - 2) + ' ' + IdeGetLine(line - 1) + ' ' + IdeGetLine(line); |
273 |
} |
274 |
else {
|
275 |
file = Null; |
276 |
CParser pa(s); |
277 |
pa.Char2('0', 'x'); |
278 |
if(pa.IsNumber(16)) |
279 |
addr = pa.ReadNumber(16);
|
280 |
SyncDisas(fr); |
281 |
} |
282 |
frame.Clear(); |
283 |
frame.Add(0, FormatFrame(Cmd("frame"))); |
284 |
frame <<= 0;
|
285 |
Data(); |
286 |
return s;
|
287 |
} |
288 |
|
289 |
bool Gdb::RunTo()
|
290 |
{ |
291 |
String bi; |
292 |
bool df = disas.HasFocus();
|
293 |
if(df) {
|
294 |
if(!disas.GetCursor())
|
295 |
return false; |
296 |
bi = Sprintf("*0x%X", disas.GetCursor());
|
297 |
} |
298 |
else
|
299 |
bi = Bpoint(*host, IdeGetFileName(), IdeGetFileLine()); |
300 |
if(!TryBreak("b " + bi)) { |
301 |
Exclamation("No code at chosen location !");
|
302 |
return false; |
303 |
} |
304 |
String e = Cmdp(firstrun ? "run" : "continue"); |
305 |
firstrun = false;
|
306 |
FastCmd("clear " + bi);
|
307 |
if(df)
|
308 |
disas.SetFocus(); |
309 |
CheckEnd(e); |
310 |
IdeActivateBottom(); |
311 |
return true; |
312 |
} |
313 |
|
314 |
void Gdb::Run()
|
315 |
{ |
316 |
CheckEnd(Cmdp(firstrun ? "run" : "continue")); |
317 |
firstrun = false;
|
318 |
IdeActivateBottom(); |
319 |
} |
320 |
|
321 |
void Gdb::Step(const char *cmd) |
322 |
{ |
323 |
bool b = disas.HasFocus();
|
324 |
String s = Cmdp(cmd); |
325 |
if(b) disas.SetFocus();
|
326 |
CheckEnd(s); |
327 |
IdeActivateBottom(); |
328 |
} |
329 |
|
330 |
void Gdb::DisasCursor()
|
331 |
{ |
332 |
if(!disas.HasFocus())
|
333 |
return;
|
334 |
int line;
|
335 |
String file; |
336 |
adr_t addr; |
337 |
if(ParsePos(FastCmd(Sprintf("info line *0x%X", disas.GetCursor())), file, line, addr)) |
338 |
IdeSetDebugPos(file, line - 1, DbgImg::DisasPtr(), 1); |
339 |
disas.SetFocus(); |
340 |
} |
341 |
|
342 |
void Gdb::DisasFocus()
|
343 |
{ |
344 |
// if(!disas.HasFocus())
|
345 |
// IdeSetDebugPos(file, 0, Null, 1);
|
346 |
} |
347 |
|
348 |
void Gdb::DropFrames()
|
349 |
{ |
350 |
int i = 0; |
351 |
int q = ~frame;
|
352 |
frame.Clear(); |
353 |
for(;;) {
|
354 |
String s = FormatFrame(FastCmd(Sprintf("frame %d", i)));
|
355 |
if(IsNull(s)) break; |
356 |
frame.Add(i++, s); |
357 |
} |
358 |
frame <<= q; |
359 |
} |
360 |
|
361 |
void Gdb::ShowFrame()
|
362 |
{ |
363 |
int i = (int)~frame; |
364 |
Cmdp(Sprintf("frame %d", i), i);
|
365 |
} |
366 |
|
367 |
String DataClean(CParser& p) |
368 |
{ |
369 |
String r; |
370 |
if(p.Char('{')) { |
371 |
while(!p.IsEof() && !p.Char('}')) { |
372 |
p.Id("static");
|
373 |
if(p.Char('<')) { |
374 |
int level = 1; |
375 |
while(level > 0) |
376 |
if(p.Char('<')) |
377 |
level++; |
378 |
else
|
379 |
if(p.Char('>')) |
380 |
level--; |
381 |
else
|
382 |
p.GetChar(); |
383 |
p.Spaces(); |
384 |
p.Char('=');
|
385 |
String q = DataClean(p); |
386 |
if(!q.IsEmpty()) {
|
387 |
if(!r.IsEmpty())
|
388 |
r << ", ";
|
389 |
r << q; |
390 |
} |
391 |
} |
392 |
else
|
393 |
if(p.IsId()) {
|
394 |
String id = p.ReadId(); |
395 |
p.Char('=');
|
396 |
String q = DataClean(p); |
397 |
if(!q.IsEmpty()) {
|
398 |
if(!r.IsEmpty())
|
399 |
r << ", ";
|
400 |
r << id << "=" << q;
|
401 |
} |
402 |
} |
403 |
else {
|
404 |
p.GetChar(); |
405 |
p.Spaces(); |
406 |
} |
407 |
} |
408 |
if(!r.IsEmpty() && (*r != '{' || *r.Last() != '}')) |
409 |
|
410 |
return '{' + r + '}'; |
411 |
return r;
|
412 |
} |
413 |
p.Spaces(); |
414 |
for(;;) {
|
415 |
bool sp = p.Spaces();
|
416 |
if(p.IsChar('}') || p.IsChar(',') || p.IsEof()) |
417 |
break;
|
418 |
if(sp)
|
419 |
r << ' ';
|
420 |
if(p.IsString())
|
421 |
r << AsCString(p.ReadString()); |
422 |
else
|
423 |
r.Cat(p.GetChar()); |
424 |
} |
425 |
return r;
|
426 |
} |
427 |
|
428 |
String DataClean(const char *s) |
429 |
{ |
430 |
CParser p(s); |
431 |
return DataClean(p);
|
432 |
} |
433 |
|
434 |
void Gdb::Locals()
|
435 |
{ |
436 |
VectorMap<String, String> prev = DataMap(locals); |
437 |
locals.Clear(); |
438 |
String s = FastCmd("info locals");
|
439 |
StringStream ss(s); |
440 |
while(!ss.IsEof()) {
|
441 |
String ln = ss.GetLine(); |
442 |
const char *s = ln; |
443 |
const char *q = strchr(s, '='); |
444 |
if(!q) break; |
445 |
const char *e = q; |
446 |
while(e > s && e[-1] == ' ') |
447 |
e--; |
448 |
q++; |
449 |
while(*q == ' ') |
450 |
q++; |
451 |
locals.Add(String(s, e), DataClean(q)); |
452 |
} |
453 |
MarkChanged(prev, locals); |
454 |
} |
455 |
|
456 |
String Gdb::Print(const String& exp)
|
457 |
{ |
458 |
String q = FastCmd("print " + exp);
|
459 |
StringStream ss(q); |
460 |
String ln = ss.GetLine(); |
461 |
const char *s = strchr(ln, '='); |
462 |
if(s) {
|
463 |
s++; |
464 |
while(*s == ' ') |
465 |
s++; |
466 |
return DataClean(s);
|
467 |
} |
468 |
else
|
469 |
return DataClean(ln);
|
470 |
} |
471 |
|
472 |
void Gdb::Watches()
|
473 |
{ |
474 |
VectorMap<String, String> prev = DataMap(watches); |
475 |
for(int i = 0; i < watches.GetCount(); i++) |
476 |
watches.Set(i, 1, Print(watches.Get(i, 0))); |
477 |
MarkChanged(prev, watches); |
478 |
} |
479 |
|
480 |
void Gdb::Autos()
|
481 |
{ |
482 |
VectorMap<String, String> prev = DataMap(autos); |
483 |
autos.Clear(); |
484 |
CParser p(autoline); |
485 |
while(!p.IsEof()) {
|
486 |
if(p.IsId()) {
|
487 |
String exp = p.ReadId(); |
488 |
for(;;) {
|
489 |
if(p.Char('.') && p.IsId()) |
490 |
exp << '.';
|
491 |
else
|
492 |
if(p.Char2('-', '>') && p.IsId()) |
493 |
exp << "->";
|
494 |
else
|
495 |
break;
|
496 |
exp << p.ReadId(); |
497 |
} |
498 |
if(autos.Find(exp) < 0) { |
499 |
String val = Print(exp); |
500 |
if(!IsNull(val) && val.Find('(') < 0) |
501 |
autos.Add(exp, val); |
502 |
} |
503 |
} |
504 |
p.SkipTerm(); |
505 |
} |
506 |
autos.Sort(); |
507 |
MarkChanged(prev, autos); |
508 |
} |
509 |
|
510 |
void Gdb::QuickWatch()
|
511 |
{ |
512 |
for(;;) {
|
513 |
int q = quickwatch.Run();
|
514 |
if(q == IDCANCEL)
|
515 |
break;
|
516 |
FastCmd("set print pretty on");
|
517 |
String s = FastCmd("p " + (String)~quickwatch.expression);
|
518 |
const char *a = strchr(s, '='); |
519 |
if(a) {
|
520 |
a++; |
521 |
while(*a == ' ') |
522 |
a++; |
523 |
quickwatch.value <<= a; |
524 |
quickwatch.expression.AddHistory(); |
525 |
} |
526 |
else
|
527 |
quickwatch.value <<= s; |
528 |
FastCmd("set print pretty off");
|
529 |
} |
530 |
quickwatch.Close(); |
531 |
} |
532 |
|
533 |
void Gdb::Data()
|
534 |
{ |
535 |
switch(tab.Get()) {
|
536 |
case 0: Watches(); break; |
537 |
case 1: Locals(); break; |
538 |
case 2: Autos(); break; |
539 |
} |
540 |
} |
541 |
|
542 |
bool Gdb::Key(dword key, int count) |
543 |
{ |
544 |
if(key >= 32 && key < 65535 && tab.Get() == 2) { |
545 |
watches.DoInsertAfter(); |
546 |
Ctrl* f = GetFocusCtrl(); |
547 |
if(f && watches.HasChildDeep(f))
|
548 |
f->Key(key, count); |
549 |
return true; |
550 |
} |
551 |
return Ctrl::Key(key, count);
|
552 |
} |
553 |
|
554 |
bool Gdb::Create(One<Host> _host, const String& exefile, const String& cmdline) |
555 |
{ |
556 |
host = _host; |
557 |
dbg = host->StartProcess("gdb " + GetHostPath(exefile));
|
558 |
if(!dbg) {
|
559 |
Exclamation("Error invoking gdb !");
|
560 |
return false; |
561 |
} |
562 |
IdeSetBottom(*this);
|
563 |
IdeSetRight(disas); |
564 |
|
565 |
disas.AddFrame(regs); |
566 |
disas.WhenCursor = THISBACK(DisasCursor); |
567 |
disas.WhenFocus = THISBACK(DisasFocus); |
568 |
frame.WhenDrop = THISBACK(DropFrames); |
569 |
frame <<= THISBACK(ShowFrame); |
570 |
|
571 |
watches.WhenAcceptEdit = THISBACK(Data); |
572 |
tab <<= THISBACK(Data); |
573 |
|
574 |
Cmd("set prompt " PROMPT);
|
575 |
Cmd("set disassembly-flavor intel");
|
576 |
Cmd("set exec-done-display off");
|
577 |
Cmd("set annotate 1");
|
578 |
Cmd("set height 0");
|
579 |
Cmd("set width 0");
|
580 |
Cmd("set confirm off");
|
581 |
Cmd("set print asm-demangle");
|
582 |
|
583 |
if(!IsNull(cmdline))
|
584 |
Cmd("set args " + cmdline);
|
585 |
|
586 |
|
587 |
firstrun = true;
|
588 |
|
589 |
return true; |
590 |
} |
591 |
|
592 |
Gdb::~Gdb() |
593 |
{ |
594 |
IdeRemoveBottom(*this);
|
595 |
IdeRemoveRight(disas); |
596 |
} |
597 |
|
598 |
One<Debugger> GdbCreate(One<Host> host, const String& exefile, const String& cmdline) |
599 |
{ |
600 |
Gdb *dbg = new Gdb;
|
601 |
if(!dbg->Create(host, exefile, cmdline)) {
|
602 |
delete dbg;
|
603 |
return NULL; |
604 |
} |
605 |
return dbg;
|
606 |
} |