Skip to content

Commit 404127c

Browse files
committed
cmd/compile: fix off-by-one error in traceback argument counting
For traceback argument printing, we want to print at most 10 words, then print "..." if there are still more args and/or fields. The current code has off-by-one error that for 11 non-aggregate typed args, it prints the first 10 but without the "...". Also, for aggregate-typed args, in some cases it may print an extra "..." when there is actually no more fields. The problem for this is that visitType return false (meaning not to continue visiting) if it reaches the limit anywhere during the recursive visit. It doesn't distinguish whether it has printed anything for the current arg. If it reaches the limit before it prints anything, it means that we're visiting the extra arg/field, so the caller should print "..." and stop. If it prints something then reaches the limit, however, the caller should keep going, and only print "..." at the next iteration when there is actually an extra arg/field. This CL does so. Fixes #47159. Change-Id: I93fc25b73ada2b5a98df780c45e5b0c9565dc2fc Reviewed-on: https://go-review.googlesource.com/c/go/+/334710 Trust: Cherry Mui <[email protected]> Run-TryBot: Cherry Mui <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 6298cfe commit 404127c

File tree

2 files changed

+190
-32
lines changed

2 files changed

+190
-32
lines changed

src/cmd/compile/internal/ssagen/ssa.go

+15-28
Original file line numberDiff line numberDiff line change
@@ -6598,6 +6598,7 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
65986598
x := base.Ctxt.Lookup(fmt.Sprintf("%s.arginfo%d", f.LSym.Name, f.ABI))
65996599

66006600
PtrSize := int64(types.PtrSize)
6601+
uintptrTyp := types.Types[types.TUINTPTR]
66016602

