fpdebug.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 1998 by Berczi Gabor
  5. Debugger call routines for the IDE
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit FPDebug;
  13. interface
  14. uses
  15. Views,FPViews,
  16. Objects,GDBCon,GDBInt;
  17. type
  18. PDebugController=^TDebugController;
  19. TDebugController=object(TGDBController)
  20. InvalidSourceLine : boolean;
  21. LastFileName : string;
  22. LastSource : PView; {PsourceWindow !! }
  23. HiddenStepsCount : longint;
  24. constructor Init(const exefn:string);
  25. destructor Done;
  26. procedure DoSelectSourceline(const fn:string;line:longint);virtual;
  27. { procedure DoStartSession;virtual;
  28. procedure DoBreakSession;virtual;}
  29. procedure DoEndSession(code:longint);virtual;
  30. procedure AnnotateError;
  31. procedure InsertBreakpoints;
  32. procedure RemoveBreakpoints;
  33. procedure ResetBreakpointsValues;
  34. procedure DoDebuggerScreen;virtual;
  35. procedure DoUserScreen;virtual;
  36. procedure Reset;virtual;
  37. procedure Run;virtual;
  38. procedure Continue;virtual;
  39. end;
  40. BreakpointType = (bt_function,bt_file_line,bt_watch,bt_awatch,bt_rwatch,bt_invalid);
  41. BreakpointState = (bs_enabled,bs_disabled,bs_deleted);
  42. PBreakpointCollection=^TBreakpointCollection;
  43. PBreakpoint=^TBreakpoint;
  44. TBreakpoint=object(TObject)
  45. typ : BreakpointType;
  46. state : BreakpointState;
  47. owner : PBreakpointCollection;
  48. Name : PString; { either function name or expr to watch }
  49. FileName : PString;
  50. OldValue,CurrentValue : Pstring;
  51. Line : Longint; { only used for bt_file_line type }
  52. Conditions : PString; { conditions relative to that breakpoint }
  53. IgnoreCount : Longint; { how many counts should be ignored }
  54. Commands : pchar; { commands that should be executed on breakpoint }
  55. GDBIndex : longint;
  56. GDBState : BreakpointState;
  57. constructor Init_function(Const AFunc : String);
  58. constructor Init_file_line(AFile : String; ALine : longint);
  59. constructor Init_type(atyp : BreakpointType;Const AnExpr : String);
  60. procedure Insert;
  61. procedure Remove;
  62. procedure Enable;
  63. procedure Disable;
  64. procedure ResetValues;
  65. destructor Done;virtual;
  66. end;
  67. TBreakpointCollection=object(TCollection)
  68. function At(Index: Integer): PBreakpoint;
  69. function GetGDB(index : longint) : PBreakpoint;
  70. function GetType(typ : BreakpointType;Const s : String) : PBreakpoint;
  71. function ToggleFileLine(Const FileName: String;LineNr : Longint) : boolean;
  72. procedure Update;
  73. procedure FreeItem(Item: Pointer); virtual;
  74. procedure ShowBreakpoints(W : PSourceWindow);
  75. end;
  76. const
  77. BreakpointTypeStr : Array[BreakpointType] of String[9]
  78. = ( 'function','file-line','watch','awatch','rwatch','invalid' );
  79. BreakpointStateStr : Array[BreakpointState] of String[8]
  80. = ( 'enabled','disabled','invalid' );
  81. var
  82. Debugger : PDebugController;
  83. BreakpointCollection : PBreakpointCollection;
  84. procedure InitDebugger;
  85. procedure DoneDebugger;
  86. procedure InitBreakpoints;
  87. procedure DoneBreakpoints;
  88. implementation
  89. uses
  90. Dos,Mouse,Video,
  91. App,Strings,
  92. FPVars,FPUtils,FPConst,
  93. FPIntf,FPCompile,FPIde;
  94. {****************************************************************************
  95. TDebugController
  96. ****************************************************************************}
  97. constructor TDebugController.Init(const exefn:string);
  98. var f: string;
  99. begin
  100. inherited Init;
  101. f := exefn;
  102. LoadFile(f);
  103. SetArgs(GetRunParameters);
  104. Debugger:=@self;
  105. InsertBreakpoints;
  106. end;
  107. procedure TDebugController.InsertBreakpoints;
  108. procedure DoInsert(PB : PBreakpoint);
  109. begin
  110. PB^.Insert;
  111. end;
  112. begin
  113. BreakpointCollection^.ForEach(@DoInsert);
  114. end;
  115. procedure TDebugController.RemoveBreakpoints;
  116. procedure DoDelete(PB : PBreakpoint);
  117. begin
  118. PB^.Remove;
  119. end;
  120. begin
  121. BreakpointCollection^.ForEach(@DoDelete);
  122. end;
  123. procedure TDebugController.ResetBreakpointsValues;
  124. procedure DoResetVal(PB : PBreakpoint);
  125. begin
  126. PB^.ResetValues;
  127. end;
  128. begin
  129. BreakpointCollection^.ForEach(@DoResetVal);
  130. end;
  131. destructor TDebugController.Done;
  132. begin
  133. { kill the program if running }
  134. Reset;
  135. RemoveBreakpoints;
  136. inherited Done;
  137. end;
  138. procedure TDebugController.Run;
  139. begin
  140. ResetBreakpointsValues;
  141. inherited Run;
  142. MyApp.SetCmdState([cmResetDebugger],true);
  143. end;
  144. procedure TDebugController.Continue;
  145. begin
  146. if not debugger_started then
  147. Run;
  148. inherited Continue;
  149. end;
  150. procedure TDebugController.Reset;
  151. var
  152. W : PSourceWindow;
  153. begin
  154. inherited Reset;
  155. MyApp.SetCmdState([cmResetDebugger],false);
  156. W:=PSourceWindow(LastSource);
  157. if assigned(W) then
  158. W^.Editor^.SetHighlightRow(-1);
  159. end;
  160. procedure TDebugController.AnnotateError;
  161. var errornb : longint;
  162. begin
  163. if error then
  164. begin
  165. errornb:=error_num;
  166. ErrorBox(#3'Error within GDB'#13#3'Error code = %d',@errornb);
  167. end;
  168. end;
  169. procedure TDebugController.DoSelectSourceLine(const fn:string;line:longint);
  170. var
  171. W: PSourceWindow;
  172. Found : boolean;
  173. PB : PBreakpoint;
  174. S : String;
  175. BreakIndex : longint;
  176. begin
  177. BreakIndex:=stop_breakpoint_number;
  178. Desktop^.Lock;
  179. if Line>0 then
  180. dec(Line);
  181. if (fn=LastFileName) then
  182. begin
  183. W:=PSourceWindow(LastSource);
  184. if assigned(W) then
  185. begin
  186. W^.Editor^.SetCurPtr(0,Line);
  187. W^.Editor^.TrackCursor(true);
  188. W^.Editor^.SetHighlightRow(Line);
  189. W^.Select;
  190. InvalidSourceLine:=false;
  191. end
  192. else
  193. InvalidSourceLine:=true;
  194. end
  195. else
  196. begin
  197. W:=TryToOpenFile(nil,fn,0,Line);
  198. if assigned(W) then
  199. begin
  200. W^.Editor^.SetHighlightRow(Line);
  201. W^.Editor^.TrackCursor(true);
  202. W^.Select;
  203. LastSource:=W;
  204. InvalidSourceLine:=false;
  205. end
  206. { only search a file once }
  207. else
  208. begin
  209. Desktop^.UnLock;
  210. Found:=MyApp.OpenSearch(fn);
  211. Desktop^.Lock;
  212. if not Found then
  213. begin
  214. InvalidSourceLine:=true;
  215. LastSource:=Nil;
  216. end
  217. else
  218. begin
  219. { should now be open }
  220. W:=TryToOpenFile(nil,fn,0,Line);
  221. W^.Editor^.SetHighlightRow(Line);
  222. W^.Editor^.TrackCursor(true);
  223. W^.Select;
  224. LastSource:=W;
  225. InvalidSourceLine:=false;
  226. end;
  227. end;
  228. end;
  229. LastFileName:=fn;
  230. Desktop^.UnLock;
  231. if BreakIndex>0 then
  232. begin
  233. PB:=BreakpointCollection^.GetGDB(stop_breakpoint_number);
  234. { For watch we should get old and new value !! }
  235. If PB^.typ<>bt_file_line then
  236. begin
  237. Command('p '+GetStr(PB^.Name));
  238. S:=StrPas(GetOutput);
  239. If Pos('=',S)>0 then
  240. S:=Copy(S,Pos('=',S)+1,255);
  241. If S[Length(S)]=#10 then
  242. Delete(S,Length(S),1);
  243. if Assigned(PB^.OldValue) then
  244. DisposeStr(PB^.OldValue);
  245. PB^.OldValue:=PB^.CurrentValue;
  246. PB^.CurrentValue:=NewStr(S);
  247. If PB^.typ=bt_function then
  248. WarningBox(#3'GDB stopped due to'#13+
  249. #3+BreakpointTypeStr[PB^.typ]+' '+GetStr(PB^.Name),nil)
  250. else if (GetStr(PB^.OldValue)<>S) then
  251. WarningBox(#3'GDB stopped due to'#13+
  252. #3+BreakpointTypeStr[PB^.typ]+' '+GetStr(PB^.Name)+#13+
  253. #3+'Old value = '+GetStr(PB^.OldValue)+#13+
  254. #3+'New value = '+GetStr(PB^.CurrentValue),nil)
  255. else
  256. WarningBox(#3'GDB stopped due to'#13+
  257. #3+BreakpointTypeStr[PB^.typ]+' '+GetStr(PB^.Name)+#13+
  258. #3+' value = '+GetStr(PB^.CurrentValue),nil);
  259. end;
  260. end;
  261. end;
  262. procedure TDebugController.DoEndSession(code:longint);
  263. var P :Array[1..2] of longint;
  264. W : PSourceWindow;
  265. begin
  266. MyApp.SetCmdState([cmResetDebugger],false);
  267. W:=PSourceWindow(LastSource);
  268. if assigned(W) then
  269. W^.Editor^.SetHighlightRow(-1);
  270. If HiddenStepsCount=0 then
  271. InformationBox(#3'Program exited with '#13#3'exitcode = %d',@code)
  272. else
  273. begin
  274. P[1]:=code;
  275. P[2]:=HiddenStepsCount;
  276. WarningBox(#3'Program exited with '#13+
  277. #3'exitcode = %d'#13+
  278. #3'hidden steps = %d',@P);
  279. end;
  280. end;
  281. procedure TDebugController.DoDebuggerScreen;
  282. begin
  283. MyApp.ShowIDEScreen;
  284. end;
  285. procedure TDebugController.DoUserScreen;
  286. begin
  287. MyApp.ShowUserScreen;
  288. end;
  289. {****************************************************************************
  290. TBreakpoint
  291. ****************************************************************************}
  292. constructor TBreakpoint.Init_function(Const AFunc : String);
  293. begin
  294. typ:=bt_function;
  295. state:=bs_enabled;
  296. GDBState:=bs_deleted;
  297. Name:=NewStr(AFunc);
  298. FileName:=nil;
  299. Line:=0;
  300. IgnoreCount:=0;
  301. Commands:=nil;
  302. Conditions:=nil;
  303. OldValue:=nil;
  304. CurrentValue:=nil;
  305. end;
  306. constructor TBreakpoint.Init_type(atyp : BreakpointType;Const AnExpr : String);
  307. begin
  308. typ:=atyp;
  309. state:=bs_enabled;
  310. GDBState:=bs_deleted;
  311. Name:=NewStr(AnExpr);
  312. IgnoreCount:=0;
  313. Commands:=nil;
  314. Conditions:=nil;
  315. OldValue:=nil;
  316. CurrentValue:=nil;
  317. end;
  318. constructor TBreakpoint.Init_file_line(AFile : String; ALine : longint);
  319. begin
  320. typ:=bt_file_line;
  321. state:=bs_enabled;
  322. GDBState:=bs_deleted;
  323. { d:test.pas:12 does not work !! }
  324. { I do not know how to solve this if
  325. if (Length(AFile)>1) and (AFile[2]=':') then
  326. AFile:=Copy(AFile,3,255);
  327. Only use base name for now !! PM }
  328. FileName:=NewStr(AFile);
  329. Name:=nil;
  330. Line:=ALine;
  331. IgnoreCount:=0;
  332. Commands:=nil;
  333. Conditions:=nil;
  334. OldValue:=nil;
  335. CurrentValue:=nil;
  336. end;
  337. procedure TBreakpoint.Insert;
  338. begin
  339. If not assigned(Debugger) then Exit;
  340. Remove;
  341. Debugger^.last_breakpoint_number:=0;
  342. if (GDBState=bs_deleted) and (state=bs_enabled) then
  343. begin
  344. if (typ=bt_file_line) then
  345. Debugger^.Command('break '+NameAndExtOf(FileName^)+':'+IntToStr(Line))
  346. else if typ=bt_function then
  347. Debugger^.Command('break '+name^)
  348. else if typ=bt_watch then
  349. Debugger^.Command('watch '+name^)
  350. else if typ=bt_awatch then
  351. Debugger^.Command('awatch '+name^)
  352. else if typ=bt_rwatch then
  353. Debugger^.Command('rwatch '+name^);
  354. if Debugger^.last_breakpoint_number<>0 then
  355. begin
  356. GDBIndex:=Debugger^.last_breakpoint_number;
  357. GDBState:=bs_enabled;
  358. Debugger^.Command('cond '+IntToStr(GDBIndex)+' '+GetStr(Conditions));
  359. Debugger^.Command('ignore '+IntToStr(GDBIndex)+' '+IntToStr(IgnoreCount));
  360. If Assigned(Commands) then
  361. begin
  362. {Commands are not handled yet }
  363. end;
  364. end
  365. else
  366. { Here there was a problem !! }
  367. begin
  368. GDBIndex:=0;
  369. ErrorBox(#3'Could not set Breakpoint'#13+
  370. #3+BreakpointTypeStr[typ]+' '+Name^,nil);
  371. state:=bs_disabled;
  372. end;
  373. end
  374. else if (GDBState=bs_disabled) and (state=bs_enabled) then
  375. Enable
  376. else if (GDBState=bs_enabled) and (state=bs_disabled) then
  377. Disable;
  378. end;
  379. procedure TBreakpoint.Remove;
  380. begin
  381. If not assigned(Debugger) then Exit;
  382. if GDBIndex>0 then
  383. Debugger^.Command('delete '+IntToStr(GDBIndex));
  384. GDBIndex:=0;
  385. GDBState:=bs_deleted;
  386. end;
  387. procedure TBreakpoint.Enable;
  388. begin
  389. If not assigned(Debugger) then Exit;
  390. if GDBIndex>0 then
  391. Debugger^.Command('enable '+IntToStr(GDBIndex))
  392. else
  393. Insert;
  394. GDBState:=bs_enabled;
  395. end;
  396. procedure TBreakpoint.Disable;
  397. begin
  398. If not assigned(Debugger) then Exit;
  399. if GDBIndex>0 then
  400. Debugger^.Command('disable '+IntToStr(GDBIndex));
  401. GDBState:=bs_disabled;
  402. end;
  403. procedure TBreakpoint.ResetValues;
  404. begin
  405. if assigned(OldValue) then
  406. DisposeStr(OldValue);
  407. OldValue:=nil;
  408. if assigned(CurrentValue) then
  409. DisposeStr(CurrentValue);
  410. CurrentValue:=nil;
  411. end;
  412. destructor TBreakpoint.Done;
  413. begin
  414. Remove;
  415. ResetValues;
  416. if assigned(Name) then
  417. DisposeStr(Name);
  418. if assigned(FileName) then
  419. DisposeStr(FileName);
  420. if assigned(Conditions) then
  421. DisposeStr(Conditions);
  422. if assigned(Commands) then
  423. StrDispose(Commands);
  424. inherited Done;
  425. end;
  426. {****************************************************************************
  427. TBreakpointCollection
  428. ****************************************************************************}
  429. function TBreakpointCollection.At(Index: Integer): PBreakpoint;
  430. begin
  431. At:=inherited At(Index);
  432. end;
  433. procedure TBreakpointCollection.FreeItem(Item: Pointer);
  434. begin
  435. if Item<>nil then
  436. Dispose(PBreakpoint(Item),Done);
  437. end;
  438. procedure TBreakpointCollection.Update;
  439. begin
  440. if assigned(Debugger) then
  441. begin
  442. Debugger^.RemoveBreakpoints;
  443. Debugger^.InsertBreakpoints;
  444. end;
  445. end;
  446. function TBreakpointCollection.GetGDB(index : longint) : PBreakpoint;
  447. function IsNum(P : PBreakpoint) : boolean;
  448. begin
  449. IsNum:=P^.GDBIndex=index;
  450. end;
  451. begin
  452. if index=0 then
  453. GetGDB:=nil
  454. else
  455. GetGDB:=FirstThat(@IsNum);
  456. end;
  457. procedure TBreakpointCollection.ShowBreakpoints(W : PSourceWindow);
  458. procedure SetInSource(P : PBreakpoint);
  459. begin
  460. If assigned(P^.FileName) and (P^.FileName^=W^.Editor^.FileName) then
  461. W^.Editor^.SetLineBreakState(P^.Line,P^.state=bs_enabled);
  462. end;
  463. begin
  464. ForEach(@SetInSource);
  465. end;
  466. function TBreakpointCollection.GetType(typ : BreakpointType;Const s : String) : PBreakpoint;
  467. function IsThis(P : PBreakpoint) : boolean;
  468. begin
  469. IsThis:=(P^.typ=typ) and (P^.Name^=S);
  470. end;
  471. begin
  472. GetType:=FirstThat(@IsThis);
  473. end;
  474. function TBreakpointCollection.ToggleFileLine(Const FileName: String;LineNr : Longint) : boolean;
  475. var PB : PBreakpoint;
  476. function IsThere(P : PBreakpoint) : boolean;
  477. begin
  478. IsThere:=(P^.typ=bt_file_line) and (P^.FileName^=FileName) and (P^.Line=LineNr);
  479. end;
  480. begin
  481. PB:=FirstThat(@IsThere);
  482. ToggleFileLine:=false;
  483. If Assigned(PB) then
  484. if PB^.state=bs_disabled then
  485. begin
  486. PB^.state:=bs_enabled;
  487. ToggleFileLine:=true;
  488. end
  489. else if PB^.state=bs_enabled then
  490. PB^.state:=bs_disabled;
  491. If not assigned(PB) then
  492. begin
  493. PB:= New(PBreakpoint,Init_file_line(FileName,LineNr));
  494. if assigned(PB) then
  495. Begin
  496. Insert(PB);
  497. ToggleFileLine:=true;
  498. End;
  499. end;
  500. Update;
  501. end;
  502. {****************************************************************************
  503. Initialize
  504. ****************************************************************************}
  505. procedure InitDebugger;
  506. begin
  507. Assign(gdb_file,'gdb$$$.out');
  508. Rewrite(gdb_file);
  509. Use_gdb_file:=true;
  510. if (not ExistsFile(ExeFile)) or (CompilationPhase<>cpDone) then
  511. DoCompile(cRun);
  512. if CompilationPhase<>cpDone then
  513. Exit;
  514. if (EXEFile='') then
  515. begin
  516. ErrorBox('Oooops, nothing to debug.',nil);
  517. Exit;
  518. end;
  519. { init debugcontroller }
  520. if assigned(Debugger) then
  521. dispose(Debugger,Done);
  522. new(Debugger,Init(ExeFile));
  523. end;
  524. procedure DoneDebugger;
  525. begin
  526. if assigned(Debugger) then
  527. dispose(Debugger,Done);
  528. Debugger:=nil;
  529. If Use_gdb_file then
  530. Close(GDB_file);
  531. Use_gdb_file:=false;
  532. end;
  533. procedure InitBreakpoints;
  534. begin
  535. New(BreakpointCollection,init(10,10));
  536. end;
  537. procedure DoneBreakpoints;
  538. begin
  539. Dispose(BreakpointCollection,Done);
  540. BreakpointCollection:=nil;
  541. end;
  542. end.
  543. {
  544. $Log$
  545. Revision 1.10 1999-02-10 09:55:07 pierre
  546. + added OldValue and CurrentValue field for watchpoints
  547. + InitBreakpoints and DoneBreakpoints
  548. + MessageBox if GDB stops bacause of a watchpoint !
  549. Revision 1.9 1999/02/08 17:43:43 pierre
  550. * RestDebugger or multiple running of debugged program now works
  551. + added DoContToCursor(F4)
  552. * Breakpoints are now inserted correctly (was mainlyy a problem
  553. of directories)
  554. Revision 1.8 1999/02/05 17:21:52 pierre
  555. Invalid_line renamed InvalidSourceLine
  556. Revision 1.7 1999/02/05 13:08:41 pierre
  557. + new breakpoint types added
  558. Revision 1.6 1999/02/05 12:11:53 pierre
  559. + SourceDir that stores directories for sources that the
  560. compiler should not know about
  561. Automatically asked for addition when a new file that
  562. needed filedialog to be found is in an unknown directory
  563. Stored and retrieved from INIFile
  564. + Breakpoints conditions added to INIFile
  565. * Breakpoints insterted and removed at debin and end of debug session
  566. Revision 1.5 1999/02/04 17:54:22 pierre
  567. + several commands added
  568. Revision 1.4 1999/02/04 13:32:02 pierre
  569. * Several things added (I cannot commit them independently !)
  570. + added TBreakpoint and TBreakpointCollection
  571. + added cmResetDebugger,cmGrep,CmToggleBreakpoint
  572. + Breakpoint list in INIFile
  573. * Select items now also depend of SwitchMode
  574. * Reading of option '-g' was not possible !
  575. + added search for -Fu args pathes in TryToOpen
  576. + added code for automatic opening of FileDialog
  577. if source not found
  578. Revision 1.3 1999/02/02 16:41:38 peter
  579. + automatic .pas/.pp adding by opening of file
  580. * better debuggerscreen changes
  581. Revision 1.2 1999/01/22 18:14:09 pierre
  582. * adaptd to changes in gdbint and gdbcon for to /
  583. Revision 1.1 1999/01/22 10:24:03 peter
  584. * first debugger things
  585. }