compiler: rationalize external symbol names
[official-gcc.git] / libgo / go / runtime / pprof / proto.go
blob793be44a4176f42b7180ba29bb116a6a1f00e25f
1 // Copyright 2016 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.
5 package pprof
7 import (
8 "bytes"
9 "compress/gzip"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "runtime"
14 "sort"
15 "strconv"
16 "time"
17 "unsafe"
20 // lostProfileEvent is the function to which lost profiling
21 // events are attributed.
22 // (The name shows up in the pprof graphs.)
23 func lostProfileEvent() { lostProfileEvent() }
25 // funcPC returns the PC for the func value f.
26 func funcPC(f interface{}) uintptr {
27 type iface struct {
28 tab unsafe.Pointer
29 data unsafe.Pointer
31 i := (*iface)(unsafe.Pointer(&f))
32 return **(**uintptr)(i.data)
35 // A profileBuilder writes a profile incrementally from a
36 // stream of profile samples delivered by the runtime.
37 type profileBuilder struct {
38 start time.Time
39 end time.Time
40 havePeriod bool
41 period int64
42 m profMap
44 // encoding state
45 w io.Writer
46 zw *gzip.Writer
47 pb protobuf
48 strings []string
49 stringMap map[string]int
50 locs map[uintptr]int
51 funcs map[string]int // Package path-qualified function name to Function.ID
52 mem []memMap
55 type memMap struct {
56 start uintptr
57 end uintptr
60 const (
61 // message Profile
62 tagProfile_SampleType = 1 // repeated ValueType
63 tagProfile_Sample = 2 // repeated Sample
64 tagProfile_Mapping = 3 // repeated Mapping
65 tagProfile_Location = 4 // repeated Location
66 tagProfile_Function = 5 // repeated Function
67 tagProfile_StringTable = 6 // repeated string
68 tagProfile_DropFrames = 7 // int64 (string table index)
69 tagProfile_KeepFrames = 8 // int64 (string table index)
70 tagProfile_TimeNanos = 9 // int64
71 tagProfile_DurationNanos = 10 // int64
72 tagProfile_PeriodType = 11 // ValueType (really optional string???)
73 tagProfile_Period = 12 // int64
75 // message ValueType
76 tagValueType_Type = 1 // int64 (string table index)
77 tagValueType_Unit = 2 // int64 (string table index)
79 // message Sample
80 tagSample_Location = 1 // repeated uint64
81 tagSample_Value = 2 // repeated int64
82 tagSample_Label = 3 // repeated Label
84 // message Label
85 tagLabel_Key = 1 // int64 (string table index)
86 tagLabel_Str = 2 // int64 (string table index)
87 tagLabel_Num = 3 // int64
89 // message Mapping
90 tagMapping_ID = 1 // uint64
91 tagMapping_Start = 2 // uint64
92 tagMapping_Limit = 3 // uint64
93 tagMapping_Offset = 4 // uint64
94 tagMapping_Filename = 5 // int64 (string table index)
95 tagMapping_BuildID = 6 // int64 (string table index)
96 tagMapping_HasFunctions = 7 // bool
97 tagMapping_HasFilenames = 8 // bool
98 tagMapping_HasLineNumbers = 9 // bool
99 tagMapping_HasInlineFrames = 10 // bool
101 // message Location
102 tagLocation_ID = 1 // uint64
103 tagLocation_MappingID = 2 // uint64
104 tagLocation_Address = 3 // uint64
105 tagLocation_Line = 4 // repeated Line
107 // message Line
108 tagLine_FunctionID = 1 // uint64
109 tagLine_Line = 2 // int64
111 // message Function
112 tagFunction_ID = 1 // uint64
113 tagFunction_Name = 2 // int64 (string table index)
114 tagFunction_SystemName = 3 // int64 (string table index)
115 tagFunction_Filename = 4 // int64 (string table index)
116 tagFunction_StartLine = 5 // int64
119 // stringIndex adds s to the string table if not already present
120 // and returns the index of s in the string table.
121 func (b *profileBuilder) stringIndex(s string) int64 {
122 id, ok := b.stringMap[s]
123 if !ok {
124 id = len(b.strings)
125 b.strings = append(b.strings, s)
126 b.stringMap[s] = id
128 return int64(id)
131 func (b *profileBuilder) flush() {
132 const dataFlush = 4096
133 if b.pb.nest == 0 && len(b.pb.data) > dataFlush {
134 b.zw.Write(b.pb.data)
135 b.pb.data = b.pb.data[:0]
139 // pbValueType encodes a ValueType message to b.pb.
140 func (b *profileBuilder) pbValueType(tag int, typ, unit string) {
141 start := b.pb.startMessage()
142 b.pb.int64(tagValueType_Type, b.stringIndex(typ))
143 b.pb.int64(tagValueType_Unit, b.stringIndex(unit))
144 b.pb.endMessage(tag, start)
147 // pbSample encodes a Sample message to b.pb.
148 func (b *profileBuilder) pbSample(values []int64, locs []uint64, labels func()) {
149 start := b.pb.startMessage()
150 b.pb.int64s(tagSample_Value, values)
151 b.pb.uint64s(tagSample_Location, locs)
152 if labels != nil {
153 labels()
155 b.pb.endMessage(tagProfile_Sample, start)
156 b.flush()
159 // pbLabel encodes a Label message to b.pb.
160 func (b *profileBuilder) pbLabel(tag int, key, str string, num int64) {
161 start := b.pb.startMessage()
162 b.pb.int64Opt(tagLabel_Key, b.stringIndex(key))
163 b.pb.int64Opt(tagLabel_Str, b.stringIndex(str))
164 b.pb.int64Opt(tagLabel_Num, num)
165 b.pb.endMessage(tag, start)
168 // pbLine encodes a Line message to b.pb.
169 func (b *profileBuilder) pbLine(tag int, funcID uint64, line int64) {
170 start := b.pb.startMessage()
171 b.pb.uint64Opt(tagLine_FunctionID, funcID)
172 b.pb.int64Opt(tagLine_Line, line)
173 b.pb.endMessage(tag, start)
176 // pbMapping encodes a Mapping message to b.pb.
177 func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file, buildID string) {
178 start := b.pb.startMessage()
179 b.pb.uint64Opt(tagMapping_ID, id)
180 b.pb.uint64Opt(tagMapping_Start, base)
181 b.pb.uint64Opt(tagMapping_Limit, limit)
182 b.pb.uint64Opt(tagMapping_Offset, offset)
183 b.pb.int64Opt(tagMapping_Filename, b.stringIndex(file))
184 b.pb.int64Opt(tagMapping_BuildID, b.stringIndex(buildID))
185 // TODO: Set any of HasInlineFrames, HasFunctions, HasFilenames, HasLineNumbers?
186 // It seems like they should all be true, but they've never been set.
187 b.pb.endMessage(tag, start)
190 // locForPC returns the location ID for addr.
191 // addr must be a return PC. This returns the location of the call.
192 // It may emit to b.pb, so there must be no message encoding in progress.
193 func (b *profileBuilder) locForPC(addr uintptr) uint64 {
194 id := uint64(b.locs[addr])
195 if id != 0 {
196 return id
199 // Expand this one address using CallersFrames so we can cache
200 // each expansion. In general, CallersFrames takes a whole
201 // stack, but in this case we know there will be no skips in
202 // the stack and we have return PCs anyway.
203 frames := runtime.CallersFrames([]uintptr{addr})
204 frame, more := frames.Next()
205 if frame.Function == "runtime.goexit" || frame.Function == "runtime.kickoff" {
206 // Short-circuit if we see runtime.goexit so the loop
207 // below doesn't allocate a useless empty location.
208 return 0
211 if frame.PC == 0 {
212 // If we failed to resolve the frame, at least make up
213 // a reasonable call PC. This mostly happens in tests.
214 frame.PC = addr - 1
217 // We can't write out functions while in the middle of the
218 // Location message, so record new functions we encounter and
219 // write them out after the Location.
220 type newFunc struct {
221 id uint64
222 name, file string
224 newFuncs := make([]newFunc, 0, 8)
226 id = uint64(len(b.locs)) + 1
227 b.locs[addr] = int(id)
228 start := b.pb.startMessage()
229 b.pb.uint64Opt(tagLocation_ID, id)
230 b.pb.uint64Opt(tagLocation_Address, uint64(frame.PC))
231 for frame.Function != "runtime.goexit" && frame.Function != "runtime.kickoff" {
232 // Write out each line in frame expansion.
233 funcID := uint64(b.funcs[frame.Function])
234 if funcID == 0 {
235 funcID = uint64(len(b.funcs)) + 1
236 b.funcs[frame.Function] = int(funcID)
237 newFuncs = append(newFuncs, newFunc{funcID, frame.Function, frame.File})
239 b.pbLine(tagLocation_Line, funcID, int64(frame.Line))
240 if !more {
241 break
243 frame, more = frames.Next()
245 if len(b.mem) > 0 {
246 i := sort.Search(len(b.mem), func(i int) bool {
247 return b.mem[i].end > addr
249 if i < len(b.mem) && b.mem[i].start <= addr && addr < b.mem[i].end {
250 b.pb.uint64Opt(tagLocation_MappingID, uint64(i+1))
253 b.pb.endMessage(tagProfile_Location, start)
255 // Write out functions we found during frame expansion.
256 for _, fn := range newFuncs {
257 start := b.pb.startMessage()
258 b.pb.uint64Opt(tagFunction_ID, fn.id)
259 b.pb.int64Opt(tagFunction_Name, b.stringIndex(fn.name))
260 b.pb.int64Opt(tagFunction_SystemName, b.stringIndex(fn.name))
261 b.pb.int64Opt(tagFunction_Filename, b.stringIndex(fn.file))
262 b.pb.endMessage(tagProfile_Function, start)
265 b.flush()
266 return id
269 // newProfileBuilder returns a new profileBuilder.
270 // CPU profiling data obtained from the runtime can be added
271 // by calling b.addCPUData, and then the eventual profile
272 // can be obtained by calling b.finish.
273 func newProfileBuilder(w io.Writer) *profileBuilder {
274 zw, _ := gzip.NewWriterLevel(w, gzip.BestSpeed)
275 b := &profileBuilder{
276 w: w,
277 zw: zw,
278 start: time.Now(),
279 strings: []string{""},
280 stringMap: map[string]int{"": 0},
281 locs: map[uintptr]int{},
282 funcs: map[string]int{},
284 b.readMapping()
285 return b
288 // addCPUData adds the CPU profiling data to the profile.
289 // The data must be a whole number of records,
290 // as delivered by the runtime.
291 func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error {
292 if !b.havePeriod {
293 // first record is period
294 if len(data) < 3 {
295 return fmt.Errorf("truncated profile")
297 if data[0] != 3 || data[2] == 0 {
298 return fmt.Errorf("malformed profile")
300 // data[2] is sampling rate in Hz. Convert to sampling
301 // period in nanoseconds.
302 b.period = 1e9 / int64(data[2])
303 b.havePeriod = true
304 data = data[3:]
307 // Parse CPU samples from the profile.
308 // Each sample is 3+n uint64s:
309 // data[0] = 3+n
310 // data[1] = time stamp (ignored)
311 // data[2] = count
312 // data[3:3+n] = stack
313 // If the count is 0 and the stack has length 1,
314 // that's an overflow record inserted by the runtime
315 // to indicate that stack[0] samples were lost.
316 // Otherwise the count is usually 1,
317 // but in a few special cases like lost non-Go samples
318 // there can be larger counts.
319 // Because many samples with the same stack arrive,
320 // we want to deduplicate immediately, which we do
321 // using the b.m profMap.
322 for len(data) > 0 {
323 if len(data) < 3 || data[0] > uint64(len(data)) {
324 return fmt.Errorf("truncated profile")
326 if data[0] < 3 || tags != nil && len(tags) < 1 {
327 return fmt.Errorf("malformed profile")
329 count := data[2]
330 stk := data[3:data[0]]
331 data = data[data[0]:]
332 var tag unsafe.Pointer
333 if tags != nil {
334 tag = tags[0]
335 tags = tags[1:]
338 if count == 0 && len(stk) == 1 {
339 // overflow record
340 count = uint64(stk[0])
341 stk = []uint64{
342 uint64(funcPC(lostProfileEvent)),
345 b.m.lookup(stk, tag).count += int64(count)
347 return nil
350 // build completes and returns the constructed profile.
351 func (b *profileBuilder) build() error {
352 b.end = time.Now()
354 b.pb.int64Opt(tagProfile_TimeNanos, b.start.UnixNano())
355 if b.havePeriod { // must be CPU profile
356 b.pbValueType(tagProfile_SampleType, "samples", "count")
357 b.pbValueType(tagProfile_SampleType, "cpu", "nanoseconds")
358 b.pb.int64Opt(tagProfile_DurationNanos, b.end.Sub(b.start).Nanoseconds())
359 b.pbValueType(tagProfile_PeriodType, "cpu", "nanoseconds")
360 b.pb.int64Opt(tagProfile_Period, b.period)
363 values := []int64{0, 0}
364 var locs []uint64
365 for e := b.m.all; e != nil; e = e.nextAll {
366 values[0] = e.count
367 values[1] = e.count * b.period
369 var labels func()
370 if e.tag != nil {
371 labels = func() {
372 for k, v := range *(*labelMap)(e.tag) {
373 b.pbLabel(tagSample_Label, k, v, 0)
378 locs = locs[:0]
379 for i, addr := range e.stk {
380 // Addresses from stack traces point to the
381 // next instruction after each call, except
382 // for the leaf, which points to where the
383 // signal occurred. locForPC expects return
384 // PCs, so increment the leaf address to look
385 // like a return PC.
386 if i == 0 {
387 addr++
389 l := b.locForPC(addr)
390 if l == 0 { // runtime.goexit
391 continue
393 locs = append(locs, l)
395 b.pbSample(values, locs, labels)
398 // TODO: Anything for tagProfile_DropFrames?
399 // TODO: Anything for tagProfile_KeepFrames?
401 b.pb.strings(tagProfile_StringTable, b.strings)
402 b.zw.Write(b.pb.data)
403 b.zw.Close()
404 return nil
407 // readMapping reads /proc/self/maps and writes mappings to b.pb.
408 // It saves the address ranges of the mappings in b.mem for use
409 // when emitting locations.
410 func (b *profileBuilder) readMapping() {
411 data, _ := ioutil.ReadFile("/proc/self/maps")
412 parseProcSelfMaps(data, b.addMapping)
415 func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, buildID string)) {
416 // $ cat /proc/self/maps
417 // 00400000-0040b000 r-xp 00000000 fc:01 787766 /bin/cat
418 // 0060a000-0060b000 r--p 0000a000 fc:01 787766 /bin/cat
419 // 0060b000-0060c000 rw-p 0000b000 fc:01 787766 /bin/cat
420 // 014ab000-014cc000 rw-p 00000000 00:00 0 [heap]
421 // 7f7d76af8000-7f7d7797c000 r--p 00000000 fc:01 1318064 /usr/lib/locale/locale-archive
422 // 7f7d7797c000-7f7d77b36000 r-xp 00000000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so
423 // 7f7d77b36000-7f7d77d36000 ---p 001ba000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so
424 // 7f7d77d36000-7f7d77d3a000 r--p 001ba000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so
425 // 7f7d77d3a000-7f7d77d3c000 rw-p 001be000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so
426 // 7f7d77d3c000-7f7d77d41000 rw-p 00000000 00:00 0
427 // 7f7d77d41000-7f7d77d64000 r-xp 00000000 fc:01 1180217 /lib/x86_64-linux-gnu/ld-2.19.so
428 // 7f7d77f3f000-7f7d77f42000 rw-p 00000000 00:00 0
429 // 7f7d77f61000-7f7d77f63000 rw-p 00000000 00:00 0
430 // 7f7d77f63000-7f7d77f64000 r--p 00022000 fc:01 1180217 /lib/x86_64-linux-gnu/ld-2.19.so
431 // 7f7d77f64000-7f7d77f65000 rw-p 00023000 fc:01 1180217 /lib/x86_64-linux-gnu/ld-2.19.so
432 // 7f7d77f65000-7f7d77f66000 rw-p 00000000 00:00 0
433 // 7ffc342a2000-7ffc342c3000 rw-p 00000000 00:00 0 [stack]
434 // 7ffc34343000-7ffc34345000 r-xp 00000000 00:00 0 [vdso]
435 // ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
437 var line []byte
438 // next removes and returns the next field in the line.
439 // It also removes from line any spaces following the field.
440 next := func() []byte {
441 j := bytes.IndexByte(line, ' ')
442 if j < 0 {
443 f := line
444 line = nil
445 return f
447 f := line[:j]
448 line = line[j+1:]
449 for len(line) > 0 && line[0] == ' ' {
450 line = line[1:]
452 return f
455 for len(data) > 0 {
456 i := bytes.IndexByte(data, '\n')
457 if i < 0 {
458 line, data = data, nil
459 } else {
460 line, data = data[:i], data[i+1:]
462 addr := next()
463 i = bytes.IndexByte(addr, '-')
464 if i < 0 {
465 continue
467 lo, err := strconv.ParseUint(string(addr[:i]), 16, 64)
468 if err != nil {
469 continue
471 hi, err := strconv.ParseUint(string(addr[i+1:]), 16, 64)
472 if err != nil {
473 continue
475 perm := next()
476 if len(perm) < 4 || perm[2] != 'x' {
477 // Only interested in executable mappings.
478 continue
480 offset, err := strconv.ParseUint(string(next()), 16, 64)
481 if err != nil {
482 continue
484 next() // dev
485 inode := next() // inode
486 if line == nil {
487 continue
489 file := string(line)
490 if len(inode) == 1 && inode[0] == '0' && file == "" {
491 // Huge-page text mappings list the initial fragment of
492 // mapped but unpopulated memory as being inode 0.
493 // Don't report that part.
494 // But [vdso] and [vsyscall] are inode 0, so let non-empty file names through.
495 continue
498 // TODO: pprof's remapMappingIDs makes two adjustments:
499 // 1. If there is an /anon_hugepage mapping first and it is
500 // consecutive to a next mapping, drop the /anon_hugepage.
501 // 2. If start-offset = 0x400000, change start to 0x400000 and offset to 0.
502 // There's no indication why either of these is needed.
503 // Let's try not doing these and see what breaks.
504 // If we do need them, they would go here, before we
505 // enter the mappings into b.mem in the first place.
507 buildID, _ := elfBuildID(file)
508 addMapping(lo, hi, offset, file, buildID)
512 func (b *profileBuilder) addMapping(lo, hi, offset uint64, file, buildID string) {
513 b.mem = append(b.mem, memMap{uintptr(lo), uintptr(hi)})
514 b.pbMapping(tagProfile_Mapping, uint64(len(b.mem)), lo, hi, offset, file, buildID)