Skip to content

Commit

Permalink
debug: supports scope (if, for)
Browse files Browse the repository at this point in the history
  • Loading branch information
cpunion committed Sep 30, 2024
1 parent e392956 commit 29395b3
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 93 deletions.
2 changes: 1 addition & 1 deletion _lldb/llgo_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def print_all_variables(debugger: lldb.SBDebugger, _command: str, result: lldb.S

frame = debugger.GetSelectedTarget().GetProcess(
).GetSelectedThread().GetSelectedFrame()
variables = frame.GetVariables(True, True, True, False)
variables = frame.GetVariables(True, True, True, True)

output: List[str] = []
for var in variables:
Expand Down
2 changes: 1 addition & 1 deletion _lldb/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def get_variable_value(self, var_expression: str) -> Optional[str]:

def get_all_variable_names(self) -> Set[str]:
frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
return set(var.GetName() for var in frame.GetVariables(True, True, True, False))
return set(var.GetName() for var in frame.GetVariables(True, True, True, True))

def get_current_function_name(self) -> str:
frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
Expand Down
92 changes: 81 additions & 11 deletions cl/_testdata/debug/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,33 @@ func FuncWithAllTypeParams(
) (int, error) {
// Expected:
// all variables: i8 i16 i32 i64 i u8 u16 u32 u64 u f32 f64 b c64 c128 slice arr arr2 s e f pf pi intr m c err fn
// i8: '\x01'
// i16: 2
// i32: 3
// i64: 4
// i: 5
// u8: '\x06'
// u16: 7
// u32: 8
// u64: 9
// u: 10
// f32: 11
// f64: 12
// b: true
// c64: complex64{real = 13, imag = 14}
// c128: complex128{real = 15, imag = 16}
// slice: []int{21, 22, 23}
// arr: [3]int{24, 25, 26}
// arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E{{i = 27}, {i = 28}, {i = 29}}
// s: "hello"
// e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30}
// i8: '\b'
// i16: 2
// u8: '\x06'
// u16: 7
// b: true
// c64: complex64{real = 13, imag = 14}
// c128: complex128{real = 15, imag = 16}
// s: "hello"

// Expected(skip):
// i8: '\b'
// i16: 2
// u8: '\x06'
// u16: 7
// b: true
println(
i8, i16, i32, i64, i, u8, u16, u32, u64, u,
f32, f64, b,
Expand Down Expand Up @@ -352,6 +359,63 @@ func FuncStructPtrParams(t *TinyStruct, s *SmallStruct, m *MidStruct, b *BigStru
println("done")
}

func ScopeIf(branch int) {
a := 1
// Expected:
// all variables: a branch
// a: 1
if branch == 1 {
b := 2
c := 3
// Expected:
// all variables: a b c branch
// a: 1
// b: 2
// c: 3
// branch: 1
println(a, b, c)
} else {
c := 3
d := 4
// Expected:
// all variables: a c d branch
// a: 1
// c: 3
// d: 4
// branch: 0
println(c, d)
}
// Expected:
// all variables: a branch
// a: 1
println("a:", a)
}

func ScopeFor() {
a := 1
for i := 0; i < 10; i++ {
switch i {
case 0:
println("i is 0")
// Expected:
// all variables: i a
// i: 0
// a: 1
println("i:", i)
case 1:
println("i is 1")
// Expected:
// all variables: i a
// i: 1
// a: 1
println("i:", i)
default:
println("i is", i)
}
}
println("a:", a)
}

func main() {
FuncStructParams(TinyStruct{I: 1}, SmallStruct{I: 2, J: 3}, MidStruct{I: 4, J: 5, K: 6}, BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16})
FuncStructPtrParams(&TinyStruct{I: 1}, &SmallStruct{I: 2, J: 3}, &MidStruct{I: 4, J: 5, K: 6}, &BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16})
Expand Down Expand Up @@ -392,7 +456,7 @@ func main() {
pad2: 200,
}
// Expected:
// all variables: globalInt globalStruct globalStructPtr s i err
// all variables: s i err
// s.i8: '\x01'
// s.i16: 2
// s.i32: 3
Expand All @@ -419,6 +483,8 @@ func main() {
globalStructPtr = &s
globalStruct = s
println("globalInt:", globalInt)
// Expected(skip):
// all variables: globalInt globalStruct globalStructPtr s i err
println("s:", &s)
FuncWithAllTypeStructParam(s)
println("called function with struct")
Expand All @@ -437,14 +503,18 @@ func main() {
s.fn,
)
println(i, err)
println("called function with types")
ScopeIf(1)
ScopeIf(0)
ScopeFor()
println(globalStructPtr)
println(&globalStruct)
s.i8 = 0x12
println(s.i8)
// Expected:
// all variables: globalInt globalStruct globalStructPtr s i err
// all variables: s i err
// s.i8: '\x12'

// Expected(skip):
// globalStruct.i8: '\x01'
println((*globalStructPtr).i8)
println("done")
Expand Down
56 changes: 37 additions & 19 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
sig = types.NewSignatureType(nil, nil, nil, params, results, false)
}
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, f.Origin() != nil)
if debugSymbols {
fn.Inline(llssa.NoInline)
}
}

if nblk := len(f.Blocks); nblk > 0 {
Expand Down Expand Up @@ -292,25 +295,18 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
return fn, nil, goFunc
}