66026603
isAggregate := func(t *types.Type) bool {
66036604
return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice()
@@ -6641,31 +6642,28 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
66416642
n := 0
66426643
writebyte := func(o uint8) { wOff = objw.Uint8(x, wOff, o) }
66436644

6644-
// Write one non-aggrgate arg/field/element if there is room.
6645-
// Returns whether to continue.
6646-
write1 := func(sz, offset int64) bool {
6647-
if n >= limit {
6648-
return false
6649-
}
6645+
// Write one non-aggrgate arg/field/element.
6646+
write1 := func(sz, offset int64) {
66506647
if offset >= _special {
66516648
writebyte(_offsetTooLarge)
66526649
} else {
66536650
writebyte(uint8(offset))
66546651
writebyte(uint8(sz))
66556652
}
66566653
n++
6657-
return true
66586654
}
66596655

66606656
// Visit t recursively and write it out.
66616657
// Returns whether to continue visiting.
66626658
var visitType func(baseOffset int64, t *types.Type, depth int) bool
66636659
visitType = func(baseOffset int64, t *types.Type, depth int) bool {
66646660
if n >= limit {
6661+
writebyte(_dotdotdot)
66656662
return false
66666663
}
66676664
if !isAggregate(t) {
6668-
return write1(t.Size(), baseOffset)
6665+
write1(t.Size(), baseOffset)
6666+
return true
66696667
}
66706668
writebyte(_startAgg)
66716669
depth++
@@ -6675,58 +6673,47 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
66756673
n++
66766674
return true
66776675
}
6678-
var r bool
66796676
switch {
66806677
case t.IsInterface(), t.IsString():
6681-
r = write1(PtrSize, baseOffset) &&
6682-
write1(PtrSize, baseOffset+PtrSize)
6678+
_ = visitType(baseOffset, uintptrTyp, depth) &&
6679+
visitType(baseOffset+PtrSize, uintptrTyp, depth)
66836680
case t.IsSlice():
6684-
r = write1(PtrSize, baseOffset) &&
6685-
write1(PtrSize, baseOffset+PtrSize) &&
6686-
write1(PtrSize, baseOffset+PtrSize*2)
6681+
_ = visitType(baseOffset, uintptrTyp, depth) &&
6682+
visitType(baseOffset+PtrSize, uintptrTyp, depth) &&
6683+
visitType(baseOffset+PtrSize*2, uintptrTyp, depth)
66876684
case t.IsComplex():
6688-
r = write1(t.Size()/2, baseOffset) &&
6689-
write1(t.Size()/2, baseOffset+t.Size()/2)
6685+
_ = visitType(baseOffset, types.FloatForComplex(t), depth) &&
6686+
visitType(baseOffset+t.Size()/2, types.FloatForComplex(t), depth)
66906687
case t.IsArray():
6691-
r = true
66926688
if t.NumElem() == 0 {
66936689
n++ // {} counts as a component
66946690
break
66956691
}
66966692
for i := int64(0); i < t.NumElem(); i++ {
66976693
if !visitType(baseOffset, t.Elem(), depth) {
6698-
r = false
66996694
break
67006695
}
67016696
baseOffset += t.Elem().Size()
67026697
}
67036698
case t.IsStruct():
6704-
r = true
67056699
if t.NumFields() == 0 {
67066700
n++ // {} counts as a component
67076701
break
67086702
}
67096703
for _, field := range t.Fields().Slice() {
67106704
if !visitType(baseOffset+field.Offset, field.Type, depth) {
6711-
r = false
67126705
break
67136706
}
67146707
}
67156708
}
6716-
if !r {
6717-
writebyte(_dotdotdot)
6718-
}
67196709
writebyte(_endAgg)
6720-
return r
6710+
return true
67216711
}
67226712

6723-
c := true
67246713
for _, a := range abiInfo.InParams() {
6725-
if !c {
6726-
writebyte(_dotdotdot)
6714+
if !visitType(a.FrameOffset(abiInfo), a.Type, 0) {
67276715
break
67286716
}
6729-
c = visitType(a.FrameOffset(abiInfo), a.Type, 0)
67306717
}
67316718
writebyte(_endSeq)
67326719
if wOff > maxLen {

src/runtime/traceback_test.go

+175-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ func TestTracebackArgs(t *testing.T) {
1919
}{
2020
// simple ints
2121
{
22-
func() int { return testTracebackArgs1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) },
23-
"testTracebackArgs1(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...)",
22+
func() int { return testTracebackArgs1(1, 2, 3, 4, 5) },
23+
"testTracebackArgs1(0x1, 0x2, 0x3, 0x4, 0x5)",
2424
},
2525
// some aggregates
2626
{
@@ -53,6 +53,58 @@ func TestTracebackArgs(t *testing.T) {
5353
},
5454
"testTracebackArgs5(0x0, {0x1, {}, {{}, {}}}, {}, {}, {}, {}, {}, ...)",
5555
},
56+
57+
// edge cases for ...
58+
// no ... for 10 args
59+
{
60+
func() int { return testTracebackArgs6a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) },
61+
"testTracebackArgs6a(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa)",
62+
},
63+
// has ... for 11 args
64+
{
65+
func() int { return testTracebackArgs6b(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) },
66+
"testTracebackArgs6b(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...)",
67+
},
68+
// no ... for aggregates with 10 words
69+
{
70+
func() int { return testTracebackArgs7a([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) },
71+
"testTracebackArgs7a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa})",
72+
},
73+
// has ... for aggregates with 11 words
74+
{
75+
func() int { return testTracebackArgs7b([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}) },
76+
"testTracebackArgs7b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...})",
77+
},
78+
// no ... for aggregates, but with more args
79+
{
80+
func() int { return testTracebackArgs7c([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 11) },
81+
"testTracebackArgs7c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}, ...)",
82+
},
83+
// has ... for aggregates and also for more args
84+
{
85+
func() int { return testTracebackArgs7d([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 12) },
86+
"testTracebackArgs7d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...}, ...)",
87+
},
88+
// nested aggregates, no ...
89+
{
90+
func() int { return testTracebackArgs8a(testArgsType8a{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}}) },
91+
"testTracebackArgs8a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}})",
92+
},
93+
// nested aggregates, ... in inner but not outer
94+
{
95+
func() int { return testTracebackArgs8b(testArgsType8b{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}}) },
96+
"testTracebackArgs8b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}})",
97+
},
98+
// nested aggregates, ... in outer but not inner
99+
{
100+
func() int { return testTracebackArgs8c(testArgsType8c{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}, 11}) },
101+
"testTracebackArgs8c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}, ...})",
102+
},
103+
// nested aggregates, ... in both inner and outer
104+
{
105+
func() int { return testTracebackArgs8d(testArgsType8d{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}, 12}) },
106+
"testTracebackArgs8d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}, ...})",
107+
},
56108
}
57109
for _, test := range tests {
58110
n := test.fn()
@@ -64,11 +116,11 @@ func TestTracebackArgs(t *testing.T) {
64116
}
65117

66118
//go:noinline
67-
func testTracebackArgs1(a, b, c, d, e, f, g, h, i, j, k, l int) int {
119+
func testTracebackArgs1(a, b, c, d, e int) int {
68120
n := runtime.Stack(testTracebackArgsBuf[:], false)
69121
if a < 0 {
70122
// use in-reg args to keep them alive
71-
return a + b + c + d + e + f + g + h + i + j + k + l
123+
return a + b + c + d + e
72124
}
73125
return n
74126
}
@@ -119,3 +171,122 @@ func testTracebackArgs5(a bool, x struct {
119171
}
120172
return n
121173
}
174+
175+
//go:noinline
176+
func testTracebackArgs6a(a, b, c, d, e, f, g, h, i, j int) int {
177+
n := runtime.Stack(testTracebackArgsBuf[:], false)
178+
if a < 0 {
179+
// use in-reg args to keep them alive
180+
return a + b + c + d + e + f + g + h + i + j
181+
}
182+
return n
183+
}
184+
185+
//go:noinline
186+
func testTracebackArgs6b(a, b, c, d, e, f, g, h, i, j, k int) int {
187+
n := runtime.Stack(testTracebackArgsBuf[:], false)
188+
if a < 0 {
189+
// use in-reg args to keep them alive
190+
return a + b + c + d + e + f + g + h + i + j + k
191+
}
192+
return n
193+
}
194+
195+
//go:noinline
196+
func testTracebackArgs7a(a [10]int) int {
197+
n := runtime.Stack(testTracebackArgsBuf[:], false)
198+
if a[0] < 0 {
199+
// use in-reg args to keep them alive
200+
return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9]
201+
}
202+
return n
203+
}
204+
205+
//go:noinline
206+
func testTracebackArgs7b(a [11]int) int {
207+
n := runtime.Stack(testTracebackArgsBuf[:], false)
208+
if a[0] < 0 {
209+
// use in-reg args to keep them alive
210+
return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10]
211+
}
212+
return n
213+
}
214+
215+
//go:noinline
216+
func testTracebackArgs7c(a [10]int, b int) int {
217+
n := runtime.Stack(testTracebackArgsBuf[:], false)
218+
if a[0] < 0 {
219+
// use in-reg args to keep them alive
220+
return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + b
221+
}
222+
return n
223+
}
224+
225+
//go:noinline
226+
func testTracebackArgs7d(a [11]int, b int) int {
227+
n := runtime.Stack(testTracebackArgsBuf[:], false)
228+
if a[0] < 0 {
229+
// use in-reg args to keep them alive
230+
return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10] + b
231+
}
232+
return n
233+
}
234+
235+
type testArgsType8a struct {
236+
a, b, c, d, e, f, g, h int
237+
i [2]int
238+
}
239+
type testArgsType8b struct {
240+
a, b, c, d, e, f, g, h int
241+
i [3]int
242+
}
243+
type testArgsType8c struct {
244+
a, b, c, d, e, f, g, h int
245+
i [2]int
246+
j int
247+
}
248+
type testArgsType8d struct {
249+
a, b, c, d, e, f, g, h int
250+
i [3]int
251+
j int
252+
}
253+
254+
//go:noinline
255+
func testTracebackArgs8a(a testArgsType8a) int {
256+
n := runtime.Stack(testTracebackArgsBuf[:], false)
257+
if a.a < 0 {
258+
// use in-reg args to keep them alive
259+
return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1]
260+
}
261+
return n
262+
}
263+
264+
//go:noinline
265+
func testTracebackArgs8b(a testArgsType8b) int {
266+
n := runtime.Stack(testTracebackArgsBuf[:], false)
267+
if a.a < 0 {
268+
// use in-reg args to keep them alive
269+
return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2]
270+
}
271+
return n
272+
}
273+
274+
//go:noinline
275+
func testTracebackArgs8c(a testArgsType8c) int {
276+
n := runtime.Stack(testTracebackArgsBuf[:], false)
277+
if a.a < 0 {
278+
// use in-reg args to keep them alive
279+
return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.j
280+
}
281+
return n
282+
}
283+
284+
//go:noinline
285+
func testTracebackArgs8d(a testArgsType8d) int {
286+
n := runtime.Stack(testTracebackArgsBuf[:], false)
287+
if a.a < 0 {
288+
// use in-reg args to keep them alive
289+
return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2] + a.j
290+
}
291+
return n
292+
}

0 commit comments

Comments
 (0)