TOPLLab / WARDuino-VSCode

🕵️ A VSCode debugger plugin for WARDuino.
Mozilla Public License 2.0
2 stars 2 forks source link

Assemblyscript sourcemapping #98

Closed tolauwae closed 1 year ago

tolauwae commented 1 year ago

Add basic support for AssemblyScript debugging using existing sourcemapping code.

carllocos commented 1 year ago

@tolauwae do you have any example code for me to test an application in AssemblyScript? I need this to review the code and make sure that everything works properly.

tolauwae commented 1 year ago

All the examples on the docs website should work: https://topllab.github.io/WARDuino/guide/examples/

For instance, blink example:

import {pinMode, PinMode, PinVoltage,
        digitalWrite, delay} from "as-warduino";

const LED = 26;

export function main(): void {
    pinMode(led, PinMode.OUTPUT);

    let pause: u32 = 1000;
    while (true) {
        digitalWrite(LED, PinVoltage.HIGH);
        delay(pause);
        digitalWrite(LED, PinVoltage.LOW);
        delay(pause);
    }
}
carllocos commented 1 year ago

Apologies if I missed this in the documentation but where can I find the as-warduino module? Running this as the documentation suggests npm install as-warduino gives me a 404 Not found error.

tolauwae commented 1 year ago

That is the glue code library: https://github.com/TOPLLab/WARDuino-libs/tree/main/glue/assemblyscript It hasn't been published yet, so you need to pack the npm package locally.

Here is a zipped archive of the entire blink example with the package: blink.zip

If you unzip this. You can just do npm install.

tolauwae commented 1 year ago

Ok thanks for testing. I will look into the issues and fix them.

For the issue with the start location, is it different from how it behaves for .wast files? Because I know I have to step once there as well before it goes to the right stop (second instruction in that case).

carllocos commented 1 year ago

For the issue with the start location, is it different from how it behaves for .wast files? Because I know I have to step once there as well before it goes to the right stop (second instruction in that case).

I recall you mentioning this and to be honest I never experienced this issue. For all the WAST files that I created the debugger had no issue highlighting the first line of the main. The files were however created manually and not via asc. Could that be the reason?

I have a suggestion for this piece of code https://github.com/TOPLLab/WARDuino-VSCode/blob/192c30cfb74225e9566108a051da21a7fb2b8b9c/src/DebugBridges/AbstractDebugBridge.ts#L135

I understand that for AssemblyScript one step can potentially mean several steps at the Wasm level. If I understand this piece of code correctly, stepping over multiple instructions simply means applying the step interrupt until the while condition is satisfied. I believe that this is inefficient since at each step we need to request the state over and over again. And unfortunately, I know that we currently have no other alternative than this approach since the VM does not provide any interrupt that lets us step over multiple instructions. In WOOD, I added an interrupt that lets us step until a program location where the program location is passed via the payload. Maybe that could make this piece of code a little bit more efficient. What do you think? The functionality will of course be provided as a separate PR.

carllocos commented 1 year ago

For the issue with the start location, is it different from how it behaves for .wast files? Because I know I have to step once there as well before it goes to the right stop (second instruction in that case).

Never mind my answer above, now I do experience the issue.

tolauwae commented 1 year ago

In WOOD, I added an interrupt that lets us step until a program location where the program location is passed via the payload.

Sounds good. But indeed not something we want to refactor in this PR.

carllocos commented 1 year ago

The debugger highlights the first sentence of the source file as the starting debug location instead of the first let in the body of the main function i.e., let led = config.LED;

Fixed in PR https://github.com/TOPLLab/WARDuino-VSCode/pull/113

carllocos commented 1 year ago
  • stepping once from pinMode(led, PinMode.OUTPUT); causes the program to advance until the digitalWrite(led, PinVoltage.HIGH); and thus skips the let pause: u32 = 1000; and while (true)

The reason why the step jumps to digitalWrite is because source mappings are missing for lines 12 (let pause: u32 = 1000;) and 13 (while (true)). The upload.wast below is the one generated by the compiler and used by the plugin to derive the source mappings. We can clearly see that no mappings are present for line 12 ;;@ blink.ts:12:x and line 13 ;;@ blink.ts:13:x.

@tolauwae I guess the problem here is not using the right compilation options. What do you think?

Looking at the asc documentation, I believe the used compilation flags are correct:

