`Feature`: implemented [`Debugger`]
Features
DAP adapter
- Events support.
- Multi-session and multi-thread support.
- Extensions (within the limits of the protocol) have been made to basic requests, for example,
launch(nameandsourcehave been added), and some custom requests have been added (for example,restart_session). disassemble,read_memory,stack_trace, etc. according to the protocol support.
Debugger
- Multi-session and multi-thread support.
- Breakpoints support (data/function/instruction).
read_memorysupport.disassemblesupport.step_*,read_register,read_global,read_registers,read_upvaluessupport.stack_snapshot,backtracesupport.
Why was the visibility of some piccolo structures changed?
Currently, a significant portion of the piccolo API is closed, without which it's simply impossible to implement any debugger. I made part of the piccolo API public so that the debugger would have access to low-level utilities, structures, and functions of piccolo.
A bit of information about the debugger itself
About [Debugger::disassemble]
[Debugger::disassemble] receives opcodes and converts them into already compiled operations.
What is memory_reference?
memory_reference defines what information we will receive and from where. If you specify "" (empty string) in memory_reference, it takes the last frame for disassembly by default. If the string is "frame:id" or "prototype:id", it searches for the needed FunctionPrototype and disassembles it specifically.
Output format
Approximate output of the [Debugger::disassemble] method looks like this:
Disassembled { symbol: " ", index: 0, line: 2, operation: SetUpTable { table: UpValueIndex(0), key: Constant(ConstantIndex8(0)), value: Constant(ConstantIndex8(1)) } }
Disassembled { symbol: "=>", index: 1, line: 3, operation: LoadConstant { dest: RegisterIndex(0), constant: ConstantIndex16(2) } }
Disassembled { symbol: " ", index: 2, line: 4, operation: Closure { dest: RegisterIndex(1), proto: PrototypeIndex(0) } }
Disassembled { symbol: " ", index: 3, line: 4, operation: SetUpTable { table: UpValueIndex(0), key: Constant(ConstantIndex8(3)), value: Register(RegisterIndex(1)) } }
Disassembled { symbol: " ", index: 4, line: 10, operation: Closure { dest: RegisterIndex(1), proto: PrototypeIndex(1) } }
Disassembled { symbol: " ", index: 5, line: 10, operation: SetUpTable { table: UpValueIndex(0), key: Constant(ConstantIndex8(4)), value: Register(RegisterIndex(1)) } }
Disassembled { symbol: " ", index: 6, line: 15, operation: GetUpTable { dest: RegisterIndex(1), table: UpValueIndex(0), key: Constant(ConstantIndex8(4)) } }
Disassembled { symbol: " ", index: 7, line: 15, operation: Call { func: RegisterIndex(1), args: VarCount(Opt254(Some(0))), returns: VarCount(Opt254(Some(1))) } }
Disassembled { symbol: " ", index: 8, line: 15, operation: Jump { offset: 0, close_upvalues: Opt254(Some(0)) } }
Disassembled { symbol: " ", index: 9, line: 16, operation: Return { start: RegisterIndex(0), count: VarCount(Opt254(Some(0))) } }
About [Debugger::stack_snapshot] (or stack trace)
[Debugger::stack_snapshot] goes through all frames and collects information about the chunk name, its location, line, and the number of containing registers.
Output format
Approximate output of the [Debugger::stack_snapshot] method looks like this:
test.lua:2 <chunk> (2 regs)
Note: all output is described after data formatting
at the same time, [Debugger] functions completely correctly, see /examples/simple_debug.rs
you can also see tests of the main functionality in /examples/simple_debug.rs
@kyren @Aeledfyr can u review it and #130?