Debugger: things would have been easier
Project: SmallForth
So I eventually got round to adding the beginnings of a debugger. It would have been a lot easier converting the C++-based control flow to Forth if I had written this first.
The idea is to be able to debug calls into DOCOL, which all second level (:-created) words do, by having a state variable #debugstate. This would normally be 0, but if it is set to 1, a call to C++-based DOCOL will forward the call to a method called DOCOL_Debug.
This new version of DOCOL will first of all, alter any further calls to DOCOL to actually call the debug version. It then will paused before every step to prompt the user on what they want to do.
The user can choose to (O) step-over the WORD, (I) step-into the WORD as long as it is a second-level word, or (R) run execution.
These options meant having to add two more states to the #debugstate:
- 0: not debugging
- 1: stepping through code
- 2: running
- 3: stepping out of a WORD, unnesting to the previous word.
Debugging can be started with #debug and stopped with #sdebug. This just alters the #debugstate state variable. Although threads are not yet implemented, this state variable is in thread-local storage, thus, only the current thread will debug. When threads are implemented, a decision will need to be taken on if all threads are to debugged, or whether debugging only affects threads forked from the one being debugged.
Breakpoints are also supported. These can be added to any level-2 word by invoking #setbp, toggled using #togbp, and removed using #rembp. These methods expect ( n $ADDR -- ), and are invoked like this:/p>
6 ` if #setbp
Viewing the source to a second level word has now been altered, so it shows where breakpoints are:
` if see
An active breakpoint has a +B after the IP (instruction pointer) number, and xB for a disabled breakpoint.
These can also be set when debugging:
- B: Add breakpoint
- X: Remove breakpoint
- E: enable breakpoint
- D: disable breakpoint
Moving from debugging to execution by hitting R will now stop at the next breakpoint.
The stack can be displayed at any point by pressing the following during debugging:
- 1: Display the data stack (.s)
- 2: Display the return stack (.rs)
- 3: Display the temporary stack (.ts)
Future directions:
- Currently words being compiled are displayed, but not allows to be stepped through. This would help debug defining words.
- Fix halting via ctrl-c
- Allow any WORD to be flagged as 'not-to-be-debugged'. Currently the code disabled debugging before executing .ts, .s or .rs, to allow the stack to be displayed. It would be beneficial if there were debug versions of these words that had a flag to indicate that they should not be debugged.
- Control the console, to display the past and future words, like a normal debugger, with the stack traces displayed too. Note, this should be written in Forth itself.
- Allow any Forth to be entered during debugging, in a limited area of the console.