asc blink.ts --exportTable --disable bulk-memory --sourceMap -O3s --debug --outFile upload.wasm --textFile upload.wast

I also tried asc blink.ts --exportTable --disable bulk-memory --sourceMap --optimize --debug --outFile upload.wasm --textFile upload.wast which I think is more in line with the latest doc but this gives the same upload.wast as here below. So maybe the issue comes from the Assemblyscript compiler.

upload.wast

(module
 (type $i32_i32_=>_none (func (param i32 i32)))
 (type $i32_=>_none (func (param i32)))
 (type $none_=>_none (func))
 (import "env" "chip_pin_mode" (func $~lib/as-warduino/assembly/warduino/_pin_mode (param i32 i32)))
 (import "env" "chip_digital_write" (func $~lib/as-warduino/assembly/warduino/_digital_write (param i32 i32)))
 (import "env" "chip_delay" (func $~lib/as-warduino/assembly/warduino/_delay (param i32)))
 (memory $0 1)
 (data (i32.const 1036) ",")
 (data (i32.const 1048) "\01\00\00\00\1c\00\00\00B\00r\00i\00n\00k\00l\00e\00y\00 \00C\00o\00u\00r\00t")
 (data (i32.const 1084) "L")
 (data (i32.const 1096) "\01\00\00\000\00\00\00B\00a\00r\00m\00y\00 \00F\00o\00t\00h\00e\00r\00i\00n\00g\00a\00y\00 \00P\00h\00i\00p\00p\00s")
 (data (i32.const 1164) "<")
 (data (i32.const 1176) "\01\00\00\00*\00\00\00r\00a\00n\00d\00o\00m\00-\00m\00q\00t\00t\00-\00c\00l\00i\00e\00n\00t\00-\00i\00d")
 (table $0 1 funcref)
 (export "main" (func $blink/main))
 (export "memory" (memory $0))
 (export "table" (table $0))
 (func $blink/main
  ;;@ blink.ts:10:4
  (call $~lib/as-warduino/assembly/warduino/_pin_mode
   ;;@ blink.ts:9:14
   (i32.const 10)
   ;;@ ~lib/as-warduino/assembly/index.ts:95:24
   (i32.const 2)
  )
  (loop $while-continue|0
   ;;@ blink.ts:14:8
   (call $~lib/as-warduino/assembly/warduino/_digital_write
    ;;@ ~lib/as-warduino/assembly/index.ts:100:24
    (i32.const 10)
    ;;@ ~lib/as-warduino/assembly/index.ts:100:29
    (i32.const 1)
   )
   ;;@ blink.ts:15:8
   (call $~lib/as-warduino/assembly/warduino/_delay
    ;;@ ~lib/as-warduino/assembly/index.ts:41:16
    (i32.const 1000)
   )
   ;;@ blink.ts:16:8
   (call $~lib/as-warduino/assembly/warduino/_digital_write
    ;;@ ~lib/as-warduino/assembly/index.ts:100:24
    (i32.const 10)
    ;;@ ~lib/as-warduino/assembly/index.ts:100:29
    (i32.const 0)
   )
   ;;@ blink.ts:17:8
   (call $~lib/as-warduino/assembly/warduino/_delay
    ;;@ ~lib/as-warduino/assembly/index.ts:41:16
    (i32.const 1000)
   )
   (br $while-continue|0)
  )
 )
)
tolauwae commented 1 year ago

@tolauwae I guess the problem here is not using the right compilation options. What do you think?

TLDR: I don't think getting this information from sourcemapping is possible.

The fact that there is no sourcemapping for while(true) is not that strange, when debugging wast we also jump over the loop instructions straight to the first instruction of the loop.

And for variable assignments, it does kind of suck that there is no sourcemapping for it. Although for most WAT files it wil almost be impossible. Locals get declared at the start of a function, can often be updated out of order, or can be inlined.

For instance, in the example above they don't use a local. Instead the local is inlined as it is a constant value 1000. If you don't optimise it might not get inlined, but that may not be worth it. I don't know.

carllocos commented 1 year ago

The fact that there is no sourcemapping for while(true) is not that strange, when debugging wast we also jump over the loop instructions straight to the first instruction of the loop.

Actually, I have several WAST files where the loop instruction does not get jumped over. I did write them by hand though so maybe it is indeed a matter of optimisation.

Regardless, if agreed, I propose that we still approve this PR and improve the source mapping later if possible of course.