gdbmicon.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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. interface
  16. uses
  17. gdbmiint, gdbmiwrap;
  18. type
  19. TBreakpointFlags = set of (bfTemporary, bfHardware);
  20. TWatchpointType = (wtWrite, wtReadWrite, wtRead);
  21. TGDBController = object(TGDBInterface)
  22. private
  23. FRegisterNames: array of AnsiString;
  24. procedure UpdateRegisterNames;
  25. function GetGdbRegisterNo(const RegName: string): LongInt;
  26. function GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
  27. procedure RunExecCommand(const Cmd: string);
  28. protected
  29. TBreakNumber,
  30. start_break_number: LongInt;
  31. in_command: LongInt;
  32. procedure CommandBegin(const s: string); virtual;
  33. procedure CommandEnd(const s: string); virtual;
  34. public
  35. constructor Init;
  36. destructor Done;
  37. procedure Command(const s: string);
  38. procedure Reset; virtual;
  39. { tracing }
  40. procedure StartTrace;
  41. procedure Run; virtual;
  42. procedure TraceStep;
  43. procedure TraceNext;
  44. procedure TraceStepI;
  45. procedure TraceNextI;
  46. procedure Continue; virtual;
  47. procedure UntilReturn; virtual;
  48. { registers }
  49. function GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  50. function GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  51. function GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  52. function GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  53. { breakpoints }
  54. function BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  55. function WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  56. function BreakpointDelete(BkptNo: LongInt): Boolean;
  57. function BreakpointEnable(BkptNo: LongInt): Boolean;
  58. function BreakpointDisable(BkptNo: LongInt): Boolean;
  59. function BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  60. function BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  61. procedure SetTBreak(tbreakstring : string);
  62. procedure Backtrace;
  63. function LoadFile(var fn: string): Boolean;
  64. procedure SetDir(const s: string);
  65. procedure SetArgs(const s: string);
  66. end;
  67. implementation
  68. uses
  69. {$ifdef Windows}
  70. Windebug,
  71. {$endif Windows}
  72. strings;
  73. procedure UnixDir(var s : string);
  74. var i : longint;
  75. begin
  76. for i:=1 to length(s) do
  77. if s[i]='\' then
  78. {$ifdef windows}
  79. { Don't touch at '\ ' used to escapes spaces in windows file names PM }
  80. if (i=length(s)) or (s[i+1]<>' ') then
  81. {$endif windows}
  82. s[i]:='/';
  83. {$ifdef windows}
  84. { if we are using cygwin, we need to convert e:\ into /cygdriveprefix/e/ PM }
  85. if using_cygwin_gdb and (length(s)>2) and (s[2]=':') and (s[3]='/') then
  86. s:=CygDrivePrefix+'/'+s[1]+copy(s,3,length(s));
  87. {$endif windows}
  88. end;
  89. constructor TGDBController.Init;
  90. begin
  91. inherited Init;
  92. end;
  93. destructor TGDBController.Done;
  94. begin
  95. inherited Done;
  96. end;
  97. procedure TGDBController.CommandBegin(const s: string);
  98. begin
  99. end;
  100. procedure TGDBController.Command(const s: string);
  101. begin
  102. Inc(in_command);
  103. CommandBegin(s);
  104. GDBOutputBuf.Reset;
  105. GDBErrorBuf.Reset;
  106. i_gdb_command(s);
  107. CommandEnd(s);
  108. Dec(in_command);
  109. end;
  110. procedure TGDBController.CommandEnd(const s: string);
  111. begin
  112. end;
  113. procedure TGDBController.UpdateRegisterNames;
  114. var
  115. I: LongInt;
  116. ResultList: TGDBMI_ListValue;
  117. begin
  118. SetLength(FRegisterNames, 0);
  119. Command('-data-list-register-names');
  120. if not GDB.ResultRecord.Success then
  121. exit;
  122. ResultList := GDB.ResultRecord.Parameters['register-names'].AsList;
  123. SetLength(FRegisterNames, ResultList.Count);
  124. for I := 0 to ResultList.Count - 1 do
  125. FRegisterNames[I] := ResultList.ValueAt[I].AsString;
  126. end;
  127. function TGDBController.GetGdbRegisterNo(const RegName: string): LongInt;
  128. var
  129. I: LongInt;
  130. begin
  131. for I := Low(FRegisterNames) to High(FRegisterNames) do
  132. if FRegisterNames[I] = RegName then
  133. begin
  134. GetGdbRegisterNo := I;
  135. exit;
  136. end;
  137. GetGdbRegisterNo := -1;
  138. end;
  139. procedure TGDBController.Reset;
  140. begin
  141. end;
  142. procedure TGDBController.StartTrace;
  143. begin
  144. Command('-break-insert -t PASCALMAIN');
  145. if not GDB.ResultRecord.Success then
  146. exit;
  147. start_break_number := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  148. Run;
  149. end;
  150. procedure TGDBController.RunExecCommand(const Cmd: string);
  151. begin
  152. UserScreen;
  153. Command(Cmd);
  154. WaitForProgramStop;
  155. end;
  156. procedure TGDBController.Run;
  157. begin
  158. RunExecCommand('-exec-run');
  159. end;
  160. procedure TGDBController.TraceStep;
  161. begin
  162. RunExecCommand('-exec-step');
  163. end;
  164. procedure TGDBController.TraceNext;
  165. begin
  166. RunExecCommand('-exec-next');
  167. end;
  168. procedure TGDBController.TraceStepI;
  169. begin
  170. RunExecCommand('-exec-step-instruction');
  171. end;
  172. procedure TGDBController.TraceNextI;
  173. begin
  174. RunExecCommand('-exec-next-instruction');
  175. end;
  176. procedure TGDBController.Continue;
  177. begin
  178. RunExecCommand('-exec-continue');
  179. end;
  180. procedure TGDBController.UntilReturn;
  181. begin
  182. RunExecCommand('-exec-finish');
  183. end;
  184. function TGDBController.GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
  185. var
  186. RegNo: LongInt;
  187. RegNoStr: string;
  188. begin
  189. GetRegisterAsString := False;
  190. Value := '';
  191. RegNo := GetGdbRegisterNo(RegName);
  192. if RegNo = -1 then
  193. exit;
  194. Str(RegNo, RegNoStr);
  195. Command('-data-list-register-values ' + Format + ' ' + RegNoStr);
  196. if not GDB.ResultRecord.Success then
  197. exit;
  198. Value := GDB.ResultRecord.Parameters['register-values'].AsList.ValueAt[0].AsTuple['value'].AsString;
  199. GetRegisterAsString := True;
  200. end;
  201. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  202. var
  203. RegValueStr: string;
  204. Code: LongInt;
  205. begin
  206. GetIntRegister := False;
  207. Value := 0;
  208. if not GetRegisterAsString(RegName, 'd', RegValueStr) then
  209. exit;
  210. Val(RegValueStr, Value, Code);
  211. if Code <> 0 then
  212. exit;
  213. GetIntRegister := True;
  214. end;
  215. function TGDBController.GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  216. var
  217. U64Value: UInt64;
  218. begin
  219. GetIntRegister := GetIntRegister(RegName, U64Value);
  220. Value := Int64(U64Value);
  221. end;
  222. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  223. var
  224. U64Value: UInt64;
  225. begin
  226. GetIntRegister := GetIntRegister(RegName, U64Value);
  227. Value := UInt32(U64Value);
  228. if (U64Value shr 32) <> 0 then
  229. GetIntRegister := False;
  230. end;
  231. function TGDBController.GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  232. var
  233. U32Value: UInt32;
  234. begin
  235. GetIntRegister := GetIntRegister(RegName, U32Value);
  236. Value := Int32(U32Value);
  237. end;
  238. function TGDBController.BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  239. var
  240. Options: string = '';
  241. begin
  242. if bfTemporary in BreakpointFlags then
  243. Options := Options + '-t ';
  244. if bfHardware in BreakpointFlags then
  245. Options := Options + '-h ';
  246. Command('-break-insert ' + Options + location);
  247. if GDB.ResultRecord.Success then
  248. BreakpointInsert := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt
  249. else
  250. BreakpointInsert := 0;
  251. end;
  252. function TGDBController.WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  253. begin
  254. case WatchpointType of
  255. wtWrite:
  256. Command('-break-watch ' + location);
  257. wtReadWrite:
  258. Command('-break-watch -a ' + location);
  259. wtRead:
  260. Command('-break-watch -r ' + location);
  261. end;
  262. if GDB.ResultRecord.Success then
  263. case WatchpointType of
  264. wtWrite:
  265. WatchpointInsert := GDB.ResultRecord.Parameters['wpt'].AsTuple['number'].AsLongInt;
  266. wtReadWrite:
  267. WatchpointInsert := GDB.ResultRecord.Parameters['hw-awpt'].AsTuple['number'].AsLongInt;
  268. wtRead:
  269. WatchpointInsert := GDB.ResultRecord.Parameters['hw-rwpt'].AsTuple['number'].AsLongInt;
  270. end
  271. else
  272. WatchpointInsert := 0;
  273. end;
  274. function TGDBController.BreakpointDelete(BkptNo: LongInt): Boolean;
  275. var
  276. BkptNoStr: string;
  277. begin
  278. Str(BkptNo, BkptNoStr);
  279. Command('-break-delete ' + BkptNoStr);
  280. BreakpointDelete := GDB.ResultRecord.Success;
  281. end;
  282. function TGDBController.BreakpointEnable(BkptNo: LongInt): Boolean;
  283. var
  284. BkptNoStr: string;
  285. begin
  286. Str(BkptNo, BkptNoStr);
  287. Command('-break-enable ' + BkptNoStr);
  288. BreakpointEnable := GDB.ResultRecord.Success;
  289. end;
  290. function TGDBController.BreakpointDisable(BkptNo: LongInt): Boolean;
  291. var
  292. BkptNoStr: string;
  293. begin
  294. Str(BkptNo, BkptNoStr);
  295. Command('-break-disable ' + BkptNoStr);
  296. BreakpointDisable := GDB.ResultRecord.Success;
  297. end;
  298. function TGDBController.BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  299. var
  300. BkptNoStr: string;
  301. begin
  302. Str(BkptNo, BkptNoStr);
  303. Command('-break-condition ' + BkptNoStr + ' ' + ConditionExpr);
  304. BreakpointCondition := GDB.ResultRecord.Success;
  305. end;
  306. function TGDBController.BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  307. var
  308. BkptNoStr, IgnoreCountStr: string;
  309. begin
  310. Str(BkptNo, BkptNoStr);
  311. Str(IgnoreCount, IgnoreCountStr);
  312. Command('-break-after ' + BkptNoStr + ' ' + IgnoreCountStr);
  313. BreakpointSetIgnoreCount := GDB.ResultRecord.Success;
  314. end;
  315. procedure TGDBController.SetTBreak(tbreakstring : string);
  316. begin
  317. Command('-break-insert -t ' + tbreakstring);
  318. TBreakNumber := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  319. end;
  320. procedure TGDBController.Backtrace;
  321. var
  322. FrameList: TGDBMI_ListValue;
  323. I: LongInt;
  324. begin
  325. { forget all old frames }
  326. clear_frames;
  327. Command('-stack-list-frames');
  328. if not GDB.ResultRecord.Success then
  329. exit;
  330. FrameList := GDB.ResultRecord.Parameters['stack'].AsList;
  331. frame_count := FrameList.Count;
  332. frames := AllocMem(SizeOf(PFrameEntry) * frame_count);
  333. for I := 0 to frame_count - 1 do
  334. frames[I] := New(PFrameEntry, Init);
  335. for I := 0 to FrameList.Count - 1 do
  336. begin
  337. frames[I]^.address := FrameList.ValueAt[I].AsTuple['addr'].AsPtrInt;
  338. frames[I]^.level := FrameList.ValueAt[I].AsTuple['level'].AsLongInt;
  339. if Assigned(FrameList.ValueAt[I].AsTuple['line']) then
  340. frames[I]^.line_number := FrameList.ValueAt[I].AsTuple['line'].AsLongInt;
  341. if Assigned(FrameList.ValueAt[I].AsTuple['func']) then
  342. frames[I]^.function_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['func'].AsString));
  343. if Assigned(FrameList.ValueAt[I].AsTuple['fullname']) then
  344. frames[I]^.file_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['fullname'].AsString));
  345. end;
  346. end;
  347. function TGDBController.LoadFile(var fn: string): Boolean;
  348. var
  349. cmd: string;
  350. begin
  351. getdir(0,cmd);
  352. UnixDir(cmd);
  353. Command('-environment-cd ' + cmd);
  354. GDBOutputBuf.Reset;
  355. GDBErrorBuf.Reset;
  356. UnixDir(fn);
  357. Command('-file-exec-and-symbols ' + fn);
  358. if not GDB.ResultRecord.Success then
  359. begin
  360. LoadFile:=false;
  361. exit;
  362. end;
  363. { the register list may change *after* loading a file, because there }
  364. { are gdb versions that support multiple archs, e.g. i386 and x86_64 }
  365. UpdateRegisterNames; { so that's why we update it here }
  366. LoadFile := True;
  367. end;
  368. procedure TGDBController.SetDir(const s: string);
  369. var
  370. hs: string;
  371. begin
  372. hs:=s;
  373. UnixDir(hs);
  374. Command('-environment-cd ' + hs);
  375. end;
  376. procedure TGDBController.SetArgs(const s: string);
  377. begin
  378. Command('-exec-arguments ' + s);
  379. end;
  380. end.