Feature Request: Inspect variable while debugging

Started by JanetC, Tue 10/01/2017 22:11:21

Previous topic - Next topic

Crimson Wizard

#20
Quote from: eri0o on Wed 10/04/2024 00:10:04I am more trying to think from the perspective that the import is in the header, so it gets into all scripts. So I think it would show everywhere. Which could crowd the variable watcher.

I suppose you imply the list that mentions all known variables?
If that's a list that has all variables from all the game, then it will have to include each unique variable only once. This may be achieved by creating a unique key, something like module.variable. If local variables are also included there, then the key would be more complex, including a function and likely a range of lines (i've been speculating on this in one of the previous comments).
If that's a list that only shows variables visible from the current place (a break point), then the variables may be filtered according to their visibility scope. I suppose that may work similar to how autocomplete works, although I haven't thought this through. In my opinion this is a secondary problem, and may be done as an extra task after the general mechanism is implemented.

My primary goal right now is to make possible for user to manually type variable's name into the list, and let Editor & engine resolve that into a value.

eri0o

#21
Quote from: Crimson Wizard on Tue 09/04/2024 22:42:192. Editor resolves the context-dependent name of a variable using a local->global var name table, and passes a globally unique variable name to Engine. Engine will still have to resolve its address using table of variables generated for the given script AND table of imports for the given script.

Uhm, just thinking back at this, I know that the Editor loads a GUID for each module pair when editing the script. Is that GUID present in the script object? Could this be used as a prefix somehow to help identify? I don't remember if we can have a case where a variable in a local context can match the name of a variable in a global context or if this blocked by the compiler.

Btw, about the three options I would suggest going for whatever you feel is the easiest to implement.

Crimson Wizard

Quote from: eri0o on Wed 10/04/2024 01:39:49Uhm, just thinking back at this, I know that the Editor loads a GUID for each module pair when editing the script. Is that GUID present in the script object?

No, GUID is purely editor thing and is not a part of the compiled script. The scripts may be identified with their "sectionname" which contains either name of header or script file. This is used when resolving types for RTTI.

Crimson Wizard

#23
Some interesting progress. Now it understands variable names, including member names:



Branch is still here:
https://github.com/ivan-mogilko/ags-refactoring/tree/ags4--draft-memwatch

This requires script compiler to generate a table of contents of script memory.
And naturally, we need RTTI to be built as well, because RTTI tells us about struct fields.

For the time being I put resolving request inside Engine, it is just faster to write dirty code there.

