improved terminal output for DWARF LOCALS command

This commit is contained in:
JetSetIlly 2023-07-26 15:09:08 +01:00
parent 8a93f5b404
commit a815f9c6ae
16 changed files with 135 additions and 115 deletions

View file

@ -339,7 +339,7 @@ func (bld *build) buildTypes(src *Source) error {
num := fld.Val.(int64) + 1
src.types[baseTypeOffset] = &SourceType{
Name: fmt.Sprintf("[%d] %s", num, arrayBaseType.Name),
Name: fmt.Sprintf("%s", arrayBaseType.Name),
Size: arrayBaseType.Size * int(num),
ElementType: arrayBaseType,
ElementCount: int(num),

View file

@ -71,6 +71,7 @@ type frameSection struct {
cie map[uint32]*frameSectionCIE
fde []*frameSectionFDE
// the derivation for the framebase is written to the io.Writer
derivation io.Writer
}

View file

@ -19,6 +19,7 @@ import (
"debug/elf"
"encoding/binary"
"fmt"
"io"
"github.com/jetsetilly/gopher2600/logger"
)
@ -93,11 +94,6 @@ type loclistOperator struct {
resolve func(*loclist) (loclistStack, error)
}
type loclistDerivation struct {
operator string
value uint32
}
type loclistResult struct {
address uint64
hasAddress bool
@ -107,22 +103,20 @@ type loclistResult struct {
pieces []loclistPiece
}
func (l *loclistDerivation) String() string {
return fmt.Sprintf("%s %08x", l.operator, l.value)
}
type loclist struct {
coproc loclistCoproc
ctx loclistFramebase
list []loclistOperator
derivation []loclistDerivation
stack []loclistStack
pieces []loclistPiece
stack []loclistStack
pieces []loclistPiece
singleLoc bool
loclistPtr int64
// the derivation for the loclist is written to the io.Writer
derivation io.Writer
}
func (sec *loclistSection) newLoclistJustContext(ctx loclistFramebase) *loclist {
@ -272,7 +266,6 @@ func (loc *loclist) resolve() (loclistResult, error) {
}
// clear lists
loc.derivation = loc.derivation[:0]
loc.stack = loc.stack[:0]
loc.pieces = loc.pieces[:0]
@ -312,12 +305,9 @@ func (loc *loclist) resolve() (loclistResult, error) {
// all functionality of a piece operation is contained in the actual loclistOperator implementation
}
// always add result value to derivation list
d := loclistDerivation{
operator: loc.list[i].operator,
value: s.value,
if loc.derivation != nil {
loc.derivation.Write([]byte(fmt.Sprintf("%s %08x", loc.list[i].operator, s.value)))
}
loc.derivation = append(loc.derivation, d)
}
// return assembled pieces

View file

@ -937,15 +937,8 @@ func (src *Source) FindSourceLine(addr uint32) *SourceLine {
// UpdateGlobalVariables using the current state of the emulated coprocessor.
// Local variables are updated when coprocessor yields (see OnYield() function)
func (src *Source) UpdateGlobalVariables() {
var touch func(varb *SourceVariable)
touch = func(varb *SourceVariable) {
varb.Update()
for i := 0; i < varb.NumChildren(); i++ {
touch(varb.Child(i))
}
}
for _, varb := range src.SortedGlobals.Variables {
touch(varb)
varb.Update()
}
}

View file

@ -298,8 +298,7 @@ type SourceType struct {
// size of values of this type (in bytes)
Size int
// empty if type is not a composite type. see SourceVariable.IsComposite()
// function
// empty if type is not a composite type. see SourceVariable.IsComposite() function
Members []*SourceVariable
// number of elements in the type. if count is more than zero then this
@ -311,7 +310,22 @@ type SourceType struct {
}
func (typ *SourceType) String() string {
return typ.Name
s := strings.Builder{}
s.WriteString(typ.Name)
s.WriteString(fmt.Sprintf(" %dbytes", typ.Size))
if typ.Constant {
s.WriteString(" is constant")
}
if typ.IsComposite() {
s.WriteString(" is composite")
}
if typ.IsArray() {
s.WriteString(" is array")
}
if typ.IsPointer() {
s.WriteString(" is pointer")
}
return s.String()
}
// IsComposite returns true if SourceType is a composite type.

View file

@ -18,6 +18,7 @@ package dwarf
import (
"errors"
"fmt"
"io"
"strings"
"sync/atomic"
)
@ -56,10 +57,9 @@ type SourceVariable struct {
// variable can never be located
loclist *loclist
// if ErrorOnResolve is not nil then an error was enountered during a
// resolve() sequence. the error will be logged when the field is first set
// to true
ErrorOnResolve error
// if Error is not nil then an error was enountered during a resolve()
// sequence. the error will be logged when the field is first set to true
Error error
// most recent resolved value retrieved from emulation
cachedLocation atomic.Value // loclistResult
@ -71,23 +71,15 @@ type SourceVariable struct {
func (varb *SourceVariable) String() string {
var s strings.Builder
if !varb.loclist.singleLoc {
s.WriteString(fmt.Sprintf("[%04x] ", varb.loclist.loclistPtr))
}
s.WriteString(fmt.Sprintf("%s = ", varb.decl()))
if varb.ErrorOnResolve != nil {
s.WriteString(varb.ErrorOnResolve.Error())
s.WriteString(fmt.Sprintf("%s %s = ", varb.Type.Name, varb.Name))
if varb.Error != nil {
s.WriteString("error")
} else {
s.WriteString(fmt.Sprintf(varb.Type.Hex(), varb.Value()))
}
return s.String()
}
// decl returns the type-name and name pair
func (varb *SourceVariable) decl() string {
return fmt.Sprintf("%s %s", varb.Type.Name, varb.Name)
}
// Address returns the location in memory of the variable referred to by
// SourceVariable
func (varb *SourceVariable) Address() (uint64, bool) {
@ -136,14 +128,6 @@ func (varb *SourceVariable) piece(idx int) (loclistPiece, bool) {
return r.pieces[idx], true
}
// Derivation returns the sequence of results that led to the most recent value.
func (varb *SourceVariable) Derivation() []loclistDerivation {
if varb.loclist == nil {
return []loclistDerivation{}
}
return varb.loclist.derivation
}
// NumChildren returns the number of children for this variable
func (varb *SourceVariable) NumChildren() int {
return len(varb.children)
@ -172,27 +156,38 @@ func (varb *SourceVariable) Update() {
}
}
// WriteDerivation outputs the derivation of a varibale to the io.Writer. If the
// derivation encounters an error the error is returned
//
// Note that the basic information about the variable is not output by this
// function. The String() function provides that information
func (varb *SourceVariable) WriteDerivation(w io.Writer) error {
old := varb.loclist.derivation
varb.loclist.derivation = w
defer func() {
varb.loclist.derivation = old
}()
varb.resolve()
return varb.Error
}
// resolve address/value
func (varb *SourceVariable) resolve() loclistResult {
if varb.ErrorOnResolve != nil {
return loclistResult{}
}
if varb.loclist == nil {
varb.ErrorOnResolve = fmt.Errorf("there is no location to resolve")
varb.Error = fmt.Errorf("there is no location to resolve")
return loclistResult{}
}
r, err := varb.loclist.resolve()
if err != nil {
varb.ErrorOnResolve = err
varb.Error = err
return loclistResult{}
}
if r.hasAddress {
v, ok := varb.loclist.coproc.CoProcRead32bit(uint32(r.address))
if !ok {
varb.ErrorOnResolve = errors.New(fmt.Sprintf("error resolving address %08x", r.address))
varb.Error = errors.New(fmt.Sprintf("error resolving address %08x", r.address))
return loclistResult{}
}
r.value = v

View file

@ -20,14 +20,21 @@ import (
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
)
// State records the most recent yield.
// State records the most recent yield
type State struct {
Addr uint32
Reason mapper.CoProcYieldType
LocalVariables []*dwarf.SourceVariableLocal
}
// Cmp returns true if two YieldStates are equal.
func (y State) Cmp(w State) bool {
return y.Addr == w.Addr && y.Reason == w.Reason
// Cmp returns true if two YieldStates are equal
func (yld State) Cmp(w State) bool {
return yld.Addr == w.Addr && yld.Reason == w.Reason
}
// UpdateLocalVariables using the current state of the emulated coprocessor
func (yld State) UpdateLocalVariables() {
for _, varb := range yld.LocalVariables {
varb.Update()
}
}

View file

@ -1550,11 +1550,13 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
case "LOCALS":
var derivation bool
var ranges bool
var err bool
option, ok := tokens.Get()
for ok {
derivation = derivation || option == "DERIVATION"
ranges = ranges || option == "RANGES"
err = err || option == "ERROR"
option, ok = tokens.Get()
}
@ -1565,17 +1567,21 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
})
dbg.CoProcDev.BorrowYieldState(func(yld yield.State) {
var w io.Writer
if derivation {
w = dbg.writerInStyle(terminal.StyleFeedbackSecondary, "\t")
}
for _, l := range yld.LocalVariables {
l.Update()
dbg.printLine(terminal.StyleFeedback, l.String())
if ranges {
dbg.printLine(terminal.StyleFeedback, fmt.Sprintf(" %s", l.Range))
}
if derivation {
for _, d := range l.Derivation() {
dbg.printLine(terminal.StyleFeedback, fmt.Sprintf(" %s\n", d.String()))
e := l.WriteDerivation(w)
if err && e != nil {
for _, s := range strings.Split(e.Error(), ":") {
dbg.printLine(terminal.StyleError, fmt.Sprintf("\t%s", s))
}
}
if ranges {
dbg.printLine(terminal.StyleFeedbackSecondary, fmt.Sprintf("\t%s", l.Range.String()))
}
}
})
case "FRAMEBASE":

View file

@ -123,7 +123,7 @@ var commandTemplate = []string{
// peripherals (components that might not be present)
cmdPlusROM + " (NICK [%<name>S]|ID [%<id>S]|HOST [%<host>S]|PATH [%<path>S])",
cmdCoProc + " (ID|LIST [FAULTS|SOURCEFILES|FUNCTIONS]|TOP (%<top>N)|MEM|REGS|SET %<register>N %<value>N)",
cmdDWARF + " [FUNCTIONS|GLOBALS|LOCALS {DERIVATION|RANGES}|FRAMEBASE {DERIVATION}|LINE %<file:line>S|CALLSTACK|CALLERS %<function>S]",
cmdDWARF + " [FUNCTIONS|GLOBALS|LOCALS {DERIVATION|RANGES|ERROR}|FRAMEBASE {DERIVATION}|LINE %<file:line>S|CALLSTACK|CALLERS %<function>S]",
// user input
cmdPeripheral + " [LEFT|RIGHT] (AUTO|STICK|PADDLE|KEYPAD|GAMEPAD|SAVEKEY|ATARIVOX)",

View file

@ -52,18 +52,21 @@ func (dbg *Debugger) printLine(sty terminal.Style, s string, a ...interface{}) {
// io.Writer is required and you want to direct the output to the terminal.
// allows the application of a single style.
type styleWriter struct {
dbg *Debugger
style terminal.Style
dbg *Debugger
style terminal.Style
prefix string
}
func (dbg *Debugger) writerInStyle(sty terminal.Style) *styleWriter { // nolint: unparam
func (dbg *Debugger) writerInStyle(sty terminal.Style, prefix ...string) *styleWriter { // nolint: unparam
p := strings.Join(prefix, " ")
return &styleWriter{
dbg: dbg,
style: sty,
dbg: dbg,
style: sty,
prefix: p,
}
}
func (wrt styleWriter) Write(p []byte) (n int, err error) {
wrt.dbg.printLine(wrt.style, string(p))
wrt.dbg.printLine(wrt.style, "%s%s", wrt.prefix, string(p))
return len(p), nil
}

View file

@ -43,6 +43,9 @@ func (ct *ColorTerminal) TermPrintLine(style terminal.Style, s string) {
case terminal.StyleFeedback:
ct.EasyTerm.TermPrint(ansi.DimPens["white"])
case terminal.StyleFeedbackSecondary:
ct.EasyTerm.TermPrint(ansi.DimPens["gray"])
case terminal.StyleCPUStep:
ct.EasyTerm.TermPrint(ansi.Pens["yellow"])

View file

@ -27,25 +27,28 @@ const (
// "normalised" (eg. capitalised, leading space removed, etc.)
StyleEcho Style = iota
// information from the internal help system.
// information from the internal help system
StyleHelp
// non-error information from a command.
// information from a command
StyleFeedback
// disassembly output at CPU cycle boundaries.
// secondary information from a command
StyleFeedbackSecondary
// disassembly output at CPU cycle boundaries
StyleCPUStep
// disassembly output at video cycle boundaries.
// disassembly output at video cycle boundaries
StyleVideoStep
// information about the machine.
// information about the machine
StyleInstrument
// information as a result of an error. errors can be generated by the
// emulation or the debugger.
// emulation or the debugger
StyleError
// information from the internal logging system.
// information from the internal logging system
StyleLog
)

View file

@ -183,16 +183,17 @@ type imguiColors struct {
SaveKeyBitPointer imgui.Vec4
// terminal
TermBackground imgui.Vec4
TermInput imgui.Vec4
TermStyleEcho imgui.Vec4
TermStyleHelp imgui.Vec4
TermStyleFeedback imgui.Vec4
TermStyleCPUStep imgui.Vec4
TermStyleVideoStep imgui.Vec4
TermStyleInstrument imgui.Vec4
TermStyleError imgui.Vec4
TermStyleLog imgui.Vec4
TermBackground imgui.Vec4
TermInput imgui.Vec4
TermStyleEcho imgui.Vec4
TermStyleHelp imgui.Vec4
TermStyleFeedback imgui.Vec4
TermStyleFeedbackSecondary imgui.Vec4
TermStyleCPUStep imgui.Vec4
TermStyleVideoStep imgui.Vec4
TermStyleInstrument imgui.Vec4
TermStyleError imgui.Vec4
TermStyleLog imgui.Vec4
// helpers
ToolTipBG imgui.Vec4
@ -380,16 +381,17 @@ func newColors() *imguiColors {
SaveKeyBitPointer: imgui.Vec4{0.8, 0.8, 0.8, 1.0},
// terminal
TermBackground: imgui.Vec4{0.1, 0.1, 0.2, 0.9},
TermInput: imgui.Vec4{0.1, 0.1, 0.25, 0.9},
TermStyleEcho: imgui.Vec4{0.8, 0.8, 0.8, 1.0},
TermStyleHelp: imgui.Vec4{1.0, 1.0, 1.0, 1.0},
TermStyleFeedback: imgui.Vec4{1.0, 1.0, 1.0, 1.0},
TermStyleCPUStep: imgui.Vec4{0.9, 0.9, 0.5, 1.0},
TermStyleVideoStep: imgui.Vec4{0.7, 0.7, 0.3, 1.0},
TermStyleInstrument: imgui.Vec4{0.1, 0.95, 0.9, 1.0},
TermStyleError: imgui.Vec4{0.8, 0.3, 0.3, 1.0},
TermStyleLog: imgui.Vec4{0.8, 0.7, 0.3, 1.0},
TermBackground: imgui.Vec4{0.1, 0.1, 0.2, 0.9},
TermInput: imgui.Vec4{0.1, 0.1, 0.25, 0.9},
TermStyleEcho: imgui.Vec4{0.8, 0.8, 0.8, 1.0},
TermStyleHelp: imgui.Vec4{1.0, 1.0, 1.0, 1.0},
TermStyleFeedback: imgui.Vec4{1.0, 1.0, 1.0, 1.0},
TermStyleFeedbackSecondary: imgui.Vec4{0.8, 0.8, 0.8, 1.0},
TermStyleCPUStep: imgui.Vec4{0.9, 0.9, 0.5, 1.0},
TermStyleVideoStep: imgui.Vec4{0.7, 0.7, 0.3, 1.0},
TermStyleInstrument: imgui.Vec4{0.1, 0.95, 0.9, 1.0},
TermStyleError: imgui.Vec4{0.8, 0.3, 0.3, 1.0},
TermStyleLog: imgui.Vec4{0.8, 0.7, 0.3, 1.0},
// helpers
ToolTipBG: imgui.Vec4{0.2, 0.1, 0.2, 0.8},

View file

@ -251,14 +251,14 @@ func drawVariableTooltipShort(varb *dwarf.SourceVariable, cols *imguiColors) {
imgui.Text(fmt.Sprintf("Line: %d", varb.DeclLine.LineNumber))
imgui.PopStyleColor()
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
imgui.Spacing()
imgui.Separator()
imgui.Spacing()
imgui.Text(string(fonts.CoProcBug))
imgui.SameLine()
imgui.Text("Error on Resolve")
for _, l := range strings.Split(varb.ErrorOnResolve.Error(), ":") {
for _, l := range strings.Split(varb.Error.Error(), ":") {
imgui.Text("·")
imgui.SameLine()
imgui.Text(strings.TrimSpace(l))
@ -354,13 +354,13 @@ func drawVariableTooltip(varb *dwarf.SourceVariable, value uint32, cols *imguiCo
imgui.Text(fmt.Sprintf("Line: %d", varb.DeclLine.LineNumber))
imgui.PopStyleColor()
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
imgui.Spacing()
imgui.Separator()
imgui.Spacing()
imgui.Text(string(fonts.CoProcBug))
imgui.SameLine()
imgui.Text(varb.ErrorOnResolve.Error())
imgui.Text(varb.Error.Error())
}
}
@ -413,7 +413,7 @@ func (win *winCoProcGlobals) drawVariable(src *dwarf.Source, varb *dwarf.SourceV
// value column shows tree open/close icon unless there was an error
// during variable resolution
imgui.TableNextColumn()
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
imgui.Text(string(fonts.CoProcBug))
} else {
if win.openNodes[nodeID] {
@ -430,7 +430,7 @@ func (win *winCoProcGlobals) drawVariable(src *dwarf.Source, varb *dwarf.SourceV
}
} else {
win.img.imguiTooltip(func() {
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
drawVariableTooltipShort(varb, win.img.cols)
} else {
drawVariableTooltip(varb, varb.Value(), win.img.cols)
@ -452,7 +452,7 @@ func (win *winCoProcGlobals) drawVariable(src *dwarf.Source, varb *dwarf.SourceV
imgui.PopStyleColor()
imgui.TableNextColumn()
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
imgui.Text(string(fonts.CoProcBug))
} else {
imgui.Text(fmt.Sprintf(varb.Type.Hex(), varb.Value()))

View file

@ -167,7 +167,7 @@ func (win *winCoProcLocals) drawVariable(varb *dwarf.SourceVariable, indentLevel
// value column shows tree open/close icon unless there was an error
// during variable resolution
imgui.TableNextColumn()
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
imgui.Text(string(fonts.CoProcBug))
} else {
if win.openNodes[nodeID] {
@ -185,7 +185,7 @@ func (win *winCoProcLocals) drawVariable(varb *dwarf.SourceVariable, indentLevel
} else {
win.img.imguiTooltip(func() {
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
drawVariableTooltipShort(varb, win.img.cols)
} else {
drawVariableTooltip(varb, varb.Value(), win.img.cols)
@ -198,7 +198,7 @@ func (win *winCoProcLocals) drawVariable(varb *dwarf.SourceVariable, indentLevel
imgui.PopStyleColor()
imgui.TableNextColumn()
if varb.ErrorOnResolve != nil {
if varb.Error != nil {
imgui.Text(string(fonts.CoProcBug))
} else {
imgui.Text(fmt.Sprintf(varb.Type.Hex(), varb.Value()))

View file

@ -331,6 +331,9 @@ func (l terminalOutput) draw(wrap bool) {
case terminal.StyleFeedback:
imgui.PushStyleColor(imgui.StyleColorText, l.cols.TermStyleFeedback)
case terminal.StyleFeedbackSecondary:
imgui.PushStyleColor(imgui.StyleColorText, l.cols.TermStyleFeedbackSecondary)
case terminal.StyleCPUStep:
imgui.PushStyleColor(imgui.StyleColorText, l.cols.TermStyleCPUStep)