go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/runtime/paniccatcher/catch.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package paniccatcher
    16  
    17  import (
    18  	"runtime"
    19  )
    20  
    21  // The maximum stack buffer size (64K). This is the same value used by
    22  // net.Conn's serve() method.
    23  const maxStackBufferSize = (64 << 10)
    24  
    25  // Panic is a snapshot of a panic, containing both the panic's reason and the
    26  // system stack.
    27  type Panic struct {
    28  	// Reason is the value supplied to the recover function.
    29  	Reason any
    30  	// Stack is a stack dump at the time of the panic.
    31  	Stack string
    32  }
    33  
    34  // Catch recovers from panic. It should be used as a deferred call.
    35  //
    36  // If the supplied panic callback is nil, the panic will be silently discarded.
    37  // Otherwise, the callback will be invoked with the panic's information.
    38  func Catch(cb func(p *Panic)) {
    39  	if reason := recover(); reason != nil && cb != nil {
    40  		stack := make([]byte, maxStackBufferSize)
    41  		count := runtime.Stack(stack, true)
    42  		cb(&Panic{
    43  			Reason: reason,
    44  			Stack:  string(stack[:count]),
    45  		})
    46  	}
    47  }
    48  
    49  // Do executes f. If a panic occurs during execution, the supplied callback will
    50  // be called with the panic's information.
    51  //
    52  // If the panic callback is nil, the panic will be caught and discarded silently.
    53  func Do(f func(), cb func(p *Panic)) {
    54  	defer Catch(cb)
    55  	f()
    56  }