gdbmicon.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. {
  2. Copyright (c) 2015 by Nikolay Nikolov
  3. Copyright (c) 1998 by Peter Vreman
  4. This is a replacement for GDBCon, implemented on top of GDB/MI,
  5. instead of LibGDB. This allows integration of GDB/MI support in the
  6. text mode IDE.
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit gdbmicon;
  14. {$MODE fpc}{$H-}
  15. {$I globdir.inc}
  16. interface
  17. uses
  18. gdbmiint, gdbmiwrap;
  19. type
  20. TBreakpointFlags = set of (bfTemporary, bfHardware);
  21. TWatchpointType = (wtWrite, wtReadWrite, wtRead);
  22. TPrintFormatType = (pfbinary, pfdecimal, pfhexadecimal, pfoctal, pfnatural);
  23. TGDBController = object(TGDBInterface)
  24. private
  25. FRegisterNames: array of AnsiString;
  26. procedure UpdateRegisterNames;
  27. function GetGdbRegisterNo(const RegName: string): LongInt;
  28. function GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
  29. procedure RunExecCommand(const Cmd: string);
  30. protected
  31. TBreakNumber,
  32. start_break_number: LongInt;
  33. in_command: LongInt;
  34. procedure CommandBegin(const s: string); virtual;
  35. procedure CommandEnd(const s: string); virtual;
  36. public
  37. constructor Init;
  38. destructor Done;
  39. procedure Command(const s: string);
  40. procedure Reset; virtual;
  41. { tracing }
  42. procedure StartTrace;
  43. procedure Run; virtual;
  44. procedure TraceStep;
  45. procedure TraceNext;
  46. procedure TraceStepI;
  47. procedure TraceNextI;
  48. procedure Continue; virtual;
  49. procedure UntilReturn; virtual;
  50. { registers }
  51. function GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  52. function GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  53. function GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  54. function GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  55. function GetIntRegister(const RegName: string; var Value: UInt16): Boolean;
  56. function GetIntRegister(const RegName: string; var Value: Int16): Boolean;
  57. { set command }
  58. function SetCommand(Const SetExpr : string) : boolean;
  59. { print }
  60. function PrintCommand(const expr : string): AnsiString;
  61. function PrintFormattedCommand(const expr : string; Format : TPrintFormatType): AnsiString;
  62. { breakpoints }
  63. function BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  64. function WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  65. function BreakpointDelete(BkptNo: LongInt): Boolean;
  66. function BreakpointEnable(BkptNo: LongInt): Boolean;
  67. function BreakpointDisable(BkptNo: LongInt): Boolean;
  68. function BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  69. function BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  70. procedure SetTBreak(tbreakstring : string);
  71. { frame commands }
  72. procedure Backtrace;
  73. function SelectFrameCommand(level :longint) : boolean;
  74. function LoadFile(var fn: string): Boolean;
  75. procedure SetDir(const s: string);
  76. procedure SetArgs(const s: string);
  77. end;
  78. implementation
  79. uses
  80. {$ifdef Windows}
  81. Windebug,
  82. {$endif Windows}
  83. strings;
  84. procedure UnixDir(var s : string);
  85. var i : longint;
  86. begin
  87. for i:=1 to length(s) do
  88. if s[i]='\' then
  89. {$ifdef windows}
  90. { Don't touch at '\ ' used to escapes spaces in windows file names PM }
  91. if (i=length(s)) or (s[i+1]<>' ') then
  92. {$endif windows}
  93. s[i]:='/';
  94. {$ifdef windows}
  95. { if we are using cygwin, we need to convert e:\ into /cygdriveprefix/e/ PM }
  96. if using_cygwin_gdb and (length(s)>2) and (s[2]=':') and (s[3]='/') then
  97. s:=CygDrivePrefix+'/'+s[1]+copy(s,3,length(s));
  98. {$endif windows}
  99. end;
  100. constructor TGDBController.Init;
  101. begin
  102. inherited Init;
  103. end;
  104. destructor TGDBController.Done;
  105. begin
  106. inherited Done;
  107. end;
  108. procedure TGDBController.CommandBegin(const s: string);
  109. begin
  110. end;
  111. procedure TGDBController.Command(const s: string);
  112. begin
  113. Inc(in_command);
  114. CommandBegin(s);
  115. GDBOutputBuf.Reset;
  116. GDBErrorBuf.Reset;
  117. {$ifdef GDB_RAW_OUTPUT}
  118. GDBRawBuf.reset;
  119. {$endif GDB_RAW_OUTPUT}
  120. i_gdb_command(s);
  121. CommandEnd(s);
  122. Dec(in_command);
  123. end;
  124. procedure TGDBController.CommandEnd(const s: string);
  125. begin
  126. end;
  127. procedure TGDBController.UpdateRegisterNames;
  128. var
  129. I: LongInt;
  130. ResultList: TGDBMI_ListValue;
  131. begin
  132. SetLength(FRegisterNames, 0);
  133. Command('-data-list-register-names');
  134. if not GDB.ResultRecord.Success then
  135. exit;
  136. ResultList := GDB.ResultRecord.Parameters['register-names'].AsList;
  137. SetLength(FRegisterNames, ResultList.Count);
  138. for I := 0 to ResultList.Count - 1 do
  139. FRegisterNames[I] := ResultList.ValueAt[I].AsString;
  140. end;
  141. function TGDBController.GetGdbRegisterNo(const RegName: string): LongInt;
  142. var
  143. I: LongInt;
  144. begin
  145. for I := Low(FRegisterNames) to High(FRegisterNames) do
  146. if FRegisterNames[I] = RegName then
  147. begin
  148. GetGdbRegisterNo := I;
  149. exit;
  150. end;
  151. GetGdbRegisterNo := -1;
  152. end;
  153. procedure TGDBController.Reset;
  154. begin
  155. end;
  156. procedure TGDBController.StartTrace;
  157. begin
  158. Command('-break-insert -t PASCALMAIN');
  159. if not GDB.ResultRecord.Success then
  160. exit;
  161. start_break_number := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  162. Run;
  163. end;
  164. procedure TGDBController.RunExecCommand(const Cmd: string);
  165. begin
  166. UserScreen;
  167. Command(Cmd);
  168. if not GDB.ResultRecord.Success then
  169. begin
  170. DebuggerScreen;
  171. got_error := True;
  172. exit;
  173. end;
  174. WaitForProgramStop;
  175. end;
  176. procedure TGDBController.Run;
  177. begin
  178. RunExecCommand('-exec-run');
  179. end;
  180. procedure TGDBController.TraceStep;
  181. begin
  182. RunExecCommand('-exec-step');
  183. end;
  184. procedure TGDBController.TraceNext;
  185. begin
  186. RunExecCommand('-exec-next');
  187. end;
  188. procedure TGDBController.TraceStepI;
  189. begin
  190. RunExecCommand('-exec-step-instruction');
  191. end;
  192. procedure TGDBController.TraceNextI;
  193. begin
  194. RunExecCommand('-exec-next-instruction');
  195. end;
  196. procedure TGDBController.Continue;
  197. begin
  198. RunExecCommand('-exec-continue');
  199. end;
  200. procedure TGDBController.UntilReturn;
  201. begin
  202. RunExecCommand('-exec-finish');
  203. end;
  204. function TGDBController.GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
  205. var
  206. RegNo: LongInt;
  207. RegNoStr: string;
  208. begin
  209. GetRegisterAsString := False;
  210. Value := '';
  211. RegNo := GetGdbRegisterNo(RegName);
  212. if RegNo = -1 then
  213. exit;
  214. Str(RegNo, RegNoStr);
  215. Command('-data-list-register-values ' + Format + ' ' + RegNoStr);
  216. if not GDB.ResultRecord.Success then
  217. exit;
  218. Value := GDB.ResultRecord.Parameters['register-values'].AsList.ValueAt[0].AsTuple['value'].AsString;
  219. GetRegisterAsString := True;
  220. end;
  221. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  222. var
  223. RegValueStr: string;
  224. Code: LongInt;
  225. begin
  226. GetIntRegister := False;
  227. Value := 0;
  228. if not GetRegisterAsString(RegName, 'x', RegValueStr) then
  229. exit;
  230. Val(RegValueStr, Value, Code);
  231. if Code <> 0 then
  232. exit;
  233. GetIntRegister := True;
  234. end;
  235. function TGDBController.GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  236. var
  237. U64Value: UInt64;
  238. begin
  239. GetIntRegister := GetIntRegister(RegName, U64Value);
  240. Value := Int64(U64Value);
  241. end;
  242. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  243. var
  244. U64Value: UInt64;
  245. begin
  246. GetIntRegister := GetIntRegister(RegName, U64Value);
  247. Value := UInt32(U64Value);
  248. if (U64Value shr 32) <> 0 then
  249. GetIntRegister := False;
  250. end;
  251. function TGDBController.GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  252. var
  253. U32Value: UInt32;
  254. begin
  255. GetIntRegister := GetIntRegister(RegName, U32Value);
  256. Value := Int32(U32Value);
  257. end;
  258. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt16): Boolean;
  259. var
  260. U64Value: UInt64;
  261. begin
  262. GetIntRegister := GetIntRegister(RegName, U64Value);
  263. Value := UInt16(U64Value);
  264. if (U64Value shr 16) <> 0 then
  265. GetIntRegister := False;
  266. end;
  267. function TGDBController.GetIntRegister(const RegName: string; var Value: Int16): Boolean;
  268. var
  269. U16Value: UInt16;
  270. begin
  271. GetIntRegister := GetIntRegister(RegName, U16Value);
  272. Value := Int16(U16Value);
  273. end;
  274. { set command }
  275. function TGDBController.SetCommand(Const SetExpr : string) : boolean;
  276. begin
  277. SetCommand:=false;
  278. Command('-gdb-set '+SetExpr);
  279. if error then
  280. exit;
  281. SetCommand:=true;
  282. end;
  283. { print }
  284. function TGDBController.PrintCommand(const expr : string): AnsiString;
  285. begin
  286. Command('-data-evaluate-expression '+QuoteString(expr));
  287. if GDB.ResultRecord.Success then
  288. PrintCommand:=GDB.ResultRecord.Parameters['value'].AsString
  289. else
  290. PrintCommand:=AnsiString(GetError);
  291. end;
  292. const
  293. PrintFormatName : Array[TPrintFormatType] of string[11] =
  294. ('binary', 'decimal', 'hexadecimal', 'octal', 'natural');
  295. function TGDBController.PrintFormattedCommand(const expr : string; Format : TPrintFormatType): ansistring;
  296. begin
  297. Command('-var-evaluate-expression -f '+PrintFormatName[Format]+' '+QuoteString(expr));
  298. if GDB.ResultRecord.Success then
  299. PrintFormattedCommand:=GDB.ResultRecord.Parameters['value'].AsString
  300. else
  301. PrintFormattedCommand:=AnsiString(GetError);
  302. end;
  303. function TGDBController.BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  304. var
  305. Options: string = '';
  306. begin
  307. if bfTemporary in BreakpointFlags then
  308. Options := Options + '-t ';
  309. if bfHardware in BreakpointFlags then
  310. Options := Options + '-h ';
  311. Command('-break-insert ' + Options + location);
  312. if GDB.ResultRecord.Success then
  313. BreakpointInsert := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt
  314. else
  315. BreakpointInsert := 0;
  316. end;
  317. function TGDBController.WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  318. begin
  319. case WatchpointType of
  320. wtWrite:
  321. Command('-break-watch ' + location);
  322. wtReadWrite:
  323. Command('-break-watch -a ' + location);
  324. wtRead:
  325. Command('-break-watch -r ' + location);
  326. end;
  327. if GDB.ResultRecord.Success then
  328. case WatchpointType of
  329. wtWrite:
  330. WatchpointInsert := GDB.ResultRecord.Parameters['wpt'].AsTuple['number'].AsLongInt;
  331. wtReadWrite:
  332. WatchpointInsert := GDB.ResultRecord.Parameters['hw-awpt'].AsTuple['number'].AsLongInt;
  333. wtRead:
  334. WatchpointInsert := GDB.ResultRecord.Parameters['hw-rwpt'].AsTuple['number'].AsLongInt;
  335. end
  336. else
  337. WatchpointInsert := 0;
  338. end;
  339. function TGDBController.BreakpointDelete(BkptNo: LongInt): Boolean;
  340. var
  341. BkptNoStr: string;
  342. begin
  343. Str(BkptNo, BkptNoStr);
  344. Command('-break-delete ' + BkptNoStr);
  345. BreakpointDelete := GDB.ResultRecord.Success;
  346. end;
  347. function TGDBController.BreakpointEnable(BkptNo: LongInt): Boolean;
  348. var
  349. BkptNoStr: string;
  350. begin
  351. Str(BkptNo, BkptNoStr);
  352. Command('-break-enable ' + BkptNoStr);
  353. BreakpointEnable := GDB.ResultRecord.Success;
  354. end;
  355. function TGDBController.BreakpointDisable(BkptNo: LongInt): Boolean;
  356. var
  357. BkptNoStr: string;
  358. begin
  359. Str(BkptNo, BkptNoStr);
  360. Command('-break-disable ' + BkptNoStr);
  361. BreakpointDisable := GDB.ResultRecord.Success;
  362. end;
  363. function TGDBController.BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  364. var
  365. BkptNoStr: string;
  366. begin
  367. Str(BkptNo, BkptNoStr);
  368. Command('-break-condition ' + BkptNoStr + ' ' + ConditionExpr);
  369. BreakpointCondition := GDB.ResultRecord.Success;
  370. end;
  371. function TGDBController.BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  372. var
  373. BkptNoStr, IgnoreCountStr: string;
  374. begin
  375. Str(BkptNo, BkptNoStr);
  376. Str(IgnoreCount, IgnoreCountStr);
  377. Command('-break-after ' + BkptNoStr + ' ' + IgnoreCountStr);
  378. BreakpointSetIgnoreCount := GDB.ResultRecord.Success;
  379. end;
  380. procedure TGDBController.SetTBreak(tbreakstring : string);
  381. begin
  382. Command('-break-insert -t ' + tbreakstring);
  383. TBreakNumber := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  384. end;
  385. procedure TGDBController.Backtrace;
  386. var
  387. FrameList,FrameArgList,ArgList: TGDBMI_ListValue;
  388. I,J,arg_count: LongInt;
  389. s : ansistring;
  390. begin
  391. { forget all old frames }
  392. clear_frames;
  393. Command('-stack-list-frames');
  394. if not GDB.ResultRecord.Success then
  395. exit;
  396. FrameList := GDB.ResultRecord.Parameters['stack'].AsList;
  397. frame_count := FrameList.Count;
  398. frames := AllocMem(SizeOf(PFrameEntry) * frame_count);
  399. for I := 0 to frame_count - 1 do
  400. frames[I] := New(PFrameEntry, Init);
  401. for I := 0 to FrameList.Count - 1 do
  402. begin
  403. frames[I]^.address := FrameList.ValueAt[I].AsTuple['addr'].AsCoreAddr;
  404. frames[I]^.level := FrameList.ValueAt[I].AsTuple['level'].AsLongInt;
  405. if Assigned(FrameList.ValueAt[I].AsTuple['line']) then
  406. frames[I]^.line_number := FrameList.ValueAt[I].AsTuple['line'].AsLongInt;
  407. if Assigned(FrameList.ValueAt[I].AsTuple['func']) then
  408. frames[I]^.function_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['func'].AsString));
  409. if Assigned(FrameList.ValueAt[I].AsTuple['fullname']) then
  410. frames[I]^.file_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['fullname'].AsString));
  411. end;
  412. Command('-stack-list-arguments 1');
  413. if not GDB.ResultRecord.Success then
  414. exit;
  415. FrameArgList := GDB.ResultRecord.Parameters['stack-args'].AsList;
  416. arg_count:=FrameArgList.Count;
  417. if arg_count>frame_count then
  418. arg_count:=frame_count;
  419. for I := 0 to arg_count - 1 do
  420. begin
  421. ArgList:=FrameArgList.ValueAt[I].AsTuple['args'].AsList;
  422. s:='(';
  423. for J:=0 to ArgList.Count-1 do
  424. begin
  425. if J>0 then s:=s+', ';
  426. s:=s+ArgList.ValueAt[J].AsTuple['name'].AsString;
  427. if Assigned(ArgList.ValueAt[J].AsTuple['value']) then
  428. s:=s+':='+ArgList.ValueAt[J].AsTuple['value'].ASString;
  429. end;
  430. s:=s+')';
  431. frames[I]^.args:=StrNew(pchar(s));
  432. end;
  433. end;
  434. function TGDBController.SelectFrameCommand(level :longint) : boolean;
  435. var
  436. LevelStr : String;
  437. begin
  438. Str(Level, LevelStr);
  439. Command('-stack-select-frame '+LevelStr);
  440. SelectFrameCommand:=not error;
  441. end;
  442. function TGDBController.LoadFile(var fn: string): Boolean;
  443. var
  444. cmd: string;
  445. begin
  446. getdir(0,cmd);
  447. UnixDir(cmd);
  448. Command('-environment-cd ' + cmd);
  449. GDBOutputBuf.Reset;
  450. GDBErrorBuf.Reset;
  451. {$ifdef GDB_RAW_OUTPUT}
  452. GDBRawBuf.reset;
  453. {$endif GDB_RAW_OUTPUT}
  454. UnixDir(fn);
  455. Command('-file-exec-and-symbols ' + fn);
  456. if not GDB.ResultRecord.Success then
  457. begin
  458. LoadFile:=false;
  459. exit;
  460. end;
  461. { the register list may change *after* loading a file, because there }
  462. { are gdb versions that support multiple archs, e.g. i386 and x86_64 }
  463. UpdateRegisterNames; { so that's why we update it here }
  464. LoadFile := True;
  465. end;
  466. procedure TGDBController.SetDir(const s: string);
  467. var
  468. hs: string;
  469. begin
  470. hs:=s;
  471. UnixDir(hs);
  472. { Avoid error message if s is empty }
  473. if hs<>'' then
  474. Command('-environment-cd ' + hs);
  475. end;
  476. procedure TGDBController.SetArgs(const s: string);
  477. begin
  478. Command('-exec-arguments ' + s);
  479. end;
  480. end.