mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 01:27:16 +08:00
proc: do not always allocate struct literals (#3953)
Do not always allocate new struct literals to the target's memroy. Wait until they actually need to have an address. Fixes #1465
This commit is contained in:
committed by
GitHub
parent
d15845eb91
commit
a80904ca1f
@ -1279,6 +1279,9 @@ func (stack *evalStack) executeOp() {
|
||||
v := newVariable("", typeAddr, rttyp, scope.BinInfo, scope.Mem)
|
||||
stack.push(v.pointerToVariable())
|
||||
|
||||
case *evalop.PushNewFakeVariable:
|
||||
stack.pushNewFakeVariable(scope, op.Type)
|
||||
|
||||
default:
|
||||
stack.err = fmt.Errorf("internal debugger error: unknown eval opcode: %#v", op)
|
||||
}
|
||||
@ -1378,6 +1381,17 @@ func (stack *evalStack) pushIdent(scope *EvalScope, name string) (found bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (stack *evalStack) pushNewFakeVariable(scope *EvalScope, typ godwarf.Type) {
|
||||
cm, err := CreateCompositeMemory(scope.Mem, scope.BinInfo.Arch, *new(op.DwarfRegisters), []op.Piece{{Kind: op.ImmPiece, Bytes: make([]byte, typ.Size()), Size: int(typ.Size())}}, typ.Size())
|
||||
if err != nil {
|
||||
stack.err = err
|
||||
return
|
||||
}
|
||||
v := newVariable("", cm.base, typ, scope.BinInfo, cm)
|
||||
v.Flags = VariableFakeAddress
|
||||
stack.push(v)
|
||||
}
|
||||
|
||||
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
||||
ops, err := evalop.CompileAST(scopeToEvalLookup{scope}, t, scope.evalopFlags())
|
||||
if err != nil {
|
||||
|
||||
@ -111,7 +111,10 @@ func CompileSet(lookup evalLookup, lhexpr, rhexpr string, flags Flags) ([]Op, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.maybeMaterialize(rhe)
|
||||
err = ctx.maybeMaterialize(rhe)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.compileAST(lhe, false)
|
||||
if err != nil {
|
||||
@ -419,21 +422,7 @@ func (ctx *compileCtx) compileAST(t ast.Expr, toplevel bool) error {
|
||||
return errFuncCallNotAllowedLitAlloc
|
||||
}
|
||||
|
||||
ctx.compileSpecialCall("runtime.mallocgc", []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.INT, Value: "1"},
|
||||
node.Type,
|
||||
&ast.Ident{Name: "true"},
|
||||
}, []Op{
|
||||
&PushConst{Value: constant.MakeInt64(1)},
|
||||
&PushRuntimeType{dtyp},
|
||||
&PushConst{Value: constant.MakeBool(true)},
|
||||
}, specialCallDoPinning)
|
||||
ctx.pushOp(&TypeCast{
|
||||
DwarfType: godwarf.FakePointerType(dtyp, int64(ctx.PtrSize())),
|
||||
Node: &ast.CallExpr{
|
||||
Fun: node.Type,
|
||||
Args: []ast.Expr{&ast.Ident{Name: "new allocation"}}}})
|
||||
ctx.pushOp(&PointerDeref{&ast.StarExpr{X: &ast.Ident{Name: "new allocation"}}})
|
||||
ctx.pushOp(&PushNewFakeVariable{Type: dtyp})
|
||||
|
||||
for i, elt := range node.Elts {
|
||||
ctx.pushOp(&Dup{})
|
||||
@ -447,7 +436,10 @@ func (ctx *compileCtx) compileAST(t ast.Expr, toplevel bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.maybeMaterialize(elt.Value)
|
||||
err = ctx.maybeMaterialize(elt.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
ctx.pushOp(&Select{Name: typ.Field[i].Name})
|
||||
rhe = elt
|
||||
@ -455,7 +447,10 @@ func (ctx *compileCtx) compileAST(t ast.Expr, toplevel bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.maybeMaterialize(elt)
|
||||
err = ctx.maybeMaterialize(elt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx.pushOp(&Roll{1})
|
||||
ctx.pushOp(&SetValue{Rhe: rhe})
|
||||
@ -736,7 +731,10 @@ func (ctx *compileCtx) compileFunctionCallNoPinning(node *ast.CallExpr, id int)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error evaluating %q as argument %d in function %s: %v", astutil.ExprToString(arg), i+1, astutil.ExprToString(node.Fun), err)
|
||||
}
|
||||
ctx.maybeMaterialize(arg)
|
||||
err = ctx.maybeMaterialize(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.pushOp(&CallInjectionCopyArg{id: id, ArgNum: i, ArgExpr: arg})
|
||||
}
|
||||
|
||||
@ -760,12 +758,13 @@ func (ctx *compileCtx) compileFunctionCallWithPinning(node *ast.CallExpr, id int
|
||||
|
||||
for i, arg := range node.Args {
|
||||
err := ctx.compileAST(arg, false)
|
||||
if isStringLiteralThatNeedsAlloc(arg) {
|
||||
ctx.compileAllocLiteralString()
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error evaluating %q as argument %d in function %s: %v", astutil.ExprToString(arg), i+1, astutil.ExprToString(node.Fun), err)
|
||||
}
|
||||
err = ctx.maybeMaterialize(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx.pushOp(&Roll{len(node.Args)})
|
||||
@ -805,12 +804,67 @@ func (ctx *compileCtx) compilePinningLoop(id int) {
|
||||
ctx.pushOp(&CallInjectionComplete2{id: id})
|
||||
}
|
||||
|
||||
// If expr will produce a string literal allocate the string into the target
|
||||
// program's address space.
|
||||
func (ctx *compileCtx) maybeMaterialize(expr ast.Expr) {
|
||||
// If expr will produce a string literal or a pointer to a literal allocate
|
||||
// it into the target program's address space.
|
||||
func (ctx *compileCtx) maybeMaterialize(expr ast.Expr) error {
|
||||
if isStringLiteralThatNeedsAlloc(expr) {
|
||||
ctx.compileAllocLiteralString()
|
||||
return nil
|
||||
}
|
||||
|
||||
expr = removeParen(expr)
|
||||
addrof, isunary := expr.(*ast.UnaryExpr)
|
||||
if !isunary || addrof.Op != token.AND {
|
||||
return nil
|
||||
}
|
||||
lit, iscomplit := addrof.X.(*ast.CompositeLit)
|
||||
if !iscomplit {
|
||||
return nil
|
||||
}
|
||||
|
||||
dtyp, err := ctx.FindTypeExpr(lit.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch godwarf.ResolveTypedef(dtyp).(type) {
|
||||
case *godwarf.StructType, *godwarf.ArrayType:
|
||||
if _, isaddrof := ctx.ops[len(ctx.ops)-1].(*AddrOf); isaddrof {
|
||||
ctx.ops = ctx.ops[:len(ctx.ops)-1]
|
||||
} else {
|
||||
ctx.pushOp(&PointerDeref{&ast.StarExpr{X: &ast.Ident{Name: "unallocated-literal"}}})
|
||||
}
|
||||
|
||||
ctx.compileSpecialCall("runtime.mallocgc", []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.INT, Value: "1"},
|
||||
lit.Type,
|
||||
&ast.Ident{Name: "true"},
|
||||
}, []Op{
|
||||
&PushConst{Value: constant.MakeInt64(1)},
|
||||
&PushRuntimeType{dtyp},
|
||||
&PushConst{Value: constant.MakeBool(true)},
|
||||
}, specialCallDoPinning)
|
||||
ctx.pushOp(&TypeCast{
|
||||
DwarfType: godwarf.FakePointerType(dtyp, int64(ctx.PtrSize())),
|
||||
Node: &ast.CallExpr{
|
||||
Fun: lit.Type,
|
||||
Args: []ast.Expr{&ast.Ident{Name: "new allocation"}}}})
|
||||
|
||||
xderef := &ast.StarExpr{X: &ast.Ident{Name: "new-allocation"}}
|
||||
xset := &ast.Ident{Name: "literal-allocation"}
|
||||
|
||||
ctx.pushOp(&Dup{}) // stack after: [ ptrToRealLiteral, ptrToRealLiteral, fakeLiteral ]
|
||||
ctx.pushOp(&PointerDeref{xderef}) // stack after: [ realLiteral, ptrToRealLiteral, fakeLiteral ]
|
||||
ctx.pushOp(&Roll{2}) // stack after: [ fakeLiteral, realLiteral, ptrToRealLiteral ]
|
||||
ctx.pushOp(&Roll{1}) // stack after: [ realLiteral, fakeLiteral, ptrToRealLiteral ]
|
||||
ctx.pushOp(&SetValue{Rhe: xset}) // stack after: [ ptrToRealLiteral ]
|
||||
return nil
|
||||
|
||||
default:
|
||||
// either *godwarf.MapType, *godwarf.SliceType or *godwarf.FuncType
|
||||
return fmt.Errorf("allocating a literal of type %s not implemented", dtyp.String())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Listing(depth []int, ops []Op) string {
|
||||
|
||||
@ -347,3 +347,11 @@ type PushRuntimeType struct {
|
||||
}
|
||||
|
||||
func (*PushRuntimeType) depthCheck() (npop, npush int) { return 0, 1 }
|
||||
|
||||
// PushNewFakeVariable pushes a new debugger-allocated variable on to the
|
||||
// stack with the given type.
|
||||
type PushNewFakeVariable struct {
|
||||
Type godwarf.Type
|
||||
}
|
||||
|
||||
func (*PushNewFakeVariable) depthCheck() (npop, npush int) { return 0, 1 }
|
||||
|
||||
@ -190,7 +190,11 @@ func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
|
||||
return 0, errors.New("write out of bounds")
|
||||
}
|
||||
if mem.regs.ChangeFunc == nil {
|
||||
return 0, errors.New("can not write registers")
|
||||
for _, piece := range mem.pieces {
|
||||
if piece.Kind == op.RegPiece {
|
||||
return 0, errors.New("can not write registers")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy(mem.data[addr:], data)
|
||||
@ -216,7 +220,6 @@ func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
|
||||
return donesz + n, err
|
||||
}
|
||||
case op.ImmPiece:
|
||||
//TODO(aarzilli): maybe return an error if the user tried to change the value?
|
||||
// nothing to do
|
||||
default:
|
||||
panic("unsupported piece kind")
|
||||
|
||||
@ -669,6 +669,9 @@ func getEvalExpressionTestCases() []varTest {
|
||||
{"mnil[\"Malone\"]", false, "", "", "", errors.New("key not found")},
|
||||
{"m1[80:]", false, "", "", "", errors.New("map index out of bounds")},
|
||||
{"mlarge", false, "map[main.largestruct]main.largestruct [{name: \"one\", v: [256]uint8 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+192 more]}: {name: \"oneval\", v: [256]uint8 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+192 more]}, ]", "map[main.largestruct]main.largestruct [...]", "map[main.largestruct]main.largestruct", nil},
|
||||
{"m3[main.astruct{1,1}]", false, "42", "42", "int", nil},
|
||||
{"m4[main.astruct{2,2}]", false, "main.astruct {A: 22, B: 22}", "main.astruct {A: 22, B: 22}", "main.astruct", nil},
|
||||
{"m3[main.astruct{3,3}]", false, "", "", "", errors.New("key not found")},
|
||||
|
||||
// interfaces
|
||||
{"err1", true, "error(*main.astruct) *{A: 1, B: 2}", "error(*main.astruct) 0x…", "error", nil},
|
||||
@ -982,6 +985,12 @@ func TestEvalExpression(t *testing.T) {
|
||||
// this type of eval is unsupported with the current version of Go.
|
||||
return
|
||||
}
|
||||
if err != nil && err.Error() == "expression *ast.CompositeLit not implemented" {
|
||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) || runtime.GOARCH == "386" {
|
||||
// composite literals not supported before 1.22
|
||||
return
|
||||
}
|
||||
}
|
||||
if tc.err == nil {
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
|
||||
assertVariable(t, variable, tc)
|
||||
@ -1403,11 +1412,11 @@ func TestCallFunction(t *testing.T) {
|
||||
}
|
||||
|
||||
var testcases123 = []testCaseCallFunction{
|
||||
{`mul2(main.a2struct{Y: 3})`, []string{":int:6"}, nil, 1},
|
||||
{`mul2(main.a2struct{4})`, []string{":int:8"}, nil, 1},
|
||||
{`mul2(main.a2struct{Y: 3})`, []string{":int:6"}, nil, 0},
|
||||
{`mul2(main.a2struct{4})`, []string{":int:8"}, nil, 0},
|
||||
{`mul2ptr(&main.a2struct{Y: 3})`, []string{":int:6"}, nil, 1},
|
||||
{`mul2ptr(&main.a2struct{1})`, []string{":int:2"}, nil, 1},
|
||||
{`m[main.intpair{3, 1}]`, []string{`:string:"three,one"`}, nil, 1},
|
||||
{`m[main.intpair{3, 1}]`, []string{`:string:"three,one"`}, nil, 0},
|
||||
}
|
||||
|
||||
withTestProcessArgs("fncall", t, ".", nil, protest.AllNonOptimized, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
|
||||
Reference in New Issue
Block a user