mirror of
https://github.com/containers/skopeo.git
synced 2025-10-22 11:44:05 +00:00
containers/storage got new dependencies, so we will need to re-vendor eventually anyway, and having this separate from other major work is cleaner. But the primary goal of this commit is to see whether it makes skopeo buildable on OS X.
187 lines
4.7 KiB
Go
187 lines
4.7 KiB
Go
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"path"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
// Frame represents a program counter inside a stack frame.
|
|
type Frame uintptr
|
|
|
|
// pc returns the program counter for this frame;
|
|
// multiple frames may have the same PC value.
|
|
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
|
|
|
// file returns the full path to the file that contains the
|
|
// function for this Frame's pc.
|
|
func (f Frame) file() string {
|
|
fn := runtime.FuncForPC(f.pc())
|
|
if fn == nil {
|
|
return "unknown"
|
|
}
|
|
file, _ := fn.FileLine(f.pc())
|
|
return file
|
|
}
|
|
|
|
// line returns the line number of source code of the
|
|
// function for this Frame's pc.
|
|
func (f Frame) line() int {
|
|
fn := runtime.FuncForPC(f.pc())
|
|
if fn == nil {
|
|
return 0
|
|
}
|
|
_, line := fn.FileLine(f.pc())
|
|
return line
|
|
}
|
|
|
|
// Format formats the frame according to the fmt.Formatter interface.
|
|
//
|
|
// %s source file
|
|
// %d source line
|
|
// %n function name
|
|
// %v equivalent to %s:%d
|
|
//
|
|
// Format accepts flags that alter the printing of some verbs, as follows:
|
|
//
|
|
// %+s path of source file relative to the compile time GOPATH
|
|
// %+v equivalent to %+s:%d
|
|
func (f Frame) Format(s fmt.State, verb rune) {
|
|
switch verb {
|
|
case 's':
|
|
switch {
|
|
case s.Flag('+'):
|
|
pc := f.pc()
|
|
fn := runtime.FuncForPC(pc)
|
|
if fn == nil {
|
|
io.WriteString(s, "unknown")
|
|
} else {
|
|
file, _ := fn.FileLine(pc)
|
|
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
|
}
|
|
default:
|
|
io.WriteString(s, path.Base(f.file()))
|
|
}
|
|
case 'd':
|
|
fmt.Fprintf(s, "%d", f.line())
|
|
case 'n':
|
|
name := runtime.FuncForPC(f.pc()).Name()
|
|
io.WriteString(s, funcname(name))
|
|
case 'v':
|
|
f.Format(s, 's')
|
|
io.WriteString(s, ":")
|
|
f.Format(s, 'd')
|
|
}
|
|
}
|
|
|
|
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
|
type StackTrace []Frame
|
|
|
|
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
|
//
|
|
// %s lists source files for each Frame in the stack
|
|
// %v lists the source file and line number for each Frame in the stack
|
|
//
|
|
// Format accepts flags that alter the printing of some verbs, as follows:
|
|
//
|
|
// %+v Prints filename, function, and line number for each Frame in the stack.
|
|
func (st StackTrace) Format(s fmt.State, verb rune) {
|
|
switch verb {
|
|
case 'v':
|
|
switch {
|
|
case s.Flag('+'):
|
|
for _, f := range st {
|
|
fmt.Fprintf(s, "\n%+v", f)
|
|
}
|
|
case s.Flag('#'):
|
|
fmt.Fprintf(s, "%#v", []Frame(st))
|
|
default:
|
|
fmt.Fprintf(s, "%v", []Frame(st))
|
|
}
|
|
case 's':
|
|
fmt.Fprintf(s, "%s", []Frame(st))
|
|
}
|
|
}
|
|
|
|
// stack represents a stack of program counters.
|
|
type stack []uintptr
|
|
|
|
func (s *stack) Format(st fmt.State, verb rune) {
|
|
switch verb {
|
|
case 'v':
|
|
switch {
|
|
case st.Flag('+'):
|
|
for _, pc := range *s {
|
|
f := Frame(pc)
|
|
fmt.Fprintf(st, "\n%+v", f)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *stack) StackTrace() StackTrace {
|
|
f := make([]Frame, len(*s))
|
|
for i := 0; i < len(f); i++ {
|
|
f[i] = Frame((*s)[i])
|
|
}
|
|
return f
|
|
}
|
|
|
|
func callers() *stack {
|
|
const depth = 32
|
|
var pcs [depth]uintptr
|
|
n := runtime.Callers(3, pcs[:])
|
|
var st stack = pcs[0:n]
|
|
return &st
|
|
}
|
|
|
|
// funcname removes the path prefix component of a function's name reported by func.Name().
|
|
func funcname(name string) string {
|
|
i := strings.LastIndex(name, "/")
|
|
name = name[i+1:]
|
|
i = strings.Index(name, ".")
|
|
return name[i+1:]
|
|
}
|
|
|
|
func trimGOPATH(name, file string) string {
|
|
// Here we want to get the source file path relative to the compile time
|
|
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
|
// GOPATH at runtime, but we can infer the number of path segments in the
|
|
// GOPATH. We note that fn.Name() returns the function name qualified by
|
|
// the import path, which does not include the GOPATH. Thus we can trim
|
|
// segments from the beginning of the file path until the number of path
|
|
// separators remaining is one more than the number of path separators in
|
|
// the function name. For example, given:
|
|
//
|
|
// GOPATH /home/user
|
|
// file /home/user/src/pkg/sub/file.go
|
|
// fn.Name() pkg/sub.Type.Method
|
|
//
|
|
// We want to produce:
|
|
//
|
|
// pkg/sub/file.go
|
|
//
|
|
// From this we can easily see that fn.Name() has one less path separator
|
|
// than our desired output. We count separators from the end of the file
|
|
// path until it finds two more than in the function name and then move
|
|
// one character forward to preserve the initial path segment without a
|
|
// leading separator.
|
|
const sep = "/"
|
|
goal := strings.Count(name, sep) + 2
|
|
i := len(file)
|
|
for n := 0; n < goal; n++ {
|
|
i = strings.LastIndex(file[:i], sep)
|
|
if i == -1 {
|
|
// not enough separators found, set i so that the slice expression
|
|
// below leaves file unmodified
|
|
i = -len(sep)
|
|
break
|
|
}
|
|
}
|
|
// get back to 0 or trim the leading separator
|
|
file = file[i+len(sep):]
|
|
return file
|
|
}
|