The Sophia Debugger

The Sophia debugger was introduced in version v3.0.0 of aerepl. In this post, I will guide you on setting it up on a Linux or macOS machine, highlight its current capabilities, provide an overview of how it works, and discuss potential future plans.

How to Get It Running

To run the new Sophia debugger, you need to have Erlang/OTP version 24 or above installed. Follow these steps to set up the aerepl, which includes the debugger:

  1. Clone the aerepl repository: git clone --recurse-submodules
  2. Switch to version v3.0.0, which introduced the debugger: git switch v3.0.0
  3. Build aerepl: make

Once the build process is complete, start the repl by running the following command: ./aerepl.

While it’s preferable to have rlwrap installed for command history, you can still use the repl without it. If rlwrap is installed, you can run the repl using the command: rlwrap ./aerepl.

What It Can Do

Setting and Unsetting Breakpoints

  • :break FILE_NAME LINE_NUM (short: :b): Adds a breakpoint at the specified line in the given file.
  • :delete_break INDEX (short: :db): Deletes an existing breakpoint based on its index. To view all set breakpoints along with their indexes, use the command: :print breakpoints.
  • :delete_break_loc FILE_NAME LINE_NUM (short: :dbl): Deletes an existing breakpoint based on its location (file name and line number).

Stepping Through the Code

  • :stepin (short: :si): Resumes execution after a breakpoint is hit and proceeds to the next line.
  • :stepover (short: :sv): Resumes execution after a breakpoint is hit, skipping over function calls and proceeding to the next line.
  • :stepout (short: :so): Resumes execution after a breakpoint is hit, continuing until the current function is finished.
  • :continue (short: :c): Resumes execution after a breakpoint is hit and continues until the next breakpoint.

Printing Various Information

  • :print_var VAR_NAME (short: :pv): Prints the value of a variable.
  • :location (short: :loc): Prints the Sophia source file, highlighting the currently executing line.
  • :stacktrace (short: :bt): Prints the stack trace at the current point of execution.

Working Example

First, the contract to be debugged should be in a file, let that file be called C.aes:

contract C =
  entrypoint sum(n) =
    if (n == 0)
      n + sum(n - 1)

To start the repl:


Similar output should show up:

The following should demonstrate how to load a file, set a breakpoint, execute some code, and inspect a variable’s value.

First, to load a contract and create an instance of that contract:

AESO> :load C.aes
AESO> let c = Chain.create() : C

Then a breakpoint should be set, in this example it’s set at line 6, in the file C.aes (which is already loaded):

AESO> :break C.aes 6

The entrypoint is called with a value 3 for example:

AESO> c.sum(3)

In the above, the Break is shown because a breakpoint was hit during execution, also the prompt changes to AESO(DBG)> instead of AESO> indicating that the debugger is currently at a breakpoint. To find the current location of the file where the breakpoint is:

AESO(DBG)> :location
| 2   entrypoint sum(n) =
| 3     if (n == 0)
| 4       0
| 5     else
> 6       n + sum(n - 1)
| 7

To inspect the value of the variable n:

AESO(DBG)> :print_var n

The execution could be resumed using any of the resume commands mentioned in the previous section. To resume execution until the next breakpoint is hit:

AESO(DBG)> :continue
AESO(DBG)> :print_var n

And to resume execution until the current function returns, regardless of the remaining breakpoints inside that function:

AESO(DBG)> :stepout

The return value of the function is printed and the prompt is set back to AESO>.

How It Works

After the release v7.2.1, the Sophia language compiler introduced the ability to generate debugging symbols (with the debug_info compiler flag). To ensure well-generated debugging code, it’s necessary to disable all optimizations by using the appropriate compiler flags. These options enable the compiler to produce debugging instructions that provide the necessary information for the debugging process.

The FATE VM utilizes this information, and the debugger queries the VM for the required information depending on the commands issued to the debugger. The following FATE instructions have been added to support this process:

  • DBG_LOC FILE_NAME LINE_NUM: This instruction informs the VM about the location in the contract source file once it’s executed.
  • DBG_DEF VAR_NAME REGISTER: With this instruction, the VM is informed that a specific variable is defined and corresponds to the given register.
  • DBG_UNDEF VAR_NAME REGISTER: This instruction indicates to the VM that a specific variable, associated with a particular register, is out of scope.
  • DBG_CONTRACT CONTRACT_NAME: This instruction notifies the VM that the execution is currently within a specific contract. While the VM knows only the contract’s address by default, this instruction provides its name.

Future Plans

  • Conditional breakpoints: Pauses program execution only when a specified condition is met (e.g. pause when a variable value is more than 10).
  • FATE assembly preview and step: Currently it’s only possible to step through the lines of the Sophia code.
  • Better user interface: Ability to run the debugger from a web browser, or run it from inside of VS Code and set breakpoints from there.

Final note

The debugger is still in an early stage. Even though it’s totally usable, bugs could show up between now and then. The more users we have for the debugger, the easier it would be to find those bugs and fix them.

If you find any bug or have any question or suggestion while using it, please do not hesitate to open an issue on aerepl GitHub repository.


Can you make a video?

1 Like

awesome job, I will look into this, would be nice to somehow include it into aestudio one day, maybe with the rust compiler implementation? :wink:

1 Like

There is a video now on the Aeternity YouTube channel: Sophia Debugger Demo.