Initial commit

This commit is contained in:
eleksir
2024-09-17 23:58:51 +03:00
commit a9e0095dc9
1569 changed files with 656940 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
// Copyright 2019 The Cockroach 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 withstack
import (
"fmt"
"path/filepath"
"strings"
"github.com/cockroachdb/errors/errbase"
)
// GetOneLineSource extracts the file/line/function information
// of the topmost caller in the innermost recorded stack trace.
// The filename is simplified to remove the path prefix.
// This is used e.g. to populate the "source" field in
// PostgreSQL errors.
func GetOneLineSource(err error) (file string, line int, fn string, ok bool) {
// We want the innermost entry: start by recursing.
if c := errbase.UnwrapOnce(err); c != nil {
if file, line, fn, ok = GetOneLineSource(c); ok {
return
}
}
// If we reach this point, we haven't found anything in the cause so
// far. Look at the current level.
// If we have a stack trace in the style of github.com/pkg/errors
// (either from there or our own withStack), use it.
if st, ok := err.(errbase.StackTraceProvider); ok {
return getOneLineSourceFromPkgStack(st.StackTrace())
}
// If we have flattened a github.com/pkg/errors-style stack
// trace to a string, it will happen in the error's safe details
// and we need to parse it.
if sd, ok := err.(errbase.SafeDetailer); ok {
details := sd.SafeDetails()
if len(details) > 0 {
switch errbase.GetTypeKey(err) {
case pkgFundamental, pkgWithStackName, ourWithStackName:
return getOneLineSourceFromPrintedStack(details[0])
}
}
}
// No conversion available - no stack trace.
return "", 0, "", false
}
func getOneLineSourceFromPkgStack(
st errbase.StackTrace,
) (file string, line int, fn string, ok bool) {
if len(st) > 0 {
st = st[:1]
// Note: the stack trace logic changed between go 1.11 and 1.12.
// Trying to analyze the frame PCs point-wise will cause
// the output to change between the go versions.
stS := fmt.Sprintf("%+v", st)
return getOneLineSourceFromPrintedStack(stS)
}
return "", 0, "", false
}
func getOneLineSourceFromPrintedStack(st string) (file string, line int, fn string, ok bool) {
// We only need 3 lines: the function/file/line info will be on the first two lines.
// See parsePrintedStack() for details.
lines := strings.SplitN(strings.TrimSpace(st), "\n", 3)
if len(lines) > 0 {
_, file, line, fnName := parsePrintedStackEntry(lines, 0)
if fnName != "unknown" {
_, fn = functionName(fnName)
}
return filepath.Base(file), line, fn, true
}
return "", 0, "", false
}

View File

