mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 08:12:26 -06:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			114 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package errors
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"runtime"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// A StackFrame contains all necessary information about to generate a line
 | 
						|
// in a callstack.
 | 
						|
type StackFrame struct {
 | 
						|
	// The path to the file containing this ProgramCounter
 | 
						|
	File string
 | 
						|
	// The LineNumber in that file
 | 
						|
	LineNumber int
 | 
						|
	// The Name of the function that contains this ProgramCounter
 | 
						|
	Name string
 | 
						|
	// The Package that contains this function
 | 
						|
	Package string
 | 
						|
	// The underlying ProgramCounter
 | 
						|
	ProgramCounter uintptr
 | 
						|
}
 | 
						|
 | 
						|
// NewStackFrame popoulates a stack frame object from the program counter.
 | 
						|
func NewStackFrame(pc uintptr) (frame StackFrame) {
 | 
						|
 | 
						|
	frame = StackFrame{ProgramCounter: pc}
 | 
						|
	if frame.Func() == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	frame.Package, frame.Name = packageAndName(frame.Func())
 | 
						|
 | 
						|
	// pc -1 because the program counters we use are usually return addresses,
 | 
						|
	// and we want to show the line that corresponds to the function call
 | 
						|
	frame.File, frame.LineNumber = frame.Func().FileLine(pc - 1)
 | 
						|
	return
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// Func returns the function that contained this frame.
 | 
						|
func (frame *StackFrame) Func() *runtime.Func {
 | 
						|
	if frame.ProgramCounter == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return runtime.FuncForPC(frame.ProgramCounter)
 | 
						|
}
 | 
						|
 | 
						|
// String returns the stackframe formatted in the same way as go does
 | 
						|
// in runtime/debug.Stack()
 | 
						|
func (frame *StackFrame) String() string {
 | 
						|
	str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter)
 | 
						|
 | 
						|
	source, err := frame.SourceLine()
 | 
						|
	if err != nil {
 | 
						|
		return str
 | 
						|
	}
 | 
						|
 | 
						|
	return str + fmt.Sprintf("\t%s: %s\n", frame.Name, source)
 | 
						|
}
 | 
						|
 | 
						|
// SourceLine gets the line of code (from File and Line) of the original source if possible.
 | 
						|
func (frame *StackFrame) SourceLine() (string, error) {
 | 
						|
	if frame.LineNumber <= 0 {
 | 
						|
		return "???", nil
 | 
						|
	}
 | 
						|
 | 
						|
	file, err := os.Open(frame.File)
 | 
						|
	if err != nil {
 | 
						|
		return "", New(err)
 | 
						|
	}
 | 
						|
	defer file.Close()
 | 
						|
 | 
						|
	scanner := bufio.NewScanner(file)
 | 
						|
	currentLine := 1
 | 
						|
	for scanner.Scan() {
 | 
						|
		if currentLine == frame.LineNumber {
 | 
						|
			return string(bytes.Trim(scanner.Bytes(), " \t")), nil
 | 
						|
		}
 | 
						|
		currentLine++
 | 
						|
	}
 | 
						|
	if err := scanner.Err(); err != nil {
 | 
						|
		return "", New(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return "???", nil
 | 
						|
}
 | 
						|
 | 
						|
func packageAndName(fn *runtime.Func) (string, string) {
 | 
						|
	name := fn.Name()
 | 
						|
	pkg := ""
 | 
						|
 | 
						|
	// The name includes the path name to the package, which is unnecessary
 | 
						|
	// since the file name is already included.  Plus, it has center dots.
 | 
						|
	// That is, we see
 | 
						|
	//  runtime/debug.*T·ptrmethod
 | 
						|
	// and want
 | 
						|
	//  *T.ptrmethod
 | 
						|
	// Since the package path might contains dots (e.g. code.google.com/...),
 | 
						|
	// we first remove the path prefix if there is one.
 | 
						|
	if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
 | 
						|
		pkg += name[:lastslash] + "/"
 | 
						|
		name = name[lastslash+1:]
 | 
						|
	}
 | 
						|
	if period := strings.Index(name, "."); period >= 0 {
 | 
						|
		pkg += name[:period]
 | 
						|
		name = name[period+1:]
 | 
						|
	}
 | 
						|
 | 
						|
	name = strings.Replace(name, "·", ".", -1)
 | 
						|
	return pkg, name
 | 
						|
}
 |