func (p *context) getDebugPosition(f *ssa.Function, pos token.Pos) token.Position {
ppos := p.goProg.Fset.Position(pos)
bodyPos := p.getFuncBodyPos(f)
if bodyPos.Filename == "" {
return ppos
}
if ppos.Line < bodyPos.Line {
return bodyPos
}
return ppos
}

func (p *context) getFuncBodyPos(f *ssa.Function) token.Position {
if f.Object() != nil {
return p.goProg.Fset.Position(f.Object().(*types.Func).Scope().Pos())
}
return p.goProg.Fset.Position(f.Pos())
}

func isGlobal(v *types.Var) bool {
// TODO(lijie): better implementation
return strings.HasPrefix(v.Parent().String(), "package ")
}

func (p *context) debugRef(b llssa.Builder, v *ssa.DebugRef) {
object := v.Object()
variable, ok := object.(*types.Var)
Expand All @@ -322,15 +318,21 @@ func (p *context) debugRef(b llssa.Builder, v *ssa.DebugRef) {
// skip *ssa.FieldAddr
return
}
if isGlobal(variable) {
// avoid generate local variable debug info of global variable in function
return
}
pos := p.goProg.Fset.Position(v.Pos())
value := p.compileValue(b, v.X)
fn := v.Parent()
dbgVar := p.getLocalVariable(b, fn, variable)
scope := variable.Parent()
diScope := b.DIScope(p.fn, scope)
if v.IsAddr {
// *ssa.Alloc
b.DIDeclare(variable, value, dbgVar, p.fn, pos, b.Func.Block(v.Block().Index))
b.DIDeclare(variable, value, dbgVar, diScope, pos, b.Func.Block(v.Block().Index))
} else {
b.DIValue(variable, value, dbgVar, p.fn, pos, b.Func.Block(v.Block().Index))
b.DIValue(variable, value, dbgVar, diScope, pos, b.Func.Block(v.Block().Index))
}
}

Expand Down Expand Up @@ -527,10 +529,6 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
}
log.Panicln("unreachable:", iv)
}
if debugSymbols {
pos := p.getDebugPosition(iv.Parent(), iv.Pos())
b.DISetCurrentDebugLocation(p.fn, pos)
}
switch v := iv.(type) {
case *ssa.Call:
ret = p.call(b, llssa.Call, &v.Call)
Expand Down Expand Up @@ -698,11 +696,30 @@ func (p *context) jumpTo(v *ssa.Jump) llssa.BasicBlock {
return fn.Block(succs[0].Index)
}

func (p *context) getDebugLocScope(v *ssa.Function, pos token.Pos) *types.Scope {
if v.Object() == nil {
return nil
}
funcScope := v.Object().(*types.Func).Scope()
if funcScope == nil {
return nil

Check warning on line 705 in cl/compile.go

View check run for this annotation

Codecov / codecov/patch

cl/compile.go#L705

Added line #L705 was not covered by tests
}
return funcScope.Innermost(pos)
}

func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
if iv, ok := instr.(instrOrValue); ok {
p.compileInstrOrValue(b, iv, false)
return
}
if debugSymbols {
scope := p.getDebugLocScope(instr.Parent(), instr.Pos())
if scope != nil {
diScope := b.DIScope(p.fn, scope)
pos := p.fset.Position(instr.Pos())
b.DISetCurrentDebugLocation(diScope, pos)
}
}
switch v := instr.(type) {
case *ssa.Store:
va := v.Addr
Expand Down Expand Up @@ -779,7 +796,8 @@ func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.V
return b.DIVarParam(p.fn, pos, v.Name(), t, argNo)
}
}
return b.DIVarAuto(p.fn, pos, v.Name(), t)
scope := b.DIScope(p.fn, v.Parent())
return b.DIVarAuto(scope, pos, v.Name(), t)
}

func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) {
Expand Down
3 changes: 3 additions & 0 deletions cl/instr.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj
}
sig := fn.Signature
aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false, fn.Origin() != nil)
if debugSymbols {
aFn.Inline(llssa.NoInline)
}
}
}
return
Expand Down
23 changes: 22 additions & 1 deletion ssa/decl.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ func (p Function) NewBuilder() Builder {
b := prog.ctx.NewBuilder()
// TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize()
return &aBuilder{b, nil, p, p.Pkg, prog, make(map[Expr]dbgExpr)}
return &aBuilder{b, nil, p, p.Pkg, prog,
make(map[Expr]dbgExpr), make(map[*types.Scope]DIScope)}
}

// HasBody reports whether the function has a body.
Expand Down Expand Up @@ -327,3 +328,23 @@ func (p Function) SetRecover(blk BasicBlock) {
}

// -----------------------------------------------------------------------------

type inlineAttr int

const (
NoInline inlineAttr = iota
AlwaysInline
InlineHint
)

func (p Function) Inline(inline inlineAttr) {
inlineAttrName := map[inlineAttr]string{
NoInline: "noinline",
AlwaysInline: "alwaysinline",
InlineHint: "inlinehint",
}[inline]
inlineAttr := p.Pkg.mod.Context().CreateEnumAttribute(llvm.AttributeKindID(inlineAttrName), 0)
p.impl.AddFunctionAttr(inlineAttr)
}

// -----------------------------------------------------------------------------
Loading

0 comments on commit 29395b3

Please sign in to comment.