Skip to content

Commit

Permalink
Merge pull request #133636 from cockroachdb/blathers/backport-release…
Browse files Browse the repository at this point in the history
…-24.1-133620

release-24.1: colexecerror: improve the catcher due to a recent regression
  • Loading branch information
yuzefovich authored Nov 13, 2024
2 parents 39840bd + 5e05c2d commit 487b2ac
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 4 deletions.
23 changes: 19 additions & 4 deletions pkg/sql/colexecerror/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import (
"github.com/gogo/protobuf/proto"
)

const panicLineSubstring = "runtime/panic.go"
const (
panicLineSubstring = "runtime/panic.go"
runtimePanicFileSubstring = "runtime"
runtimePanicFunctionSubstring = "runtime."
)

// CatchVectorizedRuntimeError executes operation, catches a runtime error if
// it is coming from the vectorized engine, and returns it. If an error not
Expand Down Expand Up @@ -78,9 +82,13 @@ func CatchVectorizedRuntimeError(operation func()) (retErr error) {
// panic frame.
var panicLineFound bool
var panicEmittedFrom string
// We should be able to find it within 3 program counters, starting with the
// caller of this deferred function (2 above the runtime.Callers frame).
pc := make([]uintptr, 3)
// Usually, we'll find the offending frame within 3 program counters,
// starting with the caller of this deferred function (2 above the
// runtime.Callers frame). However, we also want to catch panics
// originating in the Go runtime with the runtime frames being returned
// by CallersFrames, so we allow for 5 more program counters for that
// case (e.g. invalid interface conversions use 2 counters).
pc := make([]uintptr, 8)
n := runtime.Callers(2, pc)
if n >= 1 {
frames := runtime.CallersFrames(pc[:n])
Expand All @@ -90,6 +98,13 @@ func CatchVectorizedRuntimeError(operation func()) (retErr error) {
if strings.Contains(frame.File, panicLineSubstring) {
panicLineFound = true
} else if panicLineFound {
if strings.HasPrefix(frame.Function, runtimePanicFunctionSubstring) &&
strings.Contains(frame.File, runtimePanicFileSubstring) {
// This frame is from the Go runtime, so we simply
// ignore it to get to the offending one within the
// CRDB.
continue
}
panicEmittedFrom = frame.Function
break
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/sql/colexecerror/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,36 @@ func TestNonCatchablePanicIsNotCaught(t *testing.T) {
})
}

type testInterface interface {
foo()
}

type testImpl1 struct{}

var _ testInterface = &testImpl1{}

func (t testImpl1) foo() {}

type testImpl2 struct{}

var _ testInterface = &testImpl2{}

func (t testImpl2) foo() {}

// TestRuntimePanicIsCaught verifies that if a runtime panic occurs in the
// safe-to-catch package (which this test package is), then it is converted into
// an internal error (#133167).
func TestRuntimePanicIsCaught(t *testing.T) {
defer leaktest.AfterTest(t)()
defer log.Scope(t).Close(t)

require.Error(t, colexecerror.CatchVectorizedRuntimeError(func() {
// Attempt an invalid interface conversion.
var o testInterface = &testImpl1{}
_ = o.(*testImpl2)
}))
}

// BenchmarkCatchVectorizedRuntimeError measures the time for
// CatchVectorizedRuntimeError to catch and process an error.
func BenchmarkCatchVectorizedRuntimeError(b *testing.B) {
Expand Down

0 comments on commit 487b2ac

Please sign in to comment.