Recent

Author Topic: Debugger development  (Read 2176 times)

Shpend

  • Full Member
  • ***
  • Posts: 148
Re: Debugger development
« Reply #15 on: February 05, 2021, 11:18:33 pm »
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:

Quote
Look 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)
« Last Edit: February 06, 2021, 12:06:32 am by Shpend »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7312
  • Debugger - SynEdit - and more
    • wiki
Re: Debugger development
« Reply #16 on: February 06, 2021, 01:37:01 am »
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][+][-]
  1.   TDBGEvaluateFlag =
  2.     (defNoTypeInfo,        // No Typeinfo object will be returned
  3.      defSimpleTypeInfo,    // Returns: Kind (skSimple, skClass, ..); TypeName (but does make no attempt to avoid an alias)
  4.      defFullTypeInfo,      // Get all typeinfo, resolve all anchestors
  5.      defClassAutoCast      // Find real class of instance, and use, instead of declared class of variable
  6.     );
  7.  
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.
« Last Edit: February 06, 2021, 02:05:43 am by Martin_fr »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7312
  • Debugger - SynEdit - and more
    • wiki
Re: Debugger development
« Reply #17 on: February 06, 2021, 01:55:29 am »
Quote
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?

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.

Quote
And 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)

Quote
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.
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)


Quote
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
"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"



« Last Edit: February 06, 2021, 01:57:16 am by Martin_fr »

 

TinyPortal © 2005-2018