gdbcon.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  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. PGDBController=^TGDBController;
  23. TGDBController=object(TGDBInterface)
  24. progname,
  25. progdir,
  26. progargs : pchar;
  27. TBreakNumber,
  28. start_break_number,
  29. in_command,
  30. init_count : longint;
  31. constructor Init;
  32. destructor Done;
  33. procedure CommandBegin(const s:string);virtual;
  34. procedure Command(const s:string);
  35. procedure CommandEnd(const s:string);virtual;
  36. procedure Reset;virtual;
  37. { tracing }
  38. procedure StartTrace;
  39. procedure Run;virtual;
  40. procedure TraceStep;virtual;
  41. procedure TraceNext;virtual;
  42. procedure TraceStepI;virtual;
  43. procedure TraceNextI;virtual;
  44. procedure Continue;virtual;
  45. procedure UntilReturn;virtual;
  46. { registers }
  47. function GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  48. function GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  49. function GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  50. function GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  51. { breakpoints }
  52. function BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  53. function WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  54. function BreakpointDelete(BkptNo: LongInt): Boolean;
  55. function BreakpointEnable(BkptNo: LongInt): Boolean;
  56. function BreakpointDisable(BkptNo: LongInt): Boolean;
  57. function BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  58. function BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  59. procedure SetTBreak(tbreakstring : string);
  60. procedure Backtrace;
  61. { needed for dos because newlines are only #10 (PM) }
  62. procedure WriteErrorBuf;
  63. procedure WriteOutputBuf;
  64. function GetOutput : Pchar;
  65. function GetError : Pchar;
  66. function LoadFile(var fn:string):boolean;
  67. procedure SetDir(const s : string);
  68. procedure SetArgs(const s : string);
  69. procedure ClearSymbols;
  70. end;
  71. procedure UnixDir(var s : string);
  72. implementation
  73. uses
  74. {$ifdef win32}
  75. windows,
  76. {$endif win32}
  77. dos,
  78. strings;
  79. {$ifdef win32}
  80. const
  81. CygDrivePrefixKey1 = 'Software';
  82. CygDrivePrefixKey2 = 'Cygnus Solutions';
  83. CygDrivePrefixKey3 = 'Cygwin';
  84. CygDrivePrefixKey4 = 'mounts v2';
  85. CygDrivePrefixKey = 'cygdrive prefix';
  86. function CygDrivePrefix : string;
  87. var
  88. i : longint;
  89. length : dword;
  90. Value : pchar;
  91. _type : dword;
  92. Key,NKey : HKey;
  93. begin
  94. Length:=0;
  95. Key:=HKEY_CURRENT_USER;
  96. i := RegOpenKeyEx(Key, CygDrivePrefixKey1, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  97. if i=ERROR_SUCCESS then
  98. begin
  99. Key:=NKey;
  100. i := RegOpenKeyEx(Key, CygDrivePrefixKey2, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  101. end;
  102. if i=ERROR_SUCCESS then
  103. begin
  104. RegCloseKey(Key);
  105. Key:=NKey;
  106. i := RegOpenKeyEx(Key, CygDrivePrefixKey3, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  107. end;
  108. if i=ERROR_SUCCESS then
  109. begin
  110. RegCloseKey(Key);
  111. Key:=NKey;
  112. i := RegOpenKeyEx(Key, CygDrivePrefixKey4, 0, KEY_ENUMERATE_SUB_KEYS, @NKey);
  113. end;
  114. if i=ERROR_SUCCESS then
  115. begin
  116. RegCloseKey(Key);
  117. Key:=NKey;
  118. i := RegQueryValueEx( Key, CygDrivePrefixKey, nil, @_type, nil, @length);
  119. end;
  120. if i<>ERROR_SUCCESS then
  121. CygDrivePrefix:='/cygdrive'
  122. else
  123. Begin
  124. GetMem(Value,Length);
  125. i := RegQueryValueEx( Key, CygDrivePrefixKey, nil, @_type, LPByte(Value), @length);
  126. if i<>ERROR_SUCCESS then
  127. CygDrivePrefix:='/cygdrive'
  128. else
  129. CygDrivePrefix:=StrPas(Value);
  130. FreeMem(Value,Length);
  131. End;
  132. if Key<>HKEY_CURRENT_USER then
  133. RegCloseKey(Key);
  134. end;
  135. {$endif win32}
  136. procedure UnixDir(var s : string);
  137. var i : longint;
  138. begin
  139. for i:=1 to length(s) do
  140. if s[i]='\' then
  141. {$ifdef win32}
  142. { Don't touch at '\ ' used to escapes spaces in windows file names PM }
  143. if (i=length(s)) or (s[i+1]<>' ') then
  144. {$endif win32}
  145. s[i]:='/';
  146. {$ifdef win32}
  147. {$ifndef USE_MINGW_GDB}
  148. { for win32 we should convert e:\ into //e/ PM }
  149. if (length(s)>2) and (s[2]=':') and (s[3]='/') then
  150. s:=CygDrivePrefix+'/'+s[1]+copy(s,3,length(s));
  151. {$endif USE_MINGW_GDB}
  152. {$endif win32}
  153. end;
  154. constructor TGDBController.Init;
  155. begin
  156. inherited init;
  157. end;
  158. destructor TGDBController.Done;
  159. begin
  160. if assigned(progname) then
  161. strdispose(progname);
  162. if assigned(progdir) then
  163. strdispose(progdir);
  164. if assigned(progargs) then
  165. strdispose(progargs);
  166. inherited done;
  167. end;
  168. procedure TGDBController.Command(const s:string);
  169. begin
  170. inc(in_command);
  171. CommandBegin(s);
  172. gdboutputbuf.reset;
  173. gdberrorbuf.reset;
  174. gdb_command(s);
  175. {
  176. What is that for ?? PM
  177. I had to comment it because
  178. it resets the debuggere after each command !!
  179. Maybe it can happen on errors ??
  180. if in_command<0 then
  181. begin
  182. in_command:=0;
  183. inc(in_command);
  184. Reset;
  185. dec(in_command);
  186. end; }
  187. CommandEnd(s);
  188. dec(in_command);
  189. end;
  190. procedure TGDBController.CommandBegin(const s:string);
  191. begin
  192. end;
  193. procedure TGDBController.CommandEnd(const s:string);
  194. begin
  195. end;
  196. function TGDBController.LoadFile(var fn:string):boolean;
  197. var
  198. cmd : string;
  199. begin
  200. getdir(0,cmd);
  201. UnixDir(cmd);
  202. cmd:='cd '+cmd;
  203. Command(cmd);
  204. GDB__Init;
  205. UnixDir(fn);
  206. if assigned(progname) then
  207. strdispose(progname);
  208. getmem(progname,length(fn)+1);
  209. strpcopy(progname,fn);
  210. if fn<>'' then
  211. Command('file '+fn);
  212. LoadFile:=true;
  213. end;
  214. procedure TGDBController.SetDir(const s : string);
  215. var
  216. hs : string;
  217. begin
  218. hs:=s;
  219. UnixDir(hs);
  220. if assigned(progdir) then
  221. strdispose(progdir);
  222. getmem(progdir,length(hs)+1);
  223. strpcopy(progdir,hs);
  224. command('cd '+hs);
  225. end;
  226. procedure TGDBController.SetArgs(const s : string);
  227. begin
  228. if assigned(progargs) then
  229. strdispose(progargs);
  230. getmem(progargs,length(s)+1);
  231. strpcopy(progargs,s);
  232. command('set args '+s);
  233. end;
  234. procedure TGDBController.Reset;
  235. begin
  236. call_reset:=false;
  237. { DeleteBreakPoints(); }
  238. if debuggee_started then
  239. begin
  240. reset_command:=true;
  241. BreakSession;
  242. Command('kill');
  243. reset_command:=false;
  244. debuggee_started:=false;
  245. end;
  246. end;
  247. procedure TGDBController.StartTrace;
  248. begin
  249. Command('tbreak PASCALMAIN');
  250. start_break_number:=last_breakpoint_number;
  251. Run;
  252. end;
  253. procedure TGDBController.Run;
  254. begin
  255. Command('run');
  256. inc(init_count);
  257. end;
  258. procedure TGDBController.TraceStep;
  259. begin
  260. Command('step');
  261. end;
  262. procedure TGDBController.TraceNext;
  263. begin
  264. Command('next');
  265. end;
  266. procedure TGDBController.TraceStepI;
  267. begin
  268. Command('stepi');
  269. end;
  270. procedure TGDBController.TraceNextI;
  271. begin
  272. Command('nexti');
  273. end;
  274. procedure TGDBController.Continue;
  275. begin
  276. Command('continue');
  277. end;
  278. procedure TGDBController.UntilReturn;
  279. begin
  280. Command('finish');
  281. end;
  282. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  283. var
  284. RegValueStr: string;
  285. Code: LongInt;
  286. p, po, p1: PChar;
  287. buffer: array [0..255] of char;
  288. begin
  289. GetIntRegister := False;
  290. Value := 0;
  291. Command('info registers ' + RegName);
  292. if Error then
  293. exit;
  294. po:=StrNew(GetOutput);
  295. p:=po;
  296. if not assigned(p) then
  297. exit;
  298. p1:=strscan(p,' ');
  299. if not assigned(p1) then
  300. begin
  301. StrDispose(po);
  302. exit;
  303. end;
  304. p1:=strscan(p,'$');
  305. { some targets use 0x instead of $ }
  306. if p1=nil then
  307. p:=strpos(p,'0x')
  308. else
  309. p:=p1;
  310. p1:=strscan(p,#9);
  311. if p1=nil then
  312. begin
  313. StrDispose(po);
  314. exit;
  315. end;
  316. strlcopy(buffer,p,p1-p);
  317. RegValueStr:=strpas(buffer);
  318. StrDispose(po);
  319. { replace the $? }
  320. if copy(RegValueStr,1,2)='0x' then
  321. RegValueStr:='$'+copy(RegValueStr,3,length(RegValueStr)-2);
  322. Val(RegValueStr, Value, Code);
  323. if Code <> 0 then
  324. exit;
  325. GetIntRegister := True;
  326. end;
  327. function TGDBController.GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  328. var
  329. U64Value: UInt64;
  330. begin
  331. GetIntRegister := GetIntRegister(RegName, U64Value);
  332. Value := Int64(U64Value);
  333. end;
  334. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  335. var
  336. U64Value: UInt64;
  337. begin
  338. GetIntRegister := GetIntRegister(RegName, U64Value);
  339. Value := UInt32(U64Value);
  340. if (U64Value shr 32) <> 0 then
  341. GetIntRegister := False;
  342. end;
  343. function TGDBController.GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  344. var
  345. U32Value: UInt32;
  346. begin
  347. GetIntRegister := GetIntRegister(RegName, U32Value);
  348. Value := Int32(U32Value);
  349. end;
  350. function TGDBController.BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  351. var
  352. Prefix: string = '';
  353. begin
  354. if bfTemporary in BreakpointFlags then
  355. Prefix:=Prefix+'t';
  356. if bfHardware in BreakpointFlags then
  357. Prefix:=Prefix+'h';
  358. Last_breakpoint_number:=0;
  359. Command(Prefix+'break '+location);
  360. BreakpointInsert:=Last_breakpoint_number;
  361. end;
  362. function TGDBController.WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  363. begin
  364. Last_breakpoint_number:=0;
  365. case WatchpointType of
  366. wtWrite:
  367. Command('watch ' + location);
  368. wtReadWrite:
  369. Command('awatch ' + location);
  370. wtRead:
  371. Command('rwatch ' + location);
  372. end;
  373. WatchpointInsert:=Last_breakpoint_number;
  374. end;
  375. function TGDBController.BreakpointDelete(BkptNo: LongInt): Boolean;
  376. var
  377. BkptNoStr: string;
  378. begin
  379. Str(BkptNo, BkptNoStr);
  380. Command('delete ' + BkptNoStr);
  381. BreakpointDelete := not Error;
  382. end;
  383. function TGDBController.BreakpointEnable(BkptNo: LongInt): Boolean;
  384. var
  385. BkptNoStr: string;
  386. begin
  387. Str(BkptNo, BkptNoStr);
  388. Command('enable ' + BkptNoStr);
  389. BreakpointEnable := not Error;
  390. end;
  391. function TGDBController.BreakpointDisable(BkptNo: LongInt): Boolean;
  392. var
  393. BkptNoStr: string;
  394. begin
  395. Str(BkptNo, BkptNoStr);
  396. Command('disable ' + BkptNoStr);
  397. BreakpointDisable := not Error;
  398. end;
  399. function TGDBController.BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  400. var
  401. BkptNoStr: string;
  402. begin
  403. Str(BkptNo, BkptNoStr);
  404. Command('condition ' + BkptNoStr + ' ' + ConditionExpr);
  405. BreakpointCondition := not Error;
  406. end;
  407. function TGDBController.BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  408. var
  409. BkptNoStr, IgnoreCountStr: string;
  410. begin
  411. Str(BkptNo, BkptNoStr);
  412. Str(IgnoreCount, IgnoreCountStr);
  413. Command('ignore ' + BkptNoStr + ' ' + IgnoreCountStr);
  414. BreakpointSetIgnoreCount := not Error;
  415. end;
  416. procedure TGDBController.SetTBreak(tbreakstring : string);
  417. begin
  418. Last_breakpoint_number:=0;
  419. Command('tbreak '+tbreakstring);
  420. TBreakNumber:=Last_breakpoint_number;
  421. end;
  422. procedure TGDBController.Backtrace;
  423. begin
  424. { forget all old frames }
  425. clear_frames;
  426. Command('backtrace');
  427. end;
  428. procedure TGDBController.ClearSymbols;
  429. begin
  430. if debuggee_started then
  431. Reset;
  432. if init_count>0 then
  433. Command('file');
  434. end;
  435. procedure BufWrite(Buf : pchar);
  436. var p,pe : pchar;
  437. begin
  438. p:=buf;
  439. While assigned(p) do
  440. begin
  441. pe:=strscan(p,#10);
  442. if pe<>nil then
  443. pe^:=#0;
  444. Writeln(p);
  445. { restore for dispose }
  446. if pe<>nil then
  447. pe^:=#10;
  448. if pe=nil then
  449. p:=nil
  450. else
  451. begin
  452. p:=pe;
  453. inc(p);
  454. end;
  455. end;
  456. end;
  457. function TGDBController.GetOutput : Pchar;
  458. begin
  459. GetOutput:=gdboutputbuf.buf;
  460. end;
  461. function TGDBController.GetError : Pchar;
  462. var p : pchar;
  463. begin
  464. p:=gdberrorbuf.buf;
  465. if (p^=#0) and got_error then
  466. GetError:=pchar(ptrint(gdboutputbuf.buf)+gdboutputbuf.idx)
  467. else
  468. GetError:=p;
  469. end;
  470. procedure TGDBController.WriteErrorBuf;
  471. begin
  472. BufWrite(gdberrorbuf.buf);
  473. end;
  474. procedure TGDBController.WriteOutputBuf;
  475. begin
  476. BufWrite(gdboutputbuf.buf);
  477. end;
  478. end.