2
0

gdbmicon.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. start_break_number := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  146. Run;
  147. end;
  148. procedure TGDBController.RunExecCommand(const Cmd: string);
  149. begin
  150. UserScreen;
  151. Command(Cmd);
  152. WaitForProgramStop;
  153. end;
  154. procedure TGDBController.Run;
  155. begin
  156. RunExecCommand('-exec-run');
  157. end;
  158. procedure TGDBController.TraceStep;
  159. begin
  160. RunExecCommand('-exec-step');
  161. end;
  162. procedure TGDBController.TraceNext;
  163. begin
  164. RunExecCommand('-exec-next');
  165. end;
  166. procedure TGDBController.TraceStepI;
  167. begin
  168. RunExecCommand('-exec-step-instruction');
  169. end;
  170. procedure TGDBController.TraceNextI;
  171. begin
  172. RunExecCommand('-exec-next-instruction');
  173. end;
  174. procedure TGDBController.Continue;
  175. begin
  176. RunExecCommand('-exec-continue');
  177. end;
  178. procedure TGDBController.UntilReturn;
  179. begin
  180. RunExecCommand('-exec-finish');
  181. end;
  182. function TGDBController.GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
  183. var
  184. RegNo: LongInt;
  185. RegNoStr: string;
  186. begin
  187. GetRegisterAsString := False;
  188. Value := '';
  189. RegNo := GetGdbRegisterNo(RegName);
  190. if RegNo = -1 then
  191. exit;
  192. Str(RegNo, RegNoStr);
  193. Command('-data-list-register-values ' + Format + ' ' + RegNoStr);
  194. if not GDB.ResultRecord.Success then
  195. exit;
  196. Value := GDB.ResultRecord.Parameters['register-values'].AsList.ValueAt[0].AsTuple['value'].AsString;
  197. GetRegisterAsString := True;
  198. end;
  199. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
  200. var
  201. RegValueStr: string;
  202. Code: LongInt;
  203. begin
  204. GetIntRegister := False;
  205. Value := 0;
  206. if not GetRegisterAsString(RegName, 'd', RegValueStr) then
  207. exit;
  208. Val(RegValueStr, Value, Code);
  209. if Code <> 0 then
  210. exit;
  211. GetIntRegister := True;
  212. end;
  213. function TGDBController.GetIntRegister(const RegName: string; var Value: Int64): Boolean;
  214. var
  215. U64Value: UInt64;
  216. begin
  217. GetIntRegister := GetIntRegister(RegName, U64Value);
  218. Value := Int64(U64Value);
  219. end;
  220. function TGDBController.GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
  221. var
  222. U64Value: UInt64;
  223. begin
  224. GetIntRegister := GetIntRegister(RegName, U64Value);
  225. Value := UInt32(U64Value);
  226. if (U64Value shr 32) <> 0 then
  227. GetIntRegister := False;
  228. end;
  229. function TGDBController.GetIntRegister(const RegName: string; var Value: Int32): Boolean;
  230. var
  231. U32Value: UInt32;
  232. begin
  233. GetIntRegister := GetIntRegister(RegName, U32Value);
  234. Value := Int32(U32Value);
  235. end;
  236. function TGDBController.BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
  237. var
  238. Options: string = '';
  239. begin
  240. if bfTemporary in BreakpointFlags then
  241. Options := Options + '-t ';
  242. if bfHardware in BreakpointFlags then
  243. Options := Options + '-h ';
  244. Command('-break-insert ' + Options + location);
  245. if GDB.ResultRecord.Success then
  246. BreakpointInsert := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt
  247. else
  248. BreakpointInsert := 0;
  249. end;
  250. function TGDBController.WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
  251. begin
  252. case WatchpointType of
  253. wtWrite:
  254. Command('-break-watch ' + location);
  255. wtReadWrite:
  256. Command('-break-watch -a ' + location);
  257. wtRead:
  258. Command('-break-watch -r ' + location);
  259. end;
  260. if GDB.ResultRecord.Success then
  261. case WatchpointType of
  262. wtWrite:
  263. WatchpointInsert := GDB.ResultRecord.Parameters['wpt'].AsTuple['number'].AsLongInt;
  264. wtReadWrite:
  265. WatchpointInsert := GDB.ResultRecord.Parameters['hw-awpt'].AsTuple['number'].AsLongInt;
  266. wtRead:
  267. WatchpointInsert := GDB.ResultRecord.Parameters['hw-rwpt'].AsTuple['number'].AsLongInt;
  268. end
  269. else
  270. WatchpointInsert := 0;
  271. end;
  272. function TGDBController.BreakpointDelete(BkptNo: LongInt): Boolean;
  273. var
  274. BkptNoStr: string;
  275. begin
  276. Str(BkptNo, BkptNoStr);
  277. Command('-break-delete ' + BkptNoStr);
  278. BreakpointDelete := GDB.ResultRecord.Success;
  279. end;
  280. function TGDBController.BreakpointEnable(BkptNo: LongInt): Boolean;
  281. var
  282. BkptNoStr: string;
  283. begin
  284. Str(BkptNo, BkptNoStr);
  285. Command('-break-enable ' + BkptNoStr);
  286. BreakpointEnable := GDB.ResultRecord.Success;
  287. end;
  288. function TGDBController.BreakpointDisable(BkptNo: LongInt): Boolean;
  289. var
  290. BkptNoStr: string;
  291. begin
  292. Str(BkptNo, BkptNoStr);
  293. Command('-break-disable ' + BkptNoStr);
  294. BreakpointDisable := GDB.ResultRecord.Success;
  295. end;
  296. function TGDBController.BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
  297. var
  298. BkptNoStr: string;
  299. begin
  300. Str(BkptNo, BkptNoStr);
  301. Command('-break-condition ' + BkptNoStr + ' ' + ConditionExpr);
  302. BreakpointCondition := GDB.ResultRecord.Success;
  303. end;
  304. function TGDBController.BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
  305. var
  306. BkptNoStr, IgnoreCountStr: string;
  307. begin
  308. Str(BkptNo, BkptNoStr);
  309. Str(IgnoreCount, IgnoreCountStr);
  310. Command('-break-after ' + BkptNoStr + ' ' + IgnoreCountStr);
  311. BreakpointSetIgnoreCount := GDB.ResultRecord.Success;
  312. end;
  313. procedure TGDBController.SetTBreak(tbreakstring : string);
  314. begin
  315. Command('-break-insert -t ' + tbreakstring);
  316. TBreakNumber := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  317. end;
  318. procedure TGDBController.Backtrace;
  319. var
  320. FrameList: TGDBMI_ListValue;
  321. I: LongInt;
  322. begin
  323. { forget all old frames }
  324. clear_frames;
  325. Command('-stack-list-frames');
  326. if not GDB.ResultRecord.Success then
  327. exit;
  328. FrameList := GDB.ResultRecord.Parameters['stack'].AsList;
  329. frame_count := FrameList.Count;
  330. frames := AllocMem(SizeOf(PFrameEntry) * frame_count);
  331. for I := 0 to frame_count - 1 do
  332. frames[I] := New(PFrameEntry, Init);
  333. for I := 0 to FrameList.Count - 1 do
  334. begin
  335. frames[I]^.address := FrameList.ValueAt[I].AsTuple['addr'].AsPtrInt;
  336. frames[I]^.level := FrameList.ValueAt[I].AsTuple['level'].AsLongInt;
  337. if Assigned(FrameList.ValueAt[I].AsTuple['line']) then
  338. frames[I]^.line_number := FrameList.ValueAt[I].AsTuple['line'].AsLongInt;
  339. if Assigned(FrameList.ValueAt[I].AsTuple['func']) then
  340. frames[I]^.function_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['func'].AsString));
  341. if Assigned(FrameList.ValueAt[I].AsTuple['fullname']) then
  342. frames[I]^.file_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['fullname'].AsString));
  343. end;
  344. end;
  345. function TGDBController.LoadFile(var fn: string): Boolean;
  346. var
  347. cmd: string;
  348. begin
  349. getdir(0,cmd);
  350. UnixDir(cmd);
  351. Command('-environment-cd ' + cmd);
  352. GDBOutputBuf.Reset;
  353. GDBErrorBuf.Reset;
  354. UnixDir(fn);
  355. Command('-file-exec-and-symbols ' + fn);
  356. { the register list may change *after* loading a file, because there }
  357. { are gdb versions that support multiple archs, e.g. i386 and x86_64 }
  358. UpdateRegisterNames; { so that's why we update it here }
  359. LoadFile := True;
  360. end;
  361. procedure TGDBController.SetDir(const s: string);
  362. var
  363. hs: string;
  364. begin
  365. hs:=s;
  366. UnixDir(hs);
  367. Command('-environment-cd ' + hs);
  368. end;
  369. procedure TGDBController.SetArgs(const s: string);
  370. begin
  371. Command('-exec-arguments ' + s);
  372. end;
  373. end.