blob: 53b26cf035b5c69e2d54d0945ad1409f408daba1 [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package paniccatcher
import (
"runtime"
)
// The maximum stack buffer size (64K). This is the same value used by
// net.Conn's serve() method.
const maxStackBufferSize = (64 << 10)
// Panic is a snapshot of a panic, containing both the panic's reason and the
// system stack.
type Panic struct {
// Reason is the value supplied to the recover function.
Reason any
// Stack is a stack dump at the time of the panic.
Stack string
}
// Catch recovers from panic. It should be used as a deferred call.
//
// If the supplied panic callback is nil, the panic will be silently discarded.
// Otherwise, the callback will be invoked with the panic's information.
func Catch(cb func(p *Panic)) {
if reason := recover(); reason != nil && cb != nil {
stack := make([]byte, maxStackBufferSize)
count := runtime.Stack(stack, true)
cb(&Panic{
Reason: reason,
Stack: string(stack[:count]),
})
}
}
// Do executes f. If a panic occurs during execution, the supplied callback will
// be called with the panic's information.
//
// If the panic callback is nil, the panic will be caught and discarded silently.
func Do(f func(), cb func(p *Panic)) {
defer Catch(cb)
f()
}
// PCall calls a nullary function and returns the panic if there was one.
func PCall(fn func()) (ret *Panic) {
defer Catch(func(thePanic *Panic) {
ret = thePanic
})
fn()
return
}