src/pkg/runtime/debug/stack.go - The Go Programming Language

Golang

Source file src/pkg/runtime/debug/stack.go

     1	// Copyright 2011 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// Package debug contains facilities for programs to debug themselves while
     6	// they are running.
     7	package debug
     8	
     9	import (
    10		"bytes"
    11		"fmt"
    12		"io/ioutil"
    13		"os"
    14		"runtime"
    15	)
    16	
    17	var (
    18		dunno     = []byte("???")
    19		centerDot = []byte("·")
    20		dot       = []byte(".")
    21	)
    22	
    23	// PrintStack prints to standard error the stack trace returned by Stack.
    24	func PrintStack() {
    25		os.Stderr.Write(stack())
    26	}
    27	
    28	// Stack returns a formatted stack trace of the goroutine that calls it.
    29	// For each routine, it includes the source line information and PC value,
    30	// then attempts to discover, for Go functions, the calling function or
    31	// method and the text of the line containing the invocation.
    32	func Stack() []byte {
    33		return stack()
    34	}
    35	
    36	// stack implements Stack, skipping 2 frames
    37	func stack() []byte {
    38		buf := new(bytes.Buffer) // the returned data
    39		// As we loop, we open files and read them. These variables record the currently
    40		// loaded file.
    41		var lines [][]byte
    42		var lastFile string
    43		for i := 2; ; i++ { // Caller we care about is the user, 2 frames up
    44			pc, file, line, ok := runtime.Caller(i)
    45			if !ok {
    46				break
    47			}
    48			// Print this much at least.  If we can't find the source, it won't show.
    49			fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
    50			if file != lastFile {
    51				data, err := ioutil.ReadFile(file)
    52				if err != nil {
    53					continue
    54				}
    55				lines = bytes.Split(data, []byte{'\n'})
    56				lastFile = file
    57			}
    58			line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
    59			fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
    60		}
    61		return buf.Bytes()
    62	}
    63	
    64	// source returns a space-trimmed slice of the n'th line.
    65	func source(lines [][]byte, n int) []byte {
    66		if n < 0 || n >= len(lines) {
    67			return dunno
    68		}
    69		return bytes.Trim(lines[n], " \t")
    70	}
    71	
    72	// function returns, if possible, the name of the function containing the PC.
    73	func function(pc uintptr) []byte {
    74		fn := runtime.FuncForPC(pc)
    75		if fn == nil {
    76			return dunno
    77		}
    78		name := []byte(fn.Name())
    79		// The name includes the path name to the package, which is unnecessary
    80		// since the file name is already included.  Plus, it has center dots.
    81		// That is, we see
    82		//	runtime/debug.*T·ptrmethod
    83		// and want
    84		//	*T.ptrmethod
    85		if period := bytes.Index(name, dot); period >= 0 {
    86			name = name[period+1:]
    87		}
    88		name = bytes.Replace(name, centerDot, dot, -1)
    89		return name
    90	}