summaryrefslogtreecommitdiff
path: root/libgo/go/runtime/symtab.go
blob: 867345740702eaf7cf31783d21d89cb5e4d0d94b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import (
	_ "unsafe" // for go:linkname
)

// Frames may be used to get function/file/line information for a
// slice of PC values returned by Callers.
type Frames struct {
	// callers is a slice of PCs that have not yet been expanded to frames.
	callers []uintptr

	// The last PC we saw.
	last uintptr

	// The number of times we've seen last.
	lastCount int
}

// Frame is the information returned by Frames for each call frame.
type Frame struct {
	// PC is the program counter for the location in this frame.
	// For a frame that calls another frame, this will be the
	// program counter of a call instruction. Because of inlining,
	// multiple frames may have the same PC value, but different
	// symbolic information.
	PC uintptr

	// Func is the Func value of this call frame. This may be nil
	// for non-Go code or fully inlined functions.
	Func *Func

	// Function is the package path-qualified function name of
	// this call frame. If non-empty, this string uniquely
	// identifies a single function in the program.
	// This may be the empty string if not known.
	// If Func is not nil then Function == Func.Name().
	Function string

	// File and Line are the file name and line number of the
	// location in this frame. For non-leaf frames, this will be
	// the location of a call. These may be the empty string and
	// zero, respectively, if not known.
	File string
	Line int

	// Entry point program counter for the function; may be zero
	// if not known. If Func is not nil then Entry ==
	// Func.Entry().
	Entry uintptr
}

// CallersFrames takes a slice of PC values returned by Callers and
// prepares to return function/file/line information.
// Do not change the slice until you are done with the Frames.
func CallersFrames(callers []uintptr) *Frames {
	return &Frames{callers: callers}
}

// Next returns frame information for the next caller.
// If more is false, there are no more callers (the Frame value is valid).
func (ci *Frames) Next() (frame Frame, more bool) {
	if len(ci.callers) == 0 {
		return Frame{}, false
	}

	pc := ci.callers[0]
	ci.callers = ci.callers[1:]

	i := 0
	if pc == ci.last {
		ci.lastCount++
		i = ci.lastCount
	} else {
		ci.last = pc
		ci.lastCount = 0
	}
	more = len(ci.callers) > 0

	// Subtract 1 from PC to undo the 1 we added in callback in
	// go-callers.c.
	function, file, line, _ := funcfileline(pc-1, int32(i), more)
	if function == "" && file == "" {
		return Frame{}, more
	}

	// Demangle function name if needed.
	function = demangleSymbol(function)

	// Create entry.
	entry := funcentry(pc - 1)
	f := &Func{name: function, entry: entry}

	xpc := pc
	if xpc > entry {
		xpc--
	}

	frame = Frame{
		PC:       xpc,
		Func:     f,
		Function: function,
		File:     file,
		Line:     line,
		Entry:    entry,
	}

	return frame, more
}

//go:noescape
// pcInlineCallers is written in C.
func pcInlineCallers(pc uintptr, locbuf *location, max int32) int32

// runtime_expandFinalInlineFrame expands the final pc in stk to include all
// "callers" if pc is inline.
//
//go:linkname runtime_expandFinalInlineFrame runtime..z2fpprof.runtime_expandFinalInlineFrame
func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr {
	if len(stk) == 0 {
		return stk
	}
	pc := stk[len(stk)-1]
	tracepc := pc - 1

	var locbuf [_TracebackMaxFrames]location
	n := pcInlineCallers(tracepc, &locbuf[0], int32(len(locbuf)))

	// Returning the same PC several times causes Frame.Next to do
	// the right thing.
	for i := int32(1); i < n; i++ {
		stk = append(stk, pc)
	}

	return stk
}

// NOTE: Func does not expose the actual unexported fields, because we return *Func
// values to users, and we want to keep them from being able to overwrite the data
// with (say) *f = Func{}.
// All code operating on a *Func must call raw() to get the *_func
// or funcInfo() to get the funcInfo instead.

// A Func represents a Go function in the running binary.
type Func struct {
	name  string
	entry uintptr
}

// FuncForPC returns a *Func describing the function that contains the
// given program counter address, or else nil.
//
// If pc represents multiple functions because of inlining, it returns
// the a *Func describing the innermost function, but with an entry
// of the outermost function.
func FuncForPC(pc uintptr) *Func {
	name, _, _, _ := funcfileline(pc, -1, false)
	if name == "" {
		return nil
	}
	entry := funcentry(pc)
	return &Func{name: name, entry: entry}
}

// Name returns the name of the function.
func (f *Func) Name() string {
	if f == nil {
		return ""
	}
	return f.name
}

// Entry returns the entry address of the function.
func (f *Func) Entry() uintptr {
	if f == nil {
		return 0
	}
	return f.entry
}

// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
// The result will not be accurate if pc is not a program
// counter within f.
func (f *Func) FileLine(pc uintptr) (file string, line int) {
	_, file, line, _ = funcfileline(pc, -1, false)
	return file, line
}

func hexval(b byte) uint {
	if b >= '0' && b <= '9' {
		return uint(b - '0')
	}
	if b >= 'a' && b <= 'f' {
		return uint(b-'a') + 10
	}
	return 0
}

func hexDigitsToRune(digits []byte, ndig int) rune {
	result := uint(0)
	for i := 0; i < ndig; i++ {
		result <<= uint(4)
		result |= hexval(digits[i])
	}
	return rune(result)
}

// Perform an in-place decoding on the input byte slice. This looks
// for "..z<hex 2 >", "..u<hex x 4>" and "..U<hex x 8>" and overwrites
// with the encoded bytes corresponding to the unicode in question.
// Return value is the number of bytes taken by the result.

func decodeIdentifier(bsl []byte) int {
	j := 0
	for i := 0; i < len(bsl); i++ {
		b := bsl[i]

		if i+1 < len(bsl) && bsl[i] == '.' && bsl[i+1] == '.' {
			if i+4 < len(bsl) && bsl[i+2] == 'z' {
				digits := bsl[i+3:]
				r := hexDigitsToRune(digits, 2)
				nc := encoderune(bsl[j:], r)
				j += nc
				i += 4
				continue
			} else if i+6 < len(bsl) && bsl[i+2] == 'u' {
				digits := bsl[i+3:]
				r := hexDigitsToRune(digits, 4)
				nc := encoderune(bsl[j:], r)
				j += nc
				i += 6
				continue
			} else if i+10 < len(bsl) && bsl[i+2] == 'U' {
				digits := bsl[i+3:]
				r := hexDigitsToRune(digits, 8)
				nc := encoderune(bsl[j:], r)
				j += nc
				i += 10
				continue
			}
		}
		bsl[j] = b
		j += 1
	}
	return j
}

// Demangle a function symbol. Applies the reverse of go_encode_id()
// as used in the compiler.

func demangleSymbol(s string) string {
	bsl := []byte(s)
	nchars := decodeIdentifier(bsl)
	bsl = bsl[:nchars]
	return string(bsl)
}

// implemented in go-caller.c
func funcfileline(uintptr, int32, bool) (string, string, int, int)
func funcentry(uintptr) uintptr