@@ -0,0 +1,204 @@
// Copyright 2019 The Cockroach 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 withstack
import (
"errors"
"fmt"
"go/build"
"path/filepath"
"strconv"
"strings"
"github.com/cockroachdb/errors/errbase"
"github.com/getsentry/sentry-go"
pkgErr "github.com/pkg/errors"
)
// ReportableStackTrace aliases the type of the same name in the sentry
// package. This is used by the 'report' error package.
type ReportableStackTrace = sentry.Stacktrace
// GetReportableStackTrace extracts a stack trace embedded in the
// given error in the format suitable for Sentry reporting.
//
// This supports:
// - errors generated by github.com/pkg/errors (either generated
// locally or after transfer through the network),
// - errors generated with WithStack() in this package,
// - any other error that implements a StackTrace() method
// returning a StackTrace from github.com/pkg/errors.
//
// Note: Sentry wants the oldest call frame first, so
// the entries are reversed in the result.
func GetReportableStackTrace(err error) *ReportableStackTrace {
// If we have a stack trace in the style of github.com/pkg/errors
// (either from there or our own withStack), use it.
if st, ok := err.(errbase.StackTraceProvider); ok {
return convertPkgStack(st.StackTrace())
}
// If we have flattened a github.com/pkg/errors-style stack
// trace to a string, it will happen in the error's safe details
// and we need to parse it.
if sd, ok := err.(errbase.SafeDetailer); ok {
details := sd.SafeDetails()
if len(details) > 0 {
switch errbase.GetTypeKey(err) {
case pkgFundamental, pkgWithStackName, ourWithStackName:
return parsePrintedStack(details[0])
}
}
}
// No conversion available - no stack trace.
return nil
}
type frame = sentry.Frame
// convertPkgStack converts a StackTrace from github.com/pkg/errors
// to a Stacktrace in github.com/getsentry/sentry-go.
func convertPkgStack(st errbase.StackTrace) *ReportableStackTrace {
// If there are no frames, the entire stacktrace is nil.
if len(st) == 0 {
return nil
}
// Note: the stack trace logic changed between go 1.11 and 1.12.
// Trying to analyze the frame PCs point-wise will cause
// the output to change between the go versions.
return parsePrintedStack(fmt.Sprintf("%+v", st))
}
// trimPath is a copy of the same function in package sentry-go.
func trimPath(filename string) string {
for _, prefix := range trimPaths {
if trimmed := strings.TrimPrefix(filename, prefix); len(trimmed) < len(filename) {
return trimmed
}
}
return filename
}
var trimPaths []string
// init is a copy of the same function in package sentry-go.
func init() {
// Collect all source directories, and make sure they
// end in a trailing "separator"
for _, prefix := range build.Default.SrcDirs() {
if prefix[len(prefix)-1] != filepath.Separator {
prefix += string(filepath.Separator)
}
trimPaths = append(trimPaths, prefix)
}
}
// functionName is an adapted copy of the same function in package sentry-go.
func functionName(fnName string) (pack string, name string) {
name = fnName
// We get this:
// runtime/debug.*T·ptrmethod
// and want this:
// pack = runtime/debug
// name = *T.ptrmethod
if idx := strings.LastIndex(name, "."); idx != -1 {
pack = name[:idx]
name = name[idx+1:]
}
name = strings.Replace(name, "·", ".", -1)
return
}
// parsePrintedStack reverse-engineers a reportable stack trace from
// the result of printing a github.com/pkg/errors stack trace with format %+v.
func parsePrintedStack(st string) *ReportableStackTrace {
// A printed stack trace looks like a repetition of either:
// "unknown"
// or
// <result of fn.Name()>
// <tab><file>:<linenum>
// It's also likely to contain a heading newline character(s).
var frames []frame
lines := strings.Split(strings.TrimSpace(st), "\n")
for i := 0; i < len(lines); i++ {
nextI, file, line, fnName := parsePrintedStackEntry(lines, i)
i = nextI
// Compose the frame.
frame := frame{
AbsPath: file,
Filename: trimPath(file),
Lineno: line,
InApp: true,
Module: "unknown",
Function: fnName,
}
if fnName != "unknown" {
// Extract the function/module details.
frame.Module, frame.Function = functionName(fnName)
}
frames = append(frames, frame)
}
if frames == nil {
return nil
}
// Sentry wants the frames with the oldest first, so reverse them.
for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
frames[i], frames[j] = frames[j], frames[i]
}
return &ReportableStackTrace{Frames: frames}
}
// parsePrintedStackEntry extracts the stack entry information
// in lines at position i. It returns the new value of i if more than
// one line was read.
func parsePrintedStackEntry(
lines []string, i int,
) (newI int, file string, line int, fnName string) {
// The function name is on the first line.
fnName = lines[i]
// The file:line pair may be on the line after that.
if i < len(lines)-1 && strings.HasPrefix(lines[i+1], "\t") {
fileLine := strings.TrimSpace(lines[i+1])
// Separate file path and line number.
lineSep := strings.LastIndexByte(fileLine, ':')
if lineSep == -1 {
file = fileLine
} else {
file = fileLine[:lineSep]
lineStr := fileLine[lineSep+1:]
line, _ = strconv.Atoi(lineStr)
}
i++
}
return i, file, line, fnName
}
var pkgFundamental errbase.TypeKey
var pkgWithStackName errbase.TypeKey
var ourWithStackName errbase.TypeKey
func init() {
err := errors.New("")
pkgFundamental = errbase.GetTypeKey(pkgErr.New(""))
pkgWithStackName = errbase.GetTypeKey(pkgErr.WithStack(err))
ourWithStackName = errbase.GetTypeKey(WithStack(err))
}

