peon/src/util/debugger.nim

229 lines
8.6 KiB
Nim
Raw Normal View History

# Copyright 2022 Mattia Giambirtone & All Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ../frontend/meta/bytecode
import multibyte
import strformat
import strutils
import terminal
type
CFIElement = ref object
start, stop, bottom, argc: int
name: string
Debugger* = ref object
chunk: Chunk
cfiData: seq[CFIElement]
proc newDebugger*: Debugger =
## Initializes a new, empty
## debugger object
new(result)
result.cfiData = @[]
proc nl = stdout.write("\n")
proc printDebug(s: string, newline: bool = false) =
stdout.styledWrite(fgMagenta, "DEBUG - Disassembler -> ")
stdout.styledWrite(fgGreen, s)
if newline:
nl()
proc printName(opcode: OpCode, newline: bool = false) =
stdout.styledWrite(fgRed, $opcode, " (", fgYellow, $uint8(opcode), fgRed, ")")
if newline:
nl()
proc printInstruction(instruction: OpCode, newline: bool = false) =
printDebug("Instruction: ")
printName(instruction)
if newline:
nl()
proc checkFrame(self: Debugger, n: int) =
for i, e in self.cfiData:
if n == e.start:
styledEcho fgBlue, "==== Peon Bytecode Debugger - Begin Frame ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ===="
styledEcho fgGreen, "\t- Start offset: ", fgYellow, $e.start
styledEcho fgGreen, "\t- End offset: ", fgYellow, $e.stop
styledEcho fgGreen, "\t- Stack bottom: ", fgYellow, $e.bottom
styledEcho fgGreen, "\t- Argument count: ", fgYellow, $e.argc
echo ""
elif n == e.stop:
styledEcho fgBlue, "==== Peon Bytecode Debugger - End Frame ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ====\n"
proc simpleInstruction(self: Debugger, instruction: OpCode, offset: int): int =
printInstruction(instruction)
nl()
return offset + 1
proc stackTripleInstruction(self: Debugger, instruction: OpCode,
offset: int): int =
## Debugs instructions that operate on a single value on the stack using a 24-bit operand
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[
offset + 3]].fromTriple()
printInstruction(instruction)
stdout.styledWrite(fgGreen, &", points to index ")
stdout.styledWriteLine(fgYellow, &"{slot}")
return offset + 4
proc stackDoubleInstruction(self: Debugger, instruction: OpCode,
offset: int): int =
## Debugs instructions that operate on a single value on the stack using a 16-bit operand
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2]].fromDouble()
printInstruction(instruction)
stdout.write(&", points to index ")
stdout.styledWrite(fgGreen, &", points to index ")
stdout.styledWriteLine(fgYellow, &"{slot}")
return offset + 3
proc argumentDoubleInstruction(self: Debugger, instruction: OpCode, offset: int): int =
## Debugs instructions that operate on a hardcoded value on the stack using a 16-bit operand
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2]].fromDouble()
printInstruction(instruction)
stdout.styledWrite(fgGreen, &", has argument ")
stdout.styledWriteLine(fgYellow, $slot)
return offset + 3
proc argumentTripleInstruction(self: Debugger, instruction: OpCode, offset: int): int =
## Debugs instructions that operate on a hardcoded value on the stack using a 24-bit operand
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[offset + 3]].fromTriple()
printInstruction(instruction)
stdout.styledWrite(fgGreen, ", has argument ")
stdout.styledWriteLine(fgYellow, $slot)
return offset + 3
proc callInstruction(self: Debugger, instruction: OpCode, offset: int): int =
## Debugs function calls
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[offset + 3]].fromTriple()
var args = [self.chunk.code[offset + 4], self.chunk.code[offset + 5], self.chunk.code[offset + 6]].fromTriple()
printInstruction(instruction)
stdout.styledWrite(fgGreen, &", jumps to address ", fgYellow, $slot, fgGreen, " with ", fgYellow, $args, fgGreen, " argument")
if args > 1:
stdout.styledWrite(fgYellow, "s")
nl()
return offset + 7
proc constantInstruction(self: Debugger, instruction: OpCode, offset: int): int =
## Debugs instructions that operate on the constant table
var constant = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[
offset + 3]].fromTriple()
printInstruction(instruction)
stdout.styledWrite(fgGreen, &", points to constant at position ", fgYellow, $constant)
nl()
printDebug("Operand: ")
stdout.styledWriteLine(fgYellow, &"{self.chunk.consts[constant]}")
return offset + 4
proc jumpInstruction(self: Debugger, instruction: OpCode, offset: int): int =
## Debugs jumps
var jump: int
case instruction:
2022-05-16 19:40:13 +02:00
of Jump, JumpIfFalse, JumpIfTrue, JumpIfFalsePop, JumpForwards, JumpBackwards:
jump = [self.chunk.code[offset + 1], self.chunk.code[offset + 2]].fromDouble().int()
of LongJump, LongJumpIfFalse, LongJumpIfTrue, LongJumpIfFalsePop,
LongJumpForwards, LongJumpBackwards:
jump = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[
offset + 3]].fromTriple().int()
else:
discard # Unreachable
printInstruction(instruction, true)
printDebug("Jump size: ")
stdout.styledWrite(fgYellow, $jump)
nl()
return offset + 3
proc disassembleInstruction*(self: Debugger, offset: int): int =
## Takes one bytecode instruction and prints it
self.checkFrame(offset)
printDebug("Offset: ")
stdout.styledWriteLine(fgYellow, $offset)
printDebug("Line: ")
stdout.styledWriteLine(fgYellow, &"{self.chunk.getLine(offset)}")
var opcode = OpCode(self.chunk.code[offset])
case opcode:
of simpleInstructions:
result = self.simpleInstruction(opcode, offset)
of constantInstructions:
result = self.constantInstruction(opcode, offset)
of stackDoubleInstructions:
result = self.stackDoubleInstruction(opcode, offset)
of stackTripleInstructions:
result = self.stackTripleInstruction(opcode, offset)
of argumentDoubleInstructions:
result = self.argumentDoubleInstruction(opcode, offset)
of argumentTripleInstructions:
result = self.argumentTripleInstruction(opcode, offset)
of callInstructions:
result = self.callInstruction(opcode, offset)
of jumpInstructions:
result = self.jumpInstruction(opcode, offset)
else:
echo &"DEBUG - Unknown opcode {opcode} at index {offset}"
result = offset + 1
proc parseCFIData(self: Debugger) =
var
start, stop, bottom, argc: int
fnName: string
idx = 0
size = 0
while idx < len(self.chunk.cfi):
start = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1], self.chunk.cfi[idx + 2]].fromTriple())
idx += 3
stop = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1], self.chunk.cfi[idx + 2]].fromTriple())
idx += 3
bottom = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1], self.chunk.cfi[idx + 2]].fromTriple())
idx += 3
argc = int(self.chunk.cfi[idx])
inc(idx)
size = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1]].fromDouble())
idx += 2
fnName = self.chunk.cfi[idx..<idx + size].fromBytes()
inc(idx, size)
self.cfiData.add(CFIElement(start: start, stop: stop, bottom: bottom, argc: argc, name: fnName))
proc disassembleChunk*(self: Debugger, chunk: Chunk, name: string) =
## Takes a chunk of bytecode and prints it
self.chunk = chunk
styledEcho fgBlue, &"==== Peon Bytecode Debugger - Chunk '{name}' ====\n"
var index = 0
self.parseCFIData()
while index < self.chunk.code.len:
index = self.disassembleInstruction(index)
echo ""
styledEcho fgBlue, &"==== Debug session ended - Chunk '{name}' ===="