Debugger development lazarus / 12-Debugger ---------------------------------------------------------------------- Shpend · 2021-01-30 18:04 @PascalDragon Hmm, Do you guys have any other alternatives over to GDB? Or do you just stick with it and try to improve it over and over? Maybe learning Debugger development could be something I could help, well I need to learn that too, but this is IMHO far easier than compiler-development, in strict comparison. Maybe the GDB should get at some point a rewrite, or "rework" in some areas to be easier in the points it currently isn't easy. Any suggestions from you guys to that, maybe where I would/could start learning effective Debugger-Development? ---------------------------------------------------------------------- Martin_fr · 2021-01-30 19:20 Edit: This topic was originally part of https://forum.lazarus.freepascal.org/index.php/topic,53069.0.html and has been splitted out to its own thread Quote from: Shpend on January 30, 2021, 07:04:42 pm@PascalDragon Hmm, Do you guys have any other alternatives over to GDB? Yes, there now is FpDebug.  And the IDE also supports lldb. GDB is not actually bad. It is a great debugger. And even for use within a frontend it has the "mi" interface to allow other applications parsing gdb output. Yet still, its Pascal support is limited. And still the IDE has to communicate via pipes to stdout/stdin, encoding all its queries into gdb commands. The results are Gdbs C driven idea of Pascal.... (Not entirely GDBs fault though, some Pascal features are not easily represented/distinguished in Dwarf/Stabs) FpDebug is a complete debugger in Pascal for Pascal. It was started in 2006 by Marc. Development was on and off by various people. In the past few years, Joost and I put some work in it. And now it is finally use-able (i.e. it has most of the features that the gdb based debugger has). It is included in 2.0.x => package LazDebuggerFp. https://wiki.lazarus.freepascal.org/Debugger_Status The entire topic, what works or not and why... All that is rather complex. For a debugger to work, you very simplified need: - Compiler support - Debuginfo format support (i.e. Dwarf/Stabs) - Debugger backend (gdb/lldb/fpdebug) / OS support / Hardware support - Frontend / IDE support. ---------------------------------------------------------------------- Martin_fr · 2021-02-02 18:16 Quote from: Shpend on January 30, 2021, 07:04:42 pm@PascalDragon Maybe learning Debugger development could be something I could help, well I need to learn that too, but this is IMHO far easier than compiler-development, in strict comparison. Maybe the GDB should get at some point a rewrite, or "rework" in some areas to be easier in the points it currently isn't easy. Any suggestions from you guys to that, maybe where I would/could start learning effective Debugger- Development? My last post was however not meant to say there are no easy points to start with. One starting point could be the front end. It has been long neglected. Now that we have a better backend, it could do with some work. Look at the watches and local dialog. It would be cool, if - watches for structures (class/record) had a + box in front of them. So they could be unfolded, and display a line for each member. (for large structure have a filter, or field picker) - same for arrays - "instance class" instead of "declared class" should be easier accessible.    And could be "on" by default, if we had a global option for that. - re-order watches in the watch window. There is lots more in the front end. (memory dump window, multiple inspect windows, fix inspect window does not always update on stack frame change, improve stack view window, ........) In doing this, you would get to know the current "dbg-interface". Which frankly is lacking any design at all. It was just ripped out the old gdbmi code. Improving this, would give a glance into the various backends. There also are the global options (Tools / Options). They could be displayed much nicer. And there is more work to be done on "per project" settings. If you want to dive in at the deep end, then that is either the gdbmi, lldb, or gdbmi backend. There is plenty to do. But it is the deep end of the pool. Here you need your pick, which backend. - For local debugging (Intel - Win/Linux/ and someday Mac) FpDebug will be taking over. - But Gdb/Lldb will remain, and be maintained. It still has some areas where it beats FpDebug, and it is needed for remote debugging (even though fpdebug will some day do some of the remote too). Gdb is also used for targets other that the ones indicated above). If you are eyeing any particular feature... ask. ---------------------------------------------------------------------- PascalDragon · 2021-02-03 08:17 Quote from: Martin_fr on February 02, 2021, 07:16:05 pmLook at the watches and local dialog. It would be cool, if - watches for structures (class/record) had a + box in front of them. So they could be unfolded, and display a line for each member. (for large structure have a filter, or field picker) - same for arrays More often than not this would be great for the hint window as well. ---------------------------------------------------------------------- Shpend · 2021-02-03 17:56 @Martin_Fr I would love to most likely work on your new FP_Debugger-Frontend for now, since to put it frankly, this would be my 1. touch on Debugger- Coding since this shall be the future of FPC where you have already the compiler, the IDE, and finally the debugger itself written in pure FPC. Of course, the further my experience goes and the better I get at developing the debugger-frontend the higher the chance I will then dig deeper into the backend. But as mentioned, for now, I will spend my time and focus on the frontend. Can you direct/guide me a bit where Can I find the specified .pas files for the frontend of the FP_Debugger, since it's quite large, and I would like to know where exactly I should start the coding. Generally, many thanks for this topic generally, martin. I will make a list of your already written notes and see on the fly, while I am developing if I should/can add more to that list, regarding of course here still the frontend. @Pascal_Dragon I will add this to my list too haha. ---------------------------------------------------------------------- Martin_fr · 2021-02-03 19:30 I started a new wiki page https://wiki.lazarus.freepascal.org/Lazarus_Debugger_Implementation Its only a draft right now. I will fill in more info over the coming days (or weeks). You will probably need info on TDebuggerDataMonitor and TDebuggerDataSupplier as they (and the specialized subclasses) define how the FrontEnd gets any data from the debugger. All the dialogs are connected to those classes. If you for example look at the watches dialog /debugger/watchesdlg.pp Then it does get its data and events from "WatchesMonitor" (and other monitors, since it needs to react to changes of the selected thread/stack) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TDebugManager.InitWatchesDlg;var  TheDialog: TWatchesDlg;begin  TheDialog := TWatchesDlg(FDialogs[ddtWatches]);  TheDialog.WatchesMonitor := FWatches; triggers Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TDebuggerDlg.SetWatchesMonitor(const AValue: TIdeWatchesMonitor);...  FWatchesMonitor.AddNotification(FWatchesNotification); which has the callbacks Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i constructor TWatchesDlg.Create(AOwner: TComponent);begin  inherited Create(AOwner);...  WatchesNotification.OnAdd       := @WatchAdd;  WatchesNotification.OnUpdate    := @WatchUpdate;  WatchesNotification.OnRemove    := @WatchRemove;  You will also see a lot of code referring to snapshots. While you can defer looking at the nitty gritty details, you should have an idea what they represent: https://wiki.lazarus.freepascal.org/IDE_Window:_Debug_History About the state: requested, evaluating, .... All data is returned on request, but since getting the data may take time, any method will immediately return, even if the value is not yet available. Once values become avail, an event will be triggered, and the dialog re-reads the values. -- I will add more info on how the WatchesMonitor works asap. ---------------------------------------------------------------------- Martin_fr · 2021-02-03 21:08 Btw, are you aware on how to debug the IDE itself? I.e. use the IDE to debug the IDE itself? ---------------------------------------------------------------------- Shpend · 2021-02-04 11:23 Actually, no I do not sadly, I will have to look on the docs for some instructions on how to do that, but why also, is that important? ---------------------------------------------------------------------- PascalDragon · 2021-02-04 12:01 Not so much important, but helpful should you hunt some bug in the code. ---------------------------------------------------------------------- Martin_fr · 2021-02-04 12:45 Well, no problem. Debugging the IDE is actually very simple. 1) Using: Tools > Configure build lazarus: rebuild the IDE with   For Gdb:    -O- -gh -gw -godwarfsets   For FpDebug:  -O- -gh -gw3   and probably also: -Criot -Sa   (windows only) if you need a console (useful for debugln / see LazLogger): -WC 2) Open the project: ide/lazarus.lpi 3) Press: F9 When you make changes, you need to recompile via the Tools menu. F9 will not recompile (not completely anyway). There are one or two pitfalls though. Normally, when the IDE recompiles, it renames "lazarus.exe" to "lazarus.exe.old". But if the current file "lazarus.exe" is locked (happens sometimes), then it creates the new file as "lazarus.exe.new" => and that gets renamed by "startlazarus" when you restart the IDE. So, if (and only if) you see a "lazarus.exe.new" in the lazarus folder, then you need to restart the IDE (or you keep debugging the old file). Also all of the above assumes that your installation is in a user- writeable location. On Windows that is usually the case. On Linux you need to install in your home dir to get that. If the install folder is not writeable, then the new "lazarus.exe" (or an linux "lazarus") is created in the primary config path /home/abc/.lazarus In that case you need to set the debugger to run the file from that location. I haven't done that myself ever, but "Run > Run params" or maybe "project options > path > target file name" should do. And one more useful thing: Menu: Run > Run Params > Command line params --primary-config-path=/path/to/new/folder That way the debugged IDE will have its own config. - You can move the window(s) to alternate locations, so they do not conflict (or conflict less) with the windows of the outer instance. - I also recommend to change some colors, maybe the gutter background color. So you have a visible indicator, that an editor belongs to the debugged instance. And to debug the debugger, you would then open a normal project in the debugged IDE, and run/debug that project. Outer IDE   debugs: inner IDE      debugs: project. If you set a breakpoint in the Update method of the watches dialog, then you can already explore where the value come from. ---------------------------------------------------------------------- Martin_fr · 2021-02-04 12:59 Btw, what OS do you use? ---------------------------------------------------------------------- Shpend · 2021-02-05 15:56 @martin_fr Sry for the late answer, had some critical stuff to finish.. I'm using Windows 10 64bit. And to the rest, thanks martin for the detailed description on how to set that debug-ide stuff up correctly. I have recompiled the IDE itself with the parameters you have frankly provided me and there were no "lazarus.exe.new" only the .old and the normal .exe one. So I guess all went ok in that regard then. One question tho, where exactly can I see which Debugger is currently being used, like if it's GDB or FpDebug? thx! EDIT: I have solved the last part myself, works all fine with FpDebug now, the only thing I don't really understand is, how to debug the debugger, so to speak, I followed all steps you mentioned @martin_fr but it seems that when opening a normal test project and then doing a normal (f9) only compiles that project but  I don't see where to debug the debugger sadly. ---------------------------------------------------------------------- Martin_fr · 2021-02-05 16:22 Quote from: Shpend on February 05, 2021, 04:56:13 pmOne question tho, where exactly can I see which Debugger is currently being used, like if its GDB or FpDebug? thx! Tools > Options > Debugger > General In Trunk: Tools > Options > Debugger > Backend  https://wiki.lazarus.freepascal. org/IDE_Window:_DebuggerClassOptionsFrame Project > Project Options > Debugger You may need to install some packages as given in the list on top of https://wiki.lazarus.freepascal.org/Debugger_Status ---------------------------------------------------------------------- Shpend · 2021-02-05 16:46 Thx martin I now use fpDebug, but how I do debug the debugger code? So should I clone your "FpDebug" repo and then do that over there or can I find it already in the Lazarus folder? Sry I need always a bit to really start efficiently working on such things, never have worked on such tool-based projects. I apologize if these questions sound stupid to you but I only want to get super "sure" to set up all right. Edit: Ok, I jerk read again this entire thread and was then aware that you frankly provided me a link to how the debugger is implemented, and yea I have a clue now, sry.. So the frontend is directly coupled to the IDE itself which makes sense. Now I'm on my way to exactly find out where exactly I can find all of the frontend-parts of the debugger, like where is the  "watches-dialog" and so on.. so I know exactly where to begin. Thx a lot so far! I will keep you updating on my progress! ---------------------------------------------------------------------- Martin_fr · 2021-02-05 17:58 First of all: You want to install and work on svn trunk (or git master). If you start providing changes, then .... the official way is patches on the official svn. Most developers use svn only, so unless anything else is agreed upfront, it is patches. (But since git can generate patches, you can use git) I use git https://github.com/User4martin/lazarus/ If you know changes will be looked at by me, then pull requests against this repro are ok. (That is usually ok for debugger and synedit). You may want to look at https://wiki.lazarus.freepascal.org/Multiple_Lazarus Quote clone your "FpDebug" repo FpDebug is part of the overall Lazarus repro. So I assume you meant Lazarus repro. There are some standalone projects, some also name FpDebug => but they are attempts by others, and not to be concerned off (there also is duby / which is related to fpdebug, even contributed a bit). QuoteSo the frontend is directly coupled to the IDE itself Yes. But I dream about the day where it will also be a package. Quote like where is the  "watches-dialog" Not sure if that is you saying "found it" or "still looking" IF the latter, see this post (reread the entire post) Quote from: Martin_fr on February 03, 2021, 08:30:32 pmIf you for example look at the watches dialog /debugger/watchesdlg.pp ---------------------------------------------------------------------- Shpend · 2021-02-05 22:18 Ok, thanks for the details on where to start all that, I have decided to go with your repo Martin and will use that Lazarus git master. (I'm always only using git, don't want to change that so good that it works with that xD) I have some questions regarding your former post: QuoteLook at the watches and local dialog. It would be cool, if 1. watches for structures (class/record) had a + box in front of them. So they could be unfolded, and display a line for each member. (for the large structure have a filter or field picker) 2. same for arrays 3. "instance class" instead of "declared class" should be easier accessible.    And could be "on" by default, if we had a global option for that. 4. reorder watches in the watch window. Is the watch window this one here? (see attachment!) Can you elaborate on 3.) what do you mean with "instance class" or rather what shall I improver over there, sry but I didn't grasp that. And to 4.) Currently, what is working, you can already reorder the watches by ascendence or descendence(a->z or z->a) and the same for values, (0->100 or 100->0), and you want a new addition in that you can drag them via the mouse in what order the user prefers, right? And with Local Dialog, you mean the "local Variables Window", right? Again, I really need to make sure I understand all of your points correctly before I start touching any LoC, so there are no misconceptions about that haha. And also, would you suggest me to create a new branch on your repro, or is it free for me to work plainly on the master-branch? Any tips on how to do it more effectively are much welcome. One other thing quickly, how can I archive to see the "Local Variable Window" when I debug the test project I made, and open that window in "menu -> View -> DebuggerWindow -> Local Variable Window" there are no data displayed in it, even tho I followed this page: https://wiki.lazarus.freepascal.org/Debugger_Setup Currently, you see this: (see attachment: picture 2) even tho, I recompiled the entire IDE as martin suggested with all the necessary compiler options and I choose under: "tools -> Configure Lazarus IDE ->  IDE with Debug information" and also set up already the IDE to use FpDebug. (see Picture 3) ---------------------------------------------------------------------- Martin_fr · 2021-02-06 00:37 ok.... Before I start: ideas on how to implement are a general direction. If you go through the code, and come up with alternative ideas, then please discuss them. Also if you have other ideas what to add/improve. - Yes: watches = "Liste der überwachten Ausdrücke". Quote what do you mean with "instance class"Basically any structured data: classes, records / data with fields. Such data - displayed in a single line - is hard to read. It may be fine for a TPoint with just x,y. But a TForm with dozens of fields - it would be easier to read with each field in a line of its own. => And if a field happens to be another record, recursively open it up. That would mean to display values in some sort of tree-view, with expandable notes. You may have noted the crude workaround. One selected watch can be displayed multiline using the "magnify glass" from the toolbar. A full expanded view (but with even more details) is available in the "debug inspector" (which is like the object inspector from the form designer). In the debug inspector, when double clicking Form1.Fcomponents it will display you the list object. It will fetch more data from the debugger. You can see, if you extend and combine the concepts, a lot of comfort function could be added. A collapse/expand tree-view could be used for watches, locals, hints and fetch more data if required. The user could travel deeper into classes. Though that's at the far end. To see for yourself, open a TFOrm (or other component) in the debug inspector. Click the "instance" (use class of instance) button. Double click fields (especially those for other embedded objects). About "class of instance": The debugger show any object according to its declaration. "Sender: TObject" is showing only the fields declared in TObject. That is pretty useless, as sender usually is some object with a lot of other fields. "class of instance" will find out what Sender is, maybe a TButton. THen it will display all the fields TButton has. Essentially it does TButton(Sender). There are more ideas, for comfort watches. But I think the above is a good first step One part of the task is to find a suitable component for the task. The current listview does not have the capacity. That part is less to do with the debugger, and more with GUI design. The other is retrieving the data. And this will probably get you into the actual debugger code. Lets start. There are 2 main ways to get watches (or rather evaluated-expressions) 1) Creating a watch 2) DebugBoss.Evaluate(FExpression, @EvaluateCallback, Opts)  see "debug inspector" (inspectdlg.pas), "eval dialog" (evaluatedlg.pp) and hint (hidden in the IDE code, search in files for "DebugBoss.Evaluate") DebugBoss.Evaluate get the current thread/stack value. If they are changed by the user (stack window / thread window) then they need to be called again) There are different detail-levels of expression eval in the debuggers (backend) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   TDBGEvaluateFlag =    (defNoTypeInfo,        // No Typeinfo object will be returned     defSimpleTypeInfo,    // Returns: Kind (skSimple, skClass, ..); TypeName (but does make no attempt to avoid an alias)   defFullTypeInfo,      // Get all typeinfo, resolve all anchestors     defClassAutoCast      // Find real class of instance, and use, instead of declared class of variable    ); They can be set on watches, or passed to DebugBoss.Evaluate defClassAutoCast => already explained above. The other 3 are not defiend very well.... Sadly. And they may need some work done.... When a watch is evaluated, the result comes back in TWatchValue (components\debuggerintf\dbgintfdebuggerbase.pp) However not all fields may be populated. The reason for that is primarily historical: Getting data from GDB is time consuming. But a slow debugger is a pain (worst case you have 20 watches, you need the last watch, and the 19 before that take 30 seconds to evaluate. Bummer) Getting details on the type, cost extra time. => Info is only retrieved when needed. A better way would be "on request" in the sense, retrieval starts when you access the property. But for simplicity retrieval depends on the flag passed in. SO WatchValue.TypeInfo will only be available if requested. And WatchValue.TypeInfo.Members (info on each field of an object) is only available at defFullTypeInfo. FpDebug still follows that. But I am not sure if it has the exact same subset of info for each of the flags. So for your work that means: Depending on what needs to be displayed, you need to evaluate (or re- evaluate) with the correct flags. You need to test what different debuggers return. The may not deliver the info at all, which must be handled too. Debugger backends may have the info but not report it in the most practical manner. Or wrongly interpret the flags. If so, then you may need to dive into the backends (I will help) and work it out. Most of it should work though, because "debug inspector" works with all backends. Ok so far so good. The above is plenty to explore. "debug inspector" and the "magnify" in watches-window  may be a point to start studying. Then when you get started you may think about how to store additional requests. If a user unfolds Form1.FComponents, then "Form1.FComponents" must be evaluated as a watch. You can use DebugBoss.Evaluate, but that has disadvantages. - the result is not kept. If the user switches stackframe, and switches back, the data needs to be retrieved again. - for that reason the data will not be avail in "debug history" either. So creating a new watch may be an option. But it needs to be know, that it is displayed in the tree, and not top-level. For the backend, it still needs to get into the current flat list of watches. But the TIdeWatch could have an "Parent: TObject" and other linking fields. Ok, I hope I did not forget to many things. I probably did skip a few.... but once you get familiar with the different classes used by the IDE and debugger, it will start to make more sense. (I remember when I looked at them for the first time around 2007/2008. I took a while....) So the aim here is Get to know the classes involved. Then you will recognize them, when you start looking at the back-end. One last note. It is a bit off topic. But may be interesting to know. Depending on what changes will become necessary, decisions may take the next bit into account.... * DisplayFormat For some obscure reason (no idea why) "procedure TIdeWatch.DoDisplayFormatChanged;" => the watch is considered changed. It is sent to the debugger, and the debugger returns the same value with the new format. (I am not sure if that is fixed in some places)... So worst case: You retrieved "MyInteger" as 127. You set DisplayFormat to hex, and the debugger backend as to evaluate it again... Really the front end should know how to display an integer in different formats. Part of the reason, again "speed" => classes come as text only, the fronted does not get the fields (unless it passes a flag). So the frontend can not reformat it. But integer..... As I said off-topic. But keep in mind stuff. Its not a black and white choice how to solve this. Lots to consider. Ok so for so good. ---------------------------------------------------------------------- Martin_fr · 2021-02-06 00:55 QuoteAnd to 4.) Currently, what is working, you can already reorder the watches by ascendence or descendence(a->z or z->a) and the same for values, (0->100 or 100->0), and you want a new addition in that you can drag them via the mouse in what order the user prefers, right? Yes. the idea stems from 2 issues. 1) speed => move the important watches to the top / Though instead one can disable some watches.....    This would mean, that re-order would not just affect display, but also re-sort them in the WatchesMonitor/Supplier 2) relation between watches. Sometimes you have maybe an StartPoint and EndPoint. I find it annoying if there are half a dozen other watches in between. It's up to you if you want to pick up that task. It might get you to the WatchesMonitor/Supplier as the list in front and backend need to be re-ordered. QuoteAnd with Local Dialog, you mean the "local Variables Window", right? Yes. Sorry, I am working with the IDE in English locale. (I actually am German, so I understand the German terms, but I am too much used to English on my PC) QuoteAnd also, would you suggest me to create a new branch on your repro, or is it free for me to work plainly on the master-branch? Any tips on how to do it more effectively are much welcome. You will have to fork the repro (at least if you want to push). In your fork you can work an the master, or a branch. IMHO a branch (or even several) is easier, since you can manage your commits independent of the master branch. If you are familiar with "git rebase" then you can always keep it up to date (rebase onto latest master). You will have to push --force- with-lease . But in your own forked repro that is ok. Your git clone would have (at least) two remotes. 1) My git / read only 2) your fork. I can get you more on git. But maybe outside this thread. Enough info on the debugger here. (feel free to PM or open another thread) QuoteOne other thing quickly, how can I archive to see the "Local Variable Window" when I debug the test project I made, and open that window in "menu -> View -> DebuggerWindow -> Local Variable Window" there are no data displayed in it, even tho I followed this page: https://wiki.lazarus.freepascal.org/Debugger_Setup "local variables" (as a concept in the Pascal language, not just debugger) apply only to functions and procedures. Your images shows you in the main "program" body. And the variables declared above this are actually "global variables"