gdbmicon.pas 14 KB

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