Source file src/runtime/symtabinl.go

     1  // Copyright 2023 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 runtime
     6  
     7  import (
     8  	"internal/abi"
     9  	_ "unsafe" // for linkname
    10  )
    11  
    12  // inlinedCall is the encoding of entries in the FUNCDATA_InlTree table.
    13  type inlinedCall struct {
    14  	funcID    abi.FuncID // type of the called function
    15  	_         [3]byte
    16  	nameOff   int32 // offset into pclntab for name of called function
    17  	parentPc  int32 // position of an instruction whose source position is the call site (offset from entry)
    18  	startLine int32 // line number of start of function (func keyword/TEXT directive)
    19  }
    20  
    21  // An inlineUnwinder iterates over the stack of inlined calls at a PC by
    22  // decoding the inline table. The last step of iteration is always the frame of
    23  // the physical function, so there's always at least one frame.
    24  //
    25  // This is typically used as:
    26  //
    27  //	for u, uf := newInlineUnwinder(...); uf.valid(); uf = u.next(uf) { ... }
    28  //
    29  // Implementation note: This is used in contexts that disallow write barriers.
    30  // Hence, the constructor returns this by value and pointer receiver methods
    31  // must not mutate pointer fields. Also, we keep the mutable state in a separate
    32  // struct mostly to keep both structs SSA-able, which generates much better
    33  // code.
    34  type inlineUnwinder struct {
    35  	f       funcInfo
    36  	inlTree *[1 << 20]inlinedCall
    37  }
    38  
    39  // An inlineFrame is a position in an inlineUnwinder.
    40  type inlineFrame struct {
    41  	// pc is the PC giving the file/line metadata of the current frame. This is
    42  	// always a "call PC" (not a "return PC"). This is 0 when the iterator is
    43  	// exhausted.
    44  	pc uintptr
    45  
    46  	// index is the index of the current record in inlTree, or -1 if we are in
    47  	// the outermost function.
    48  	index int32
    49  }
    50  
    51  // newInlineUnwinder creates an inlineUnwinder initially set to the inner-most
    52  // inlined frame at PC. PC should be a "call PC" (not a "return PC").
    53  //
    54  // This unwinder uses non-strict handling of PC because it's assumed this is
    55  // only ever used for symbolic debugging. If things go really wrong, it'll just
    56  // fall back to the outermost frame.
    57  //
    58  // newInlineUnwinder should be an internal detail,
    59  // but widely used packages access it using linkname.
    60  // Notable members of the hall of shame include:
    61  //   - github.com/phuslu/log
    62  //
    63  // Do not remove or change the type signature.
    64  // See go.dev/issue/67401.
    65  //
    66  //go:linkname newInlineUnwinder
    67  func newInlineUnwinder(f funcInfo, pc uintptr) (inlineUnwinder, inlineFrame) {
    68  	inldata := funcdata(f, abi.FUNCDATA_InlTree)
    69  	if inldata == nil {
    70  		return inlineUnwinder{f: f}, inlineFrame{pc: pc, index: -1}
    71  	}
    72  	inlTree := (*[1 << 20]inlinedCall)(inldata)
    73  	u := inlineUnwinder{f: f, inlTree: inlTree}
    74  	return u, u.resolveInternal(pc)
    75  }
    76  
    77  func (u *inlineUnwinder) resolveInternal(pc uintptr) inlineFrame {
    78  	return inlineFrame{
    79  		pc: pc,
    80  		// Conveniently, this returns -1 if there's an error, which is the same
    81  		// value we use for the outermost frame.
    82  		index: pcdatavalue1(u.f, abi.PCDATA_InlTreeIndex, pc, false),
    83  	}
    84  }
    85  
    86  func (uf inlineFrame) valid() bool {
    87  	return uf.pc != 0
    88  }
    89  
    90  // next returns the frame representing uf's logical caller.
    91  func (u *inlineUnwinder) next(uf inlineFrame) inlineFrame {
    92  	if uf.index < 0 {
    93  		uf.pc = 0
    94  		return uf
    95  	}
    96  	parentPc := u.inlTree[uf.index].parentPc
    97  	return u.resolveInternal(u.f.entry() + uintptr(parentPc))
    98  }
    99  
   100  // isInlined returns whether uf is an inlined frame.
   101  func (u *inlineUnwinder) isInlined(uf inlineFrame) bool {
   102  	return uf.index >= 0
   103  }
   104  
   105  // srcFunc returns the srcFunc representing the given frame.
   106  //
   107  // srcFunc should be an internal detail,
   108  // but widely used packages access it using linkname.
   109  // Notable members of the hall of shame include:
   110  //   - github.com/phuslu/log
   111  //
   112  // Do not remove or change the type signature.
   113  // See go.dev/issue/67401.
   114  //
   115  // The go:linkname is below.
   116  func (u *inlineUnwinder) srcFunc(uf inlineFrame) srcFunc {
   117  	if uf.index < 0 {
   118  		return u.f.srcFunc()
   119  	}
   120  	t := &u.inlTree[uf.index]
   121  	return srcFunc{
   122  		u.f.datap,
   123  		t.nameOff,
   124  		t.startLine,
   125  		t.funcID,
   126  	}
   127  }
   128  
   129  //go:linkname badSrcFunc runtime.(*inlineUnwinder).srcFunc
   130  func badSrcFunc(*inlineUnwinder, inlineFrame) srcFunc
   131  
   132  // fileLine returns the file name and line number of the call within the given
   133  // frame. As a convenience, for the innermost frame, it returns the file and
   134  // line of the PC this unwinder was started at (often this is a call to another
   135  // physical function).
   136  //
   137  // It returns "?", 0 if something goes wrong.
   138  func (u *inlineUnwinder) fileLine(uf inlineFrame) (file string, line int) {
   139  	file, line32 := funcline1(u.f, uf.pc, false)
   140  	return file, int(line32)
   141  }
   142  

View as plain text