gdbmiint.pas 10 KB

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