View File

@@ -0,0 +1,59 @@
// Copyright 2019 The Cockroach 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 withstack
import (
"fmt"
"runtime"
"github.com/cockroachdb/errors/errbase"
)
// stack represents a stack of program counters.
// This mirrors the (non-exported) type of the same name in github.com/pkg/errors.
type stack []uintptr
// Format mirrors the code in github.com/pkg/errors.
func (s *stack) Format(st fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case st.Flag('+'):
for _, pc := range *s {
f := errbase.StackFrame(pc)
fmt.Fprintf(st, "\n%+v", f)
}
}
}
}
// StackTrace mirrors the code in github.com/pkg/errors.
func (s *stack) StackTrace() errbase.StackTrace {
f := make([]errbase.StackFrame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = errbase.StackFrame((*s)[i])
}
return f
}
// callers mirrors the code in github.com/pkg/errors,
// but makes the depth customizable.
func callers(depth int) *stack {
const numFrames = 32
var pcs [numFrames]uintptr
n := runtime.Callers(2+depth, pcs[:])
var st stack = pcs[0:n]
return &st
}

View File

@@ -0,0 +1,84 @@
// Copyright 2019 The Cockroach 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 withstack
import (
"fmt"
"github.com/cockroachdb/errors/errbase"
)
// This file mirrors the WithStack functionality from
// github.com/pkg/errors. We would prefer to reuse the withStack
// struct from that package directly (the library recognizes it well)
// unfortunately github.com/pkg/errors does not enable client code to
// customize the depth at which the stack trace is captured.
// WithStack annotates err with a stack trace at the point WithStack was called.
//
// Detail is shown:
// - via `errors.GetSafeDetails()`
// - when formatting with `%+v`.
// - in Sentry reports.
// - when innermost stack capture, with `errors.GetOneLineSource()`.
func WithStack(err error) error {
// Skip the frame of WithStack itself, this mirrors the behavior
// of WithStack() in github.com/pkg/errors.
return WithStackDepth(err, 1)
}
// WithStackDepth annotates err with a stack trace starting from the
// given call depth. The value zero identifies the caller
// of WithStackDepth itself.
// See the documentation of WithStack() for more details.
func WithStackDepth(err error, depth int) error {
if err == nil {
return nil
}
return &withStack{cause: err, stack: callers(depth + 1)}
}
type withStack struct {
cause error
*stack
}
var _ error = (*withStack)(nil)
var _ fmt.Formatter = (*withStack)(nil)
var _ errbase.SafeFormatter = (*withStack)(nil)
var _ errbase.SafeDetailer = (*withStack)(nil)
func (w *withStack) Error() string { return w.cause.Error() }
func (w *withStack) Cause() error { return w.cause }
func (w *withStack) Unwrap() error { return w.cause }
// Format implements the fmt.Formatter interface.
func (w *withStack) Format(s fmt.State, verb rune) { errbase.FormatError(w, s, verb) }
// SafeFormatError implements the errbase.SafeFormatter interface.
func (w *withStack) SafeFormatError(p errbase.Printer) error {
if p.Detail() {
p.Printf("attached stack trace")
}
// We do not print the stack trace ourselves - errbase.FormatError()
// does this for us.
return w.cause
}
// SafeDetails implements the errbase.SafeDetailer interface.
func (w *withStack) SafeDetails() []string {
return []string{fmt.Sprintf("%+v", w.StackTrace())}
}