gdbmiint.pas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. {
  2. Copyright (c) 2015 by Nikolay Nikolov
  3. Copyright (c) 1998 by Peter Vreman
  4. This is a replacement for GDBInt, 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 gdbmiint;
  14. {$MODE fpc}{$H-}
  15. interface
  16. uses
  17. gdbmiwrap;
  18. type
  19. CORE_ADDR = PtrInt;
  20. PPFrameEntry = ^PFrameEntry;
  21. PFrameEntry = ^TFrameEntry;
  22. TFrameEntry = object
  23. private
  24. procedure Reset;
  25. procedure Clear;
  26. public
  27. file_name: PChar;
  28. function_name: PChar;
  29. args: PChar;
  30. line_number: LongInt;
  31. address: PtrInt;
  32. constructor Init;
  33. destructor Done;
  34. end;
  35. TGDBBuffer = object
  36. private
  37. buf: PChar;
  38. size, idx: LongInt;
  39. procedure Resize(nsize: LongInt);
  40. procedure Append(p: PChar);
  41. procedure LAppend(p: PChar; len: LongInt);
  42. public
  43. constructor Init;
  44. destructor Done;
  45. procedure Reset;
  46. end;
  47. TGDBInterface = object
  48. private
  49. user_screen_shown: Boolean;
  50. frame_size: LongInt;
  51. protected
  52. GDB: TGDBWrapper;
  53. procedure i_gdb_command(const S: string);
  54. procedure WaitForProgramStop;
  55. procedure ProcessResponse;
  56. public
  57. GDBErrorBuf: TGDBBuffer;
  58. GDBOutputBuf: TGDBBuffer;
  59. got_error: Boolean;
  60. reset_command: Boolean;
  61. Debuggee_started: Boolean;
  62. { frames and frame info while recording a frame }
  63. frames: PPFrameEntry;
  64. frame_count: LongInt;
  65. command_level,
  66. stop_breakpoint_number: LongInt;
  67. signal_name: PChar;
  68. signal_string: PChar;
  69. current_pc: CORE_ADDR;
  70. last_breakpoint_number: LongInt;
  71. switch_to_user: Boolean;
  72. { init }
  73. constructor Init;
  74. destructor Done;
  75. { from gdbcon }
  76. function GetOutput: PChar;
  77. function GetError: PChar;
  78. { Lowlevel }
  79. function error: Boolean;
  80. function error_num: LongInt;
  81. function get_current_frame: PtrInt;
  82. function set_current_frame(level: LongInt): Boolean;
  83. procedure clear_frames;
  84. { Highlevel }
  85. procedure DebuggerScreen;
  86. procedure UserScreen;
  87. procedure FlushAll; virtual;
  88. function Query(question: PChar; args: PChar): LongInt; virtual;
  89. { Hooks }
  90. procedure DoSelectSourceline(const fn: string; line: LongInt); virtual;
  91. procedure DoStartSession; virtual;
  92. procedure DoBreakSession; virtual;
  93. procedure DoEndSession(code: LongInt); virtual;
  94. procedure DoUserSignal; virtual;
  95. procedure DoDebuggerScreen; virtual;
  96. procedure DoUserScreen; virtual;
  97. function AllowQuit: Boolean; virtual;
  98. end;
  99. const
  100. use_gdb_file: Boolean = False;
  101. var
  102. gdb_file: Text;
  103. function GDBVersion: string;
  104. implementation
  105. uses
  106. strings;
  107. constructor TFrameEntry.Init;
  108. begin
  109. Reset;
  110. end;
  111. destructor TFrameEntry.Done;
  112. begin
  113. Clear;
  114. end;
  115. procedure TFrameEntry.Reset;
  116. begin
  117. file_name := nil;
  118. function_name := nil;
  119. args := nil;
  120. line_number := 0;
  121. address := 0;
  122. end;
  123. procedure TFrameEntry.Clear;
  124. begin
  125. if Assigned(file_name) then
  126. StrDispose(file_name);
  127. if Assigned(function_name) then
  128. StrDispose(function_name);
  129. if Assigned(args) then
  130. StrDispose(args);
  131. Reset;
  132. end;
  133. const
  134. BlockSize = 2048;
  135. constructor TGDBBuffer.Init;
  136. begin
  137. buf := nil;
  138. size := 0;
  139. Resize(BlockSize);
  140. Reset;
  141. end;
  142. destructor TGDBBuffer.Done;
  143. begin
  144. if Assigned(buf) then
  145. FreeMem(buf, size);
  146. end;
  147. procedure TGDBBuffer.Reset;
  148. begin
  149. idx := 0;
  150. buf[0] := #0;
  151. end;
  152. procedure TGDBBuffer.Resize(nsize: LongInt);
  153. var
  154. np: PChar;
  155. begin
  156. nsize := ((nsize + BlockSize - 1) div BlockSize) * BlockSize;
  157. GetMem(np, nsize);
  158. if Assigned(buf) then
  159. begin
  160. Move(buf^, np^, size);
  161. FreeMem(buf, size);
  162. end;
  163. buf := np;
  164. size := nsize;
  165. end;
  166. procedure TGDBBuffer.Append(p: PChar);
  167. var
  168. len: LongInt;
  169. begin
  170. if not Assigned(p) then
  171. exit;
  172. len := StrLen(p);
  173. LAppend(p, len);
  174. end;
  175. procedure TGDBBuffer.LAppend(p: PChar; len: LongInt);
  176. begin
  177. if not Assigned(p) then
  178. exit;
  179. if (len + idx + 1) > size then
  180. Resize(len + idx + 1);
  181. Move(p^, buf[idx], len);
  182. Inc(idx, len);
  183. buf[idx] := #0;
  184. end;
  185. constructor TGDBInterface.Init;
  186. begin
  187. GDBErrorBuf.Init;
  188. GDBOutputBuf.Init;
  189. GDB := TGDBWrapper.Create;
  190. command_level := 0;
  191. end;
  192. destructor TGDBInterface.Done;
  193. begin
  194. GDB.Free;
  195. GDBErrorBuf.Done;
  196. GDBOutputBuf.Done;
  197. end;
  198. function TGDBInterface.GetOutput: PChar;
  199. begin
  200. GetOutput := GDBOutputBuf.buf;
  201. end;
  202. function TGDBInterface.GetError: PChar;
  203. var
  204. p: PChar;
  205. begin
  206. p := GDBErrorBuf.buf;
  207. if (p^=#0) and got_error then
  208. GetError := PChar(PtrInt(GDBOutputBuf.buf) + GDBOutputBuf.idx)
  209. else
  210. GetError := p;
  211. end;
  212. procedure TGDBInterface.i_gdb_command(const S: string);
  213. var
  214. prev_stop_breakpoint_number: LongInt;
  215. I: LongInt;
  216. begin
  217. Inc(command_level);
  218. got_error := False;
  219. if command_level = 1 then
  220. prev_stop_breakpoint_number := 0
  221. else
  222. prev_stop_breakpoint_number := stop_breakpoint_number;
  223. GDB.Command(S);
  224. for I := 0 to GDB.ConsoleStream.Count - 1 do
  225. GDBOutputBuf.Append(PChar(GDB.ConsoleStream[I]));
  226. ProcessResponse;
  227. Dec(command_level);
  228. stop_breakpoint_number := prev_stop_breakpoint_number;
  229. end;
  230. procedure TGDBInterface.WaitForProgramStop;
  231. var
  232. Line: LongInt;
  233. begin
  234. GDB.WaitForProgramStop;
  235. if not GDB.Alive then
  236. begin
  237. DebuggerScreen;
  238. current_pc := 0;
  239. Debuggee_started := False;
  240. exit;
  241. end;
  242. ProcessResponse;
  243. case GDB.ExecAsyncOutput.Parameters['reason'].AsString of
  244. 'breakpoint-hit':
  245. begin
  246. stop_breakpoint_number := GDB.ExecAsyncOutput.Parameters['bkptno'].AsLongInt;
  247. DebuggerScreen;
  248. Debuggee_started := True;
  249. DoSelectSourceLine(GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['fullname'].AsString, GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['line'].AsLongInt);
  250. end;
  251. 'end-stepping-range':
  252. begin
  253. DebuggerScreen;
  254. Debuggee_started := True;
  255. current_pc := GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['addr'].AsPtrInt;
  256. DoSelectSourceLine(GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['fullname'].AsString, GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['line'].AsLongInt);
  257. end;
  258. 'exited':
  259. begin
  260. DebuggerScreen;
  261. current_pc := 0;
  262. Debuggee_started := False;
  263. DoEndSession(GDB.ExecAsyncOutput.Parameters['exit-code'].AsLongInt);
  264. end;
  265. 'exited-normally':
  266. begin
  267. DebuggerScreen;
  268. current_pc := 0;
  269. Debuggee_started := False;
  270. DoEndSession(0);
  271. end;
  272. end;
  273. end;
  274. procedure TGDBInterface.ProcessResponse;
  275. var
  276. NAO: TGDBMI_AsyncOutput;
  277. Code: LongInt;
  278. begin
  279. for NAO in GDB.NotifyAsyncOutput do
  280. begin
  281. if NAO.AsyncClass = 'breakpoint-created' then
  282. begin
  283. // Writeln('BREAKPOINT created!');
  284. Val(NAO.Parameters['bkpt'].AsTuple['number'].AsString, last_breakpoint_number, Code);
  285. // Writeln('last_breakpoint_number=', last_breakpoint_number);
  286. // if Assigned(NAO.Parameters['bkpt'].AsTuple['file']) then
  287. // Writeln('file = ', NAO.Parameters['bkpt'].AsTuple['file'].AsString);
  288. // Readln;
  289. end;
  290. end;
  291. end;
  292. function TGDBInterface.error: Boolean;
  293. begin
  294. error := got_error or not GDB.Alive;
  295. end;
  296. function TGDBInterface.error_num: LongInt;
  297. begin
  298. error_num := 0; { TODO }
  299. end;
  300. function TGDBInterface.get_current_frame: PtrInt;
  301. begin
  302. end;
  303. function TGDBInterface.set_current_frame(level: LongInt): Boolean;
  304. begin
  305. end;
  306. procedure TGDBInterface.clear_frames;
  307. var
  308. I: LongInt;
  309. begin
  310. for I := 0 to frame_size - 1 do
  311. Dispose(frames[I], Done);
  312. if Assigned(frames) then
  313. begin
  314. FreeMem(frames, SizeOf(Pointer) * frame_size);
  315. frames := nil;
  316. end;
  317. frame_count := 0;
  318. frame_size := 0;
  319. end;
  320. procedure TGDBInterface.DebuggerScreen;
  321. begin
  322. if user_screen_shown then
  323. DoDebuggerScreen;
  324. user_screen_shown := False;
  325. end;
  326. procedure TGDBInterface.UserScreen;
  327. begin
  328. if switch_to_user then
  329. begin
  330. if not user_screen_shown then
  331. DoUserScreen;
  332. user_screen_shown := True;
  333. end;
  334. end;
  335. procedure TGDBInterface.FlushAll;
  336. begin
  337. end;
  338. function TGDBInterface.Query(question: PChar; args: PChar): LongInt;
  339. begin
  340. Query := 0;
  341. end;
  342. procedure TGDBInterface.DoSelectSourceline(const fn: string; line: LongInt);
  343. begin
  344. end;
  345. procedure TGDBInterface.DoStartSession;
  346. begin
  347. end;
  348. procedure TGDBInterface.DoBreakSession;
  349. begin
  350. end;
  351. procedure TGDBInterface.DoEndSession(code: LongInt);
  352. begin
  353. end;
  354. procedure TGDBInterface.DoUserSignal;
  355. begin
  356. end;
  357. procedure TGDBInterface.DoDebuggerScreen;
  358. begin
  359. end;
  360. procedure TGDBInterface.DoUserScreen;
  361. begin
  362. end;
  363. function TGDBInterface.AllowQuit: Boolean;
  364. begin
  365. AllowQuit := True;
  366. end;
  367. var
  368. CachedGDBVersion: string;
  369. function GDBVersion: string;
  370. var
  371. GDB: TGDBWrapper;
  372. begin
  373. if CachedGDBVersion <> '' then
  374. begin
  375. GDBVersion := CachedGDBVersion;
  376. exit;
  377. end;
  378. GDBVersion := '';
  379. GDB := TGDBWrapper.Create;
  380. GDB.Command('-gdb-version');
  381. if GDB.ConsoleStream.Count > 0 then
  382. GDBVersion := GDB.ConsoleStream[0];
  383. if (GDBVersion <> '') and (GDBVersion[Length(GDBVersion)]=#10) then
  384. Delete(GDBVersion, Length(GDBVersion), 1);
  385. GDB.Free;
  386. CachedGDBVersion := GDBVersion;
  387. if GDBVersion = '' then
  388. GDBVersion := 'GDB missing or does not work';
  389. end;
  390. begin
  391. CachedGDBVersion := '';
  392. end.