gdbmiint.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. function inferior_pid : longint;
  105. {$ifdef windows}
  106. { We need to do some path conversions if we are using Cygwin GDB }
  107. var
  108. using_cygwin_gdb : boolean;
  109. {$endif windows}
  110. implementation
  111. uses
  112. strings;
  113. constructor TFrameEntry.Init;
  114. begin
  115. Reset;
  116. end;
  117. destructor TFrameEntry.Done;
  118. begin
  119. Clear;
  120. end;
  121. procedure TFrameEntry.Reset;
  122. begin
  123. file_name := nil;
  124. function_name := nil;
  125. args := nil;
  126. line_number := 0;
  127. address := 0;
  128. end;
  129. procedure TFrameEntry.Clear;
  130. begin
  131. if Assigned(file_name) then
  132. StrDispose(file_name);
  133. if Assigned(function_name) then
  134. StrDispose(function_name);
  135. if Assigned(args) then
  136. StrDispose(args);
  137. Reset;
  138. end;
  139. const
  140. BlockSize = 2048;
  141. constructor TGDBBuffer.Init;
  142. begin
  143. buf := nil;
  144. size := 0;
  145. Resize(BlockSize);
  146. Reset;
  147. end;
  148. destructor TGDBBuffer.Done;
  149. begin
  150. if Assigned(buf) then
  151. FreeMem(buf, size);
  152. end;
  153. procedure TGDBBuffer.Reset;
  154. begin
  155. idx := 0;
  156. buf[0] := #0;
  157. end;
  158. procedure TGDBBuffer.Resize(nsize: LongInt);
  159. var
  160. np: PChar;
  161. begin
  162. nsize := ((nsize + BlockSize - 1) div BlockSize) * BlockSize;
  163. GetMem(np, nsize);
  164. if Assigned(buf) then
  165. begin
  166. Move(buf^, np^, size);
  167. FreeMem(buf, size);
  168. end;
  169. buf := np;
  170. size := nsize;
  171. end;
  172. procedure TGDBBuffer.Append(p: PChar);
  173. var
  174. len: LongInt;
  175. begin
  176. if not Assigned(p) then
  177. exit;
  178. len := StrLen(p);
  179. LAppend(p, len);
  180. end;
  181. procedure TGDBBuffer.LAppend(p: PChar; len: LongInt);
  182. begin
  183. if not Assigned(p) then
  184. exit;
  185. if (len + idx + 1) > size then
  186. Resize(len + idx + 1);
  187. Move(p^, buf[idx], len);
  188. Inc(idx, len);
  189. buf[idx] := #0;
  190. end;
  191. constructor TGDBInterface.Init;
  192. begin
  193. GDBErrorBuf.Init;
  194. GDBOutputBuf.Init;
  195. GDB := TGDBWrapper.Create;
  196. command_level := 0;
  197. end;
  198. destructor TGDBInterface.Done;
  199. begin
  200. GDB.Free;
  201. GDBErrorBuf.Done;
  202. GDBOutputBuf.Done;
  203. end;
  204. function TGDBInterface.GetOutput: PChar;
  205. begin
  206. GetOutput := GDBOutputBuf.buf;
  207. end;
  208. function TGDBInterface.GetError: PChar;
  209. var
  210. p: PChar;
  211. begin
  212. p := GDBErrorBuf.buf;
  213. if (p^=#0) and got_error then
  214. GetError := PChar(PtrInt(GDBOutputBuf.buf) + GDBOutputBuf.idx)
  215. else
  216. GetError := p;
  217. end;
  218. procedure TGDBInterface.i_gdb_command(const S: string);
  219. var
  220. prev_stop_breakpoint_number: LongInt;
  221. I: LongInt;
  222. begin
  223. Inc(command_level);
  224. got_error := False;
  225. if command_level = 1 then
  226. prev_stop_breakpoint_number := 0
  227. else
  228. prev_stop_breakpoint_number := stop_breakpoint_number;
  229. GDB.Command(S);
  230. for I := 0 to GDB.ConsoleStream.Count - 1 do
  231. GDBOutputBuf.Append(PChar(GDB.ConsoleStream[I]));
  232. ProcessResponse;
  233. Dec(command_level);
  234. stop_breakpoint_number := prev_stop_breakpoint_number;
  235. end;
  236. procedure TGDBInterface.WaitForProgramStop;
  237. var
  238. Line: LongInt;
  239. FileName: string = '';
  240. LineNumber: LongInt = 0;
  241. begin
  242. GDB.WaitForProgramStop;
  243. if not GDB.Alive then
  244. begin
  245. DebuggerScreen;
  246. current_pc := 0;
  247. Debuggee_started := False;
  248. exit;
  249. end;
  250. ProcessResponse;
  251. case GDB.ExecAsyncOutput.Parameters['reason'].AsString of
  252. 'breakpoint-hit',
  253. 'end-stepping-range',
  254. 'function-finished':
  255. begin
  256. if Assigned(GDB.ExecAsyncOutput.Parameters['bkptno']) then
  257. stop_breakpoint_number := GDB.ExecAsyncOutput.Parameters['bkptno'].AsLongInt;
  258. DebuggerScreen;
  259. Debuggee_started := True;
  260. current_pc := GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['addr'].AsPtrInt;
  261. if Assigned(GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['fullname']) then
  262. FileName := GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['fullname'].AsString;
  263. if Assigned(GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['line']) then
  264. LineNumber := GDB.ExecAsyncOutput.Parameters['frame'].AsTuple['line'].AsLongInt;
  265. DoSelectSourceLine(FileName, LineNumber);
  266. end;
  267. 'exited':
  268. begin
  269. DebuggerScreen;
  270. current_pc := 0;
  271. Debuggee_started := False;
  272. DoEndSession(GDB.ExecAsyncOutput.Parameters['exit-code'].AsLongInt);
  273. end;
  274. 'exited-normally':
  275. begin
  276. DebuggerScreen;
  277. current_pc := 0;
  278. Debuggee_started := False;
  279. DoEndSession(0);
  280. end;
  281. end;
  282. end;
  283. procedure TGDBInterface.ProcessResponse;
  284. var
  285. NAO: TGDBMI_AsyncOutput;
  286. Code: LongInt;
  287. begin
  288. for NAO in GDB.NotifyAsyncOutput do
  289. begin
  290. if NAO.AsyncClass = 'breakpoint-created' then
  291. begin
  292. // Writeln('BREAKPOINT created!');
  293. Val(NAO.Parameters['bkpt'].AsTuple['number'].AsString, last_breakpoint_number, Code);
  294. // Writeln('last_breakpoint_number=', last_breakpoint_number);
  295. // if Assigned(NAO.Parameters['bkpt'].AsTuple['file']) then
  296. // Writeln('file = ', NAO.Parameters['bkpt'].AsTuple['file'].AsString);
  297. // Readln;
  298. end;
  299. end;
  300. end;
  301. function TGDBInterface.error: Boolean;
  302. begin
  303. error := got_error or not GDB.Alive;
  304. end;
  305. function TGDBInterface.error_num: LongInt;
  306. begin
  307. error_num := 0; { TODO }
  308. end;
  309. function TGDBInterface.get_current_frame: PtrInt;
  310. begin
  311. end;
  312. function TGDBInterface.set_current_frame(level: LongInt): Boolean;
  313. begin
  314. end;
  315. procedure TGDBInterface.clear_frames;
  316. var
  317. I: LongInt;
  318. begin
  319. for I := 0 to frame_size - 1 do
  320. Dispose(frames[I], Done);
  321. if Assigned(frames) then
  322. begin
  323. FreeMem(frames, SizeOf(Pointer) * frame_size);
  324. frames := nil;
  325. end;
  326. frame_count := 0;
  327. frame_size := 0;
  328. end;
  329. procedure TGDBInterface.DebuggerScreen;
  330. begin
  331. if user_screen_shown then
  332. DoDebuggerScreen;
  333. user_screen_shown := False;
  334. end;
  335. procedure TGDBInterface.UserScreen;
  336. begin
  337. if switch_to_user then
  338. begin
  339. if not user_screen_shown then
  340. DoUserScreen;
  341. user_screen_shown := True;
  342. end;
  343. end;
  344. procedure TGDBInterface.FlushAll;
  345. begin
  346. end;
  347. function TGDBInterface.Query(question: PChar; args: PChar): LongInt;
  348. begin
  349. Query := 0;
  350. end;
  351. procedure TGDBInterface.DoSelectSourceline(const fn: string; line: LongInt);
  352. begin
  353. end;
  354. procedure TGDBInterface.DoStartSession;
  355. begin
  356. end;
  357. procedure TGDBInterface.DoBreakSession;
  358. begin
  359. end;
  360. procedure TGDBInterface.DoEndSession(code: LongInt);
  361. begin
  362. end;
  363. procedure TGDBInterface.DoUserSignal;
  364. begin
  365. end;
  366. procedure TGDBInterface.DoDebuggerScreen;
  367. begin
  368. end;
  369. procedure TGDBInterface.DoUserScreen;
  370. begin
  371. end;
  372. function TGDBInterface.AllowQuit: Boolean;
  373. begin
  374. AllowQuit := True;
  375. end;
  376. function inferior_pid : longint;
  377. begin
  378. inferior_pid:=0; {inferior_ptid.pid; }
  379. end;
  380. var
  381. CachedGDBVersion: string;
  382. function GDBVersion: string;
  383. var
  384. GDB: TGDBWrapper;
  385. {$ifdef windows}
  386. i : longint;
  387. line :string;
  388. {$endif windows}
  389. begin
  390. if CachedGDBVersion <> '' then
  391. begin
  392. GDBVersion := CachedGDBVersion;
  393. exit;
  394. end;
  395. GDBVersion := '';
  396. GDB := TGDBWrapper.Create;
  397. GDB.Command('-gdb-version');
  398. if GDB.ConsoleStream.Count > 0 then
  399. GDBVersion := GDB.ConsoleStream[0];
  400. if (GDBVersion <> '') and (GDBVersion[Length(GDBVersion)]=#10) then
  401. Delete(GDBVersion, Length(GDBVersion), 1);
  402. {$ifdef windows}
  403. i:=0;
  404. using_cygwin_gdb:=false;
  405. while i < GDB.ConsoleStream.Count do
  406. begin
  407. line:=GDB.ConsoleStream[i];
  408. if pos('This GDB was configured',line) > 0 then
  409. using_cygwin_gdb:=pos('cygwin',line) > 0;
  410. inc(i);
  411. end;
  412. {$endif windows}
  413. GDB.Free;
  414. CachedGDBVersion := GDBVersion;
  415. if GDBVersion = '' then
  416. GDBVersion := 'GDB missing or does not work';
  417. end;
  418. begin
  419. CachedGDBVersion := '';
  420. end.