...

Source file src/github.com/golang/glog/internal/stackdump/stackdump.go

Documentation: github.com/golang/glog/internal/stackdump

     1  // Copyright 2023 Google Inc. All Rights Reserved.
     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 stackdump provides wrappers for runtime.Stack and runtime.Callers
    16  // with uniform support for skipping caller frames.
    17  //
    18  // ⚠ Unlike the functions in the runtime package, these may allocate a
    19  // non-trivial quantity of memory: use them with care. ⚠
    20  package stackdump
    21  
    22  import (
    23  	"bytes"
    24  	"runtime"
    25  )
    26  
    27  // runtimeStackSelfFrames is 1 if runtime.Stack includes the call to
    28  // runtime.Stack itself or 0 if it does not.
    29  //
    30  // As of 2016-04-27, the gccgo compiler includes runtime.Stack but the gc
    31  // compiler does not.
    32  var runtimeStackSelfFrames = func() int {
    33  	for n := 1 << 10; n < 1<<20; n *= 2 {
    34  		buf := make([]byte, n)
    35  		n := runtime.Stack(buf, false)
    36  		if bytes.Contains(buf[:n], []byte("runtime.Stack")) {
    37  			return 1
    38  		} else if n < len(buf) || bytes.Count(buf, []byte("\n")) >= 3 {
    39  			return 0
    40  		}
    41  	}
    42  	return 0
    43  }()
    44  
    45  // Stack is a stack dump for a single goroutine.
    46  type Stack struct {
    47  	// Text is a representation of the stack dump in a human-readable format.
    48  	Text []byte
    49  
    50  	// PC is a representation of the stack dump using raw program counter values.
    51  	PC []uintptr
    52  }
    53  
    54  func (s Stack) String() string { return string(s.Text) }
    55  
    56  // Caller returns the Stack dump for the calling goroutine, starting skipDepth
    57  // frames before the caller of Caller.  (Caller(0) provides a dump starting at
    58  // the caller of this function.)
    59  func Caller(skipDepth int) Stack {
    60  	return Stack{
    61  		Text: CallerText(skipDepth + 1),
    62  		PC:   CallerPC(skipDepth + 1),
    63  	}
    64  }
    65  
    66  // CallerText returns a textual dump of the stack starting skipDepth frames before
    67  // the caller.  (CallerText(0) provides a dump starting at the caller of this
    68  // function.)
    69  func CallerText(skipDepth int) []byte {
    70  	for n := 1 << 10; ; n *= 2 {
    71  		buf := make([]byte, n)
    72  		n := runtime.Stack(buf, false)
    73  		if n < len(buf) {
    74  			return pruneFrames(skipDepth+1+runtimeStackSelfFrames, buf[:n])
    75  		}
    76  	}
    77  }
    78  
    79  // CallerPC returns a dump of the program counters of the stack starting
    80  // skipDepth frames before the caller.  (CallerPC(0) provides a dump starting at
    81  // the caller of this function.)
    82  func CallerPC(skipDepth int) []uintptr {
    83  	for n := 1 << 8; ; n *= 2 {
    84  		buf := make([]uintptr, n)
    85  		n := runtime.Callers(skipDepth+2, buf)
    86  		if n < len(buf) {
    87  			return buf[:n]
    88  		}
    89  	}
    90  }
    91  
    92  // pruneFrames removes the topmost skipDepth frames of the first goroutine in a
    93  // textual stack dump.  It overwrites the passed-in slice.
    94  //
    95  // If there are fewer than skipDepth frames in the first goroutine's stack,
    96  // pruneFrames prunes it to an empty stack and leaves the remaining contents
    97  // intact.
    98  func pruneFrames(skipDepth int, stack []byte) []byte {
    99  	headerLen := 0
   100  	for i, c := range stack {
   101  		if c == '\n' {
   102  			headerLen = i + 1
   103  			break
   104  		}
   105  	}
   106  	if headerLen == 0 {
   107  		return stack // No header line - not a well-formed stack trace.
   108  	}
   109  
   110  	skipLen := headerLen
   111  	skipNewlines := skipDepth * 2
   112  	for ; skipLen < len(stack) && skipNewlines > 0; skipLen++ {
   113  		c := stack[skipLen]
   114  		if c != '\n' {
   115  			continue
   116  		}
   117  		skipNewlines--
   118  		skipLen++
   119  		if skipNewlines == 0 || skipLen == len(stack) || stack[skipLen] == '\n' {
   120  			break
   121  		}
   122  	}
   123  
   124  	pruned := stack[skipLen-headerLen:]
   125  	copy(pruned, stack[:headerLen])
   126  	return pruned
   127  }
   128  

View as plain text