Currently not supported:
- Accessing array elements (but it's a matter of parsing `[ i ]`);
- Imported variables (from other scripts). These need a mechanism that resolves local declaration to their actual memory location.
- Local variables (those that are allocated on stack). These require a somewhat different approach, because they do not exist in a single instance, but are generated as functions are called (and same local variable may be generated multiple times in case of recursive call), so there's no "fixed" address.


UPDATE: arrays work now (both regular and managed).

UPDATE2: imported variables from other scripts work now.
Imports from the engine don't work yet.

eri0o

Hey @Crimson Wizard , I just thought about it now, I imagine it's not possible to look into things that are from plugins, but how would it show, like say an object that is from a plugin. Or even perhaps like a "character*" to an engine character? Was just thinking about this.

Crimson Wizard

Quote from: eri0o on Tue 16/04/2024 17:32:28Hey @Crimson Wizard , I just thought about it now, I imagine it's not possible to look into things that are from plugins, but how would it show, like say an object that is from a plugin. Or even perhaps like a "character*" to an engine character? Was just thinking about this.

I am planning to make this work at the moment, need to revert and redo some recent changes for this. But only pointer value itself and plain fields will work, not properties.

For properties to be read, the corresponding "get_" function has to be called, which may lead to unwanted side effects. Also, properties which are handled in script cannot be run without having a dedicated script instance allocated exclusively for "watching". I'd rather not touch that until the general watch feature is completed.

eri0o

#26
Oh, yeah, didn't expect those to be accessible, I was more thinking if there is a place in the list where the typename could appear maybe these could have like "(native)" or something to indicate they aren't script objects. But it's cool you have thought about this too. A lot of my System.Log usage will disappear with this new functionality (edit: the variable inspection things). :)

Crimson Wizard

Oh right, having a "type" column would be a nice addition too.

Crimson Wizard

#28
Alright, new update:

1. Now supports local variables (from stack memory), including function parameters.
Supposedly, engine correctly calculates their lifescope, and overriding names in the nested sections should also work (but I cannot remember if old and new compilers allowed to do that yet). But it works correctly if you have multiple variables with the same name inside multiple functions.
The code for local variables turned to be uglier than the rest of the draft, so some things may still be not completely reliable.

2. Added "Type" column.

Branch is still here:
https://github.com/ivan-mogilko/ags-refactoring/tree/ags4--draft-memwatch

I might open a Draft PR to let this build on CI for easier testing.



Crimson Wizard


eri0o

Very interesting stuff, haven't managed to try it out yet but will probably be playing with it tomorrow.

QuoteSave TOC in separate files, packed along with the scripts, sort of "debug symbols" for the game.

My view is this is best. It's not useful for actually running the thing and it will make the size of things bigger. I guess it will work like pdb files or dwarf or any of the many debug parts that are as separate files.

Crimson Wizard

#31
Here's an updated, cleanely written version.
New PR: https://github.com/adventuregamestudio/ags/pull/2430
Download:
https://cirrus-ci.com/task/4820306265636864
updated 3rd June 2024

Functionally this should act the same as before.
I would appreciate any testing of this feature.

Crimson Wizard

Another updated version, now with some improvements and fixes.

* variables list now may be edited by pressing F2, and there's always one empty line added in the end.
* better error reporting (bad syntax, unknown names etc).
* correct reading of member fields of builtin structs, such as GameState ("game").

Download:
https://cirrus-ci.com/task/4728318132486144
updated 12th June 2024

Crimson Wizard

Another update, as of 13th June:
https://cirrus-ci.com/task/5488189920509952

I think this is the last update, unless mistakes are found.
There's a list of known issues, they are not critical, and I decided to postpone addressing these:
https://github.com/adventuregamestudio/ags/pull/2430#issuecomment-2166800056

...because I simply got tired of working on this feature.

As of now, I know only of 2 people who tested or at least mentioned they are going to test.


eri0o

Made a modification that would automatically watch local variables, here is the PR for testing.


eri0o

Quote from: JanetC on Tue 10/01/2017 22:11:21Most debuggers allow you to hover over a variable while stepping through the code in order to inspect the contents of the variable. This would make my life so much easier!

Taking a look at the original request again, I guess for the hover it would be like a tooltip that would show the variable name and it's value (and type?)

Minor idea how to do this...

  • Once the mouse has rested for some time in the same place in the script editor it triggers something to show a tooltip (this probably already exists)
  • then it knows it selects the keyword
  • if the debugger is active and it's a variable (how?) it will show a different text
  • then there's some interface where value of the variable is requested and shown in the tooltip
  • what happens if the user steps using a hotkey? Does the tooltip closes and show again (refreshing it)

Just thinking about this UI for getting the values of the variables at runtime.

eri0o

#37
OK, after taking a look at it, it seems a first step would be to refactor the script parsing that is in scintilla wrapper to the ScriptEditorBase. The callTip (as it's called the tooltip) is constructed in the scintilla wrapper instead of the ScriptEditorBase, and the variable watch interface that should in theory be in the ScriptEditorBase - at least I think.

Other issue is that querying and getting variables back is an async operation, and not blocking, so the code for the calltip need to instead be async to, but this may delay the caltip a bit more.

Crimson Wizard

#38
I don't think it's proper to move big part of functionality from one class to another only for the purpose of editing a tooltip.
Is there a good reason to have this functionality moved to ScriptEditorBase? In general this is a question of the purposes and roles of these classes.
ScriptEditorBase was meant to be a parent class for editor pane, which may have multiple different controls on it, besides script control. I suppose that it's better to have script parsing and related behavior (such as styling and tooltips) as close to the script text control itself as possible, which is ScintillaWrapper atm.


Events are a common solution for customizing behavior.
If it's necessary to inject some extra text to tooltip, you could add a event to ScintillaWrapper, subscribe in ScriptEditorBase, and provide reaction there.
Such event could pass a keyword and its information (what is it) in its arguments.

The async operation is a problem. But maybe it could display standard tooltip first, trigger the variable request, and then modify (replace) tooltip with an extended text as soon as an answer was received. OTOH, since the tooltip is displayed with a delay anyway, maybe the event could be triggered when the tooltip "timer" starts, and then the variable request will be processed in parallel with this delay.

Not looking into the code yet, only imagining, this could work like this:
1. ScintillaWrapper realizes it needs to display a tooltip, and fires event.
2. ScriptEditorBase receives event, checks if the editor is running a game test, and queries variable.
3. As soon as ScriptEditorBase gets an answer, it tells ScintillaWrapper to display extra text in tooltip. OR displays its own tooltip, besides / below the scintilla's one (idk if easy to do).

In this scenario, you should also detect when calltip was cancelled by moving cursor elsewhere.

SMF spam blocked by CleanTalk