gdbcon.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. {
  2. Copyright (c) 1998 by Peter Vreman
  3. Lowlevel GDB interface which communicates directly with libgdb
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. unit GDBCon;
  11. {$ifdef USE_GDBLIBINC}
  12. {$i gdblib.inc}
  13. {$else not USE_GDBLIBINC}
  14. {$i gdbver.inc}
  15. {$endif not USE_GDBLIBINC}
  16. interface
  17. uses
  18. GDBInt;
  19. type
  20. TBreakpointFlags = set of (bfTemporary, bfHardware);
  21. TWatchpointType = (wtWrite, wtReadWrite, wtRead);
  22. TPrintFormatType = (pfbinary, pfdecimal, pfhexadecimal, pfoctal, pfnatural);
  23. PGDBController=^TGDBController;
  24. TGDBController=object(TGDBInterface)
  25. private
  26. SavedWindowWidth : longint;
  27. { width }
  28. procedure MaxWidth;
  29. procedure NormWidth;
  30. { print }
  31. function InternalGetValue(Const expr : string) : AnsiString;
  32. public
  33. progname,
  34. progdir,
  35. progargs : pchar;
  36. TBreakNumber,
  37. start_break_number,
  38. in_command,
  39. init_count : longint;
  40. constructor Init;
  41. destructor Done;
  42. procedure CommandBegin(const s:string);virtual;
  43. procedure Command(const s:string);
  44. procedure CommandEnd(const s:string);virtual;
  45. procedure Reset;virtual;
  46. { tracing }
  47. procedure StartTrace;
  48. procedure Run;virtual;
  49. procedure TraceStep;virtual;
  50. procedure TraceNext;virtual;
  51. procedure TraceStepI;virtual;
  52. procedure TraceNextI;virtual;
  53. procedure Continue;virtual;
  54. procedure UntilReturn;virtual;
  55. { registers }
  56. function GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  57. function GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  58. function GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  59. function GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  60. function GetIntRegister(const RegName: string; var Value: UInt16): Boolean;
  61. function GetIntRegister(const RegName: string; var Value: Int16): Boolean;
  62. { set command }
  63. function SetCommand(Const SetExpr : string) : boolean;
  64. { print }
  65. function PrintCommand(const expr : string): AnsiString;
  66. function PrintFormattedCommand(const expr : string; Format : TPrintFormatType): AnsiString;
  67. { breakpoints }
  68. function BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  69. function WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  70. function BreakpointDelete(BkptNo: LongInt): Boolean;
  71. function BreakpointEnable(BkptNo: LongInt): Boolean;
  72. function BreakpointDisable(BkptNo: LongInt): Boolean;
  73. function BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  74. function BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  75. procedure SetTBreak(tbreakstring : string);
  76. { frame commands }
  77. procedure Backtrace;
  78. function SelectFrameCommand(level :longint) : boolean;
  79. { needed for dos because newlines are only #10 (PM) }
  80. procedure WriteErrorBuf;
  81. procedure WriteOutputBuf;
  82. function GetOutput : Pchar;
  83. function GetError : Pchar;
  84. function LoadFile(var fn:string):boolean;
  85. procedure SetDir(const s : string);
  86. procedure SetArgs(const s : string);
  87. procedure ClearSymbols;
  88. end;
  89. procedure UnixDir(var s : string);
  90. implementation
  91. uses
  92. {$ifdef win32}
  93. windows,
  94. {$endif win32}
  95. dos,
  96. strings;
  97. {$ifdef win32}
  98. const
  99. CygDrivePrefixKey1 = 'Software';
  100. CygDrivePrefixKey2 = 'Cygnus Solutions';
  101. CygDrivePrefixKey3 = 'Cygwin';
  102. CygDrivePrefixKey4 = 'mounts v2';
  103. CygDrivePrefixKey = 'cygdrive prefix';
  104. function CygDrivePrefix : string;
  105. var
  106. i : longint;
  107. length : dword;
  108. Value : pchar;
  109. _type : dword;
  110. Key,NKey : HKey;
  111. begin
  112. Length:=0;
  113. Key:=HKEY_CURRENT_USER;
  114. i := RegOpenKeyEx(Key, CygDrivePrefixKey1, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  115. if i=ERROR_SUCCESS then
  116. begin
  117. Key:=NKey;
  118. i := RegOpenKeyEx(Key, CygDrivePrefixKey2, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  119. end;
  120. if i=ERROR_SUCCESS then
  121. begin
  122. RegCloseKey(Key);
  123. Key:=NKey;
  124. i := RegOpenKeyEx(Key, CygDrivePrefixKey3, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  125. end;
  126. if i=ERROR_SUCCESS then
  127. begin
  128. RegCloseKey(Key);
  129. Key:=NKey;
  130. i := RegOpenKeyEx(Key, CygDrivePrefixKey4, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  131. end;
  132. if i=ERROR_SUCCESS then
  133. begin
  134. RegCloseKey(Key);
  135. Key:=NKey;
  136. i := RegQueryValueEx( Key, CygDrivePrefixKey, nil, @_type, nil, @length);
  137. end;
  138. if i<>ERROR_SUCCESS then
  139. CygDrivePrefix:='/cygdrive'
  140. else
  141. Begin
  142. GetMem(Value,Length);
  143. i := RegQueryValueEx( Key, CygDrivePrefixKey, nil, @_type, LPByte(Value), @length);
  144. if i<>ERROR_SUCCESS then
  145. CygDrivePrefix:='/cygdrive'
  146. else
  147. CygDrivePrefix:=StrPas(Value);
  148. FreeMem(Value,Length);
  149. End;
  150. if Key<>HKEY_CURRENT_USER then
  151. RegCloseKey(Key);
  152. end;
  153. {$endif win32}
  154. procedure UnixDir(var s : string);
  155. var i : longint;
  156. begin
  157. for i:=1 to length(s) do
  158. if s[i]='\' then
  159. {$ifdef win32}
  160. { Don't touch at '\ ' used to escapes spaces in windows file names PM }
  161. if (i=length(s)) or (s[i+1]<>' ') then
  162. {$endif win32}
  163. s[i]:='/';
  164. {$ifdef win32}
  165. {$ifndef USE_MINGW_GDB}
  166. { for win32 we should convert e:\ into //e/ PM }
  167. if (length(s)>2) and (s[2]=':') and (s[3]='/') then
  168. s:=CygDrivePrefix+'/'+s[1]+copy(s,3,length(s));
  169. {$endif USE_MINGW_GDB}
  170. {$endif win32}
  171. end;
  172. constructor TGDBController.Init;
  173. begin
  174. inherited init;
  175. end;
  176. destructor TGDBController.Done;
  177. begin
  178. if assigned(progname) then
  179. strdispose(progname);
  180. if assigned(progdir) then
  181. strdispose(progdir);
  182. if assigned(progargs) then
  183. strdispose(progargs);
  184. inherited done;
  185. end;
  186. procedure TGDBController.Command(const s:string);
  187. begin
  188. inc(in_command);
  189. CommandBegin(s);
  190. gdboutputbuf.reset;
  191. gdberrorbuf.reset;
  192. gdb_command(s);
  193. {
  194. What is that for ?? PM
  195. I had to comment it because
  196. it resets the debuggere after each command !!
  197. Maybe it can happen on errors ??
  198. if in_command<0 then
  199. begin
  200. in_command:=0;
  201. inc(in_command);
  202. Reset;
  203. dec(in_command);
  204. end; }
  205. CommandEnd(s);
  206. dec(in_command);
  207. end;
  208. procedure TGDBController.CommandBegin(const s:string);
  209. begin
  210. end;
  211. procedure TGDBController.CommandEnd(const s:string);
  212. begin
  213. end;
  214. function TGDBController.LoadFile(var fn:string):boolean;
  215. var
  216. cmd : string;
  217. begin
  218. getdir(0,cmd);
  219. UnixDir(cmd);
  220. cmd:='cd '+cmd;
  221. Command(cmd);
  222. GDB__Init;
  223. UnixDir(fn);
  224. if assigned(progname) then
  225. strdispose(progname);
  226. getmem(progname,length(fn)+1);
  227. strpcopy(progname,fn);
  228. if fn<>'' then
  229. Command('file '+fn);
  230. LoadFile:=true;
  231. end;
  232. procedure TGDBController.SetDir(const s : string);
  233. var
  234. hs : string;
  235. begin
  236. hs:=s;
  237. UnixDir(hs);
  238. if assigned(progdir) then
  239. strdispose(progdir);
  240. getmem(progdir,length(hs)+1);
  241. strpcopy(progdir,hs);
  242. command('cd '+hs);
  243. end;
  244. procedure TGDBController.SetArgs(const s : string);
  245. begin
  246. if assigned(progargs) then
  247. strdispose(progargs);
  248. getmem(progargs,length(s)+1);
  249. strpcopy(progargs,s);
  250. command('set args '+s);
  251. end;
  252. procedure TGDBController.Reset;
  253. begin
  254. call_reset:=false;
  255. { DeleteBreakPoints(); }
  256. if debuggee_started then
  257. begin
  258. reset_command:=true;
  259. BreakSession;
  260. Command('kill');
  261. reset_command:=false;
  262. debuggee_started:=false;
  263. end;
  264. end;
  265. procedure TGDBController.StartTrace;
  266. begin
  267. Command('tbreak PASCALMAIN');
  268. start_break_number:=last_breakpoint_number;
  269. Run;
  270. end;
  271. procedure TGDBController.Run;
  272. begin
  273. Command('run');
  274. inc(init_count);
  275. end;
  276. procedure TGDBController.TraceStep;
  277. begin
  278. Command('step');
  279. end;
  280. procedure TGDBController.TraceNext;
  281. begin
  282. Command('next');
  283. end;
  284. procedure TGDBController.TraceStepI;
  285. begin
  286. Command('stepi');
  287. end;
  288. procedure TGDBController.TraceNextI;
  289. begin
  290. Command('nexti');
  291. end;
  292. procedure TGDBController.Continue;
  293. begin
  294. Command('continue');
  295. end;
  296. procedure TGDBController.UntilReturn;
  297. begin
  298. Command('finish');
  299. end;
  300. { Register functions }
  301. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  302. var
  303. RegValueStr: string;
  304. Code: LongInt;
  305. p, po, p1: PChar;
  306. buffer: array [0..255] of char;
  307. begin
  308. GetIntRegister := False;
  309. Value := 0;
  310. Command('info registers ' + RegName);
  311. if Error then
  312. exit;
  313. po:=StrNew(GetOutput);
  314. p:=po;
  315. if not assigned(p) then
  316. exit;
  317. p1:=strscan(p,' ');
  318. if not assigned(p1) then
  319. begin
  320. StrDispose(po);
  321. exit;
  322. end;
  323. p1:=strscan(p,'$');
  324. { some targets use 0x instead of $ }
  325. if p1=nil then
  326. p:=strpos(p,'0x')
  327. else
  328. p:=p1;
  329. p1:=strscan(p,#9);
  330. if p1=nil then
  331. begin
  332. StrDispose(po);
  333. exit;
  334. end;
  335. strlcopy(buffer,p,p1-p);
  336. RegValueStr:=strpas(buffer);
  337. StrDispose(po);
  338. { replace the $? }
  339. if copy(RegValueStr,1,2)='0x' then
  340. RegValueStr:='$'+copy(RegValueStr,3,length(RegValueStr)-2);
  341. Val(RegValueStr, Value, Code);
  342. if Code <> 0 then
  343. exit;
  344. GetIntRegister := True;
  345. end;
  346. function TGDBController.GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  347. var
  348. U64Value: UInt64;
  349. begin
  350. GetIntRegister := GetIntRegister(RegName, U64Value);
  351. Value := Int64(U64Value);
  352. end;
  353. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  354. var
  355. U64Value: UInt64;
  356. begin
  357. GetIntRegister := GetIntRegister(RegName, U64Value);
  358. Value := UInt32(U64Value);
  359. if (U64Value shr 32) <> 0 then
  360. GetIntRegister := False;
  361. end;
  362. function TGDBController.GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  363. var
  364. U32Value: UInt32;
  365. begin
  366. GetIntRegister := GetIntRegister(RegName, U32Value);
  367. Value := Int32(U32Value);
  368. end;
  369. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt16): Boolean;
  370. var
  371. U64Value: UInt64;
  372. begin
  373. GetIntRegister := GetIntRegister(RegName, U64Value);
  374. Value := UInt16(U64Value);
  375. if (U64Value shr 16) <> 0 then
  376. GetIntRegister := False;
  377. end;
  378. function TGDBController.GetIntRegister(const RegName: string; var Value: Int16): Boolean;
  379. var
  380. U16Value: UInt16;
  381. begin
  382. GetIntRegister := GetIntRegister(RegName, U16Value);
  383. Value := Int16(U16Value);
  384. end;
  385. { set command }
  386. function TGDBController.SetCommand(Const SetExpr : string) : boolean;
  387. begin
  388. SetCommand:=false;
  389. Command('set '+SetExpr);
  390. if error then
  391. exit;
  392. SetCommand:=true;
  393. end;
  394. { width }
  395. procedure TGDBController.MaxWidth;
  396. var
  397. p,p2,p3 : pchar;
  398. begin
  399. Command('show width');
  400. p:=GetOutput;
  401. p3:=nil;
  402. if assigned(p) and (p[strlen(p)-1]=#10) then
  403. begin
  404. p3:=p+strlen(p)-1;
  405. p3^:=#0;
  406. end;
  407. if assigned(p) then
  408. p2:=strpos(p,' in a line is ')
  409. else
  410. p2:=nil;
  411. if assigned(p2) then
  412. p:=p2+length(' in a line is ');
  413. while p^ in [' ',#9] do
  414. inc(p);
  415. p3:=strpos(p,'.');
  416. if assigned(p3) then
  417. p3^:=#0;
  418. SavedWindowWidth:=-1;
  419. val(strpas(p),SavedWindowWidth);
  420. if SavedWindowWidth<>-1 then
  421. Command('set width 0xffffffff');
  422. end;
  423. procedure TGDBController.NormWidth;
  424. var
  425. st : string;
  426. saved_got_error : boolean;
  427. begin
  428. saved_got_error:=got_error;
  429. if SavedWindowWidth<>-1 then
  430. begin
  431. str(SavedWindowWidth,st);
  432. Command('set width '+St);
  433. end;
  434. got_error:=saved_got_error;
  435. end;
  436. { print }
  437. function TrimEnd(s: AnsiString): AnsiString;
  438. var
  439. I: LongInt;
  440. begin
  441. if (s<>'') and (s[Length(s)]=#10) then
  442. begin
  443. I:=Length(s);
  444. while (i>1) and ((s[i-1]=' ') or (s[i-1]=#9)) do
  445. dec(i);
  446. delete(s,i,Length(s)-i+1);
  447. end;
  448. TrimEnd:=s;
  449. end;
  450. function TGDBController.InternalGetValue(Const expr : string) : AnsiString;
  451. var
  452. p,p2 : pchar;
  453. begin
  454. MaxWidth;
  455. Command('p '+expr);
  456. p:=GetOutput;
  457. if assigned(p) then
  458. p2:=strpos(p,'=')
  459. else
  460. p2:=nil;
  461. if assigned(p2) then
  462. p:=p2+1;
  463. while p^ in [' ',#9] do
  464. inc(p);
  465. { get rid of type }
  466. if p^ = '(' then
  467. p:=strpos(p,')')+1;
  468. while p^ in [' ',#9] do
  469. inc(p);
  470. if assigned(p) and not got_error then
  471. InternalGetValue:=TrimEnd(AnsiString(p))
  472. else
  473. InternalGetValue:=TrimEnd(AnsiString(GetError));
  474. NormWidth;
  475. end;
  476. function TGDBController.PrintCommand(const expr : string): AnsiString;
  477. begin
  478. PrintCommand:=InternalGetValue(expr);
  479. end;
  480. const
  481. PrintFormatName : Array[TPrintFormatType] of string[11] =
  482. (' /b ', ' /d ', ' /x ', ' /o ', '');
  483. function TGDBController.PrintFormattedCommand(const expr : string; Format : TPrintFormatType): AnsiString;
  484. begin
  485. PrintFormattedCommand:=InternalGetValue(PrintFormatName[Format]+expr);
  486. end;
  487. function TGDBController.BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  488. var
  489. Prefix: string = '';
  490. begin
  491. if bfTemporary in BreakpointFlags then
  492. Prefix:=Prefix+'t';
  493. if bfHardware in BreakpointFlags then
  494. Prefix:=Prefix+'h';
  495. Last_breakpoint_number:=0;
  496. Command(Prefix+'break '+location);
  497. BreakpointInsert:=Last_breakpoint_number;
  498. end;
  499. function TGDBController.WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  500. begin
  501. Last_breakpoint_number:=0;
  502. case WatchpointType of
  503. wtWrite:
  504. Command('watch ' + location);
  505. wtReadWrite:
  506. Command('awatch ' + location);
  507. wtRead:
  508. Command('rwatch ' + location);
  509. end;
  510. WatchpointInsert:=Last_breakpoint_number;
  511. end;
  512. function TGDBController.BreakpointDelete(BkptNo: LongInt): Boolean;
  513. var
  514. BkptNoStr: string;
  515. begin
  516. Str(BkptNo, BkptNoStr);
  517. Command('delete ' + BkptNoStr);
  518. BreakpointDelete := not Error;
  519. end;
  520. function TGDBController.BreakpointEnable(BkptNo: LongInt): Boolean;
  521. var
  522. BkptNoStr: string;
  523. begin
  524. Str(BkptNo, BkptNoStr);
  525. Command('enable ' + BkptNoStr);
  526. BreakpointEnable := not Error;
  527. end;
  528. function TGDBController.BreakpointDisable(BkptNo: LongInt): Boolean;
  529. var
  530. BkptNoStr: string;
  531. begin
  532. Str(BkptNo, BkptNoStr);
  533. Command('disable ' + BkptNoStr);
  534. BreakpointDisable := not Error;
  535. end;
  536. function TGDBController.BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  537. var
  538. BkptNoStr: string;
  539. begin
  540. Str(BkptNo, BkptNoStr);
  541. Command('condition ' + BkptNoStr + ' ' + ConditionExpr);
  542. BreakpointCondition := not Error;
  543. end;
  544. function TGDBController.BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  545. var
  546. BkptNoStr, IgnoreCountStr: string;
  547. begin
  548. Str(BkptNo, BkptNoStr);
  549. Str(IgnoreCount, IgnoreCountStr);
  550. Command('ignore ' + BkptNoStr + ' ' + IgnoreCountStr);
  551. BreakpointSetIgnoreCount := not Error;
  552. end;
  553. procedure TGDBController.SetTBreak(tbreakstring : string);
  554. begin
  555. Last_breakpoint_number:=0;
  556. Command('tbreak '+tbreakstring);
  557. TBreakNumber:=Last_breakpoint_number;
  558. end;
  559. procedure TGDBController.Backtrace;
  560. begin
  561. { forget all old frames }
  562. clear_frames;
  563. MaxWidth;
  564. Command('backtrace');
  565. NormWidth;
  566. end;
  567. function TGDBController.SelectFrameCommand(level :longint) : boolean;
  568. var
  569. LevelStr : String;
  570. begin
  571. Str(Level, LevelStr);
  572. Command('frame '+LevelStr);
  573. SelectFrameCommand:=not error;
  574. end;
  575. procedure TGDBController.ClearSymbols;
  576. begin
  577. if debuggee_started then
  578. Reset;
  579. if init_count>0 then
  580. Command('file');
  581. end;
  582. procedure BufWrite(Buf : pchar);
  583. var p,pe : pchar;
  584. begin
  585. p:=buf;
  586. While assigned(p) do
  587. begin
  588. pe:=strscan(p,#10);
  589. if pe<>nil then
  590. pe^:=#0;
  591. Writeln(p);
  592. { restore for dispose }
  593. if pe<>nil then
  594. pe^:=#10;
  595. if pe=nil then
  596. p:=nil
  597. else
  598. begin
  599. p:=pe;
  600. inc(p);
  601. end;
  602. end;
  603. end;
  604. function TGDBController.GetOutput : Pchar;
  605. begin
  606. GetOutput:=gdboutputbuf.buf;
  607. end;
  608. function TGDBController.GetError : Pchar;
  609. var p : pchar;
  610. begin
  611. p:=gdberrorbuf.buf;
  612. if (p^=#0) and got_error then
  613. GetError:=pchar(ptrint(gdboutputbuf.buf)+gdboutputbuf.idx)
  614. else
  615. GetError:=p;
  616. end;
  617. procedure TGDBController.WriteErrorBuf;
  618. begin
  619. BufWrite(gdberrorbuf.buf);
  620. end;
  621. procedure TGDBController.WriteOutputBuf;
  622. begin
  623. BufWrite(gdboutputbuf.buf);
  624. end;
  625. end.