strings []string
stringMap map[string]int
locs map[uintptr]int
- funcs map[*runtime.Func]int
+ funcs map[string]int // Package path-qualified function name to Function.ID
mem []memMap
}
}
// locForPC returns the location ID for addr.
-// addr must be a call address (not a return address).
+// addr must be a return PC. This returns the location of the call.
// It may emit to b.pb, so there must be no message encoding in progress.
func (b *profileBuilder) locForPC(addr uintptr) uint64 {
id := uint64(b.locs[addr])
if id != 0 {
return id
}
- f := runtime.FuncForPC(addr)
- if f != nil && f.Name() == "runtime.goexit" {
+
+ // Expand this one address using CallersFrames so we can cache
+ // each expansion. In general, CallersFrames takes a whole
+ // stack, but in this case we know there will be no skips in
+ // the stack and we have return PCs anyway.
+ frames := runtime.CallersFrames([]uintptr{addr})
+ frame, more := frames.Next()
+ if frame.Function == "runtime.goexit" {
+ // Short-circuit if we see runtime.goexit so the loop
+ // below doesn't allocate a useless empty location.
return 0
}
- funcID, lineno := b.funcForPC(addr)
+
+ if frame.PC == 0 {
+ // If we failed to resolve the frame, at least make up
+ // a reasonable call PC. This mostly happens in tests.
+ frame.PC = addr - 1
+ }
+
+ // We can't write out functions while in the middle of the
+ // Location message, so record new functions we encounter and
+ // write them out after the Location.
+ type newFunc struct {
+ id uint64
+ name, file string
+ }
+ newFuncs := make([]newFunc, 0, 8)
+
id = uint64(len(b.locs)) + 1
b.locs[addr] = int(id)
start := b.pb.startMessage()
b.pb.uint64Opt(tagLocation_ID, id)
- b.pb.uint64Opt(tagLocation_Address, uint64(addr))
- b.pbLine(tagLocation_Line, funcID, int64(lineno))
+ b.pb.uint64Opt(tagLocation_Address, uint64(frame.PC))
+ for frame.Function != "runtime.goexit" {
+ // Write out each line in frame expansion.
+ funcID := uint64(b.funcs[frame.Function])
+ if funcID == 0 {
+ funcID = uint64(len(b.funcs)) + 1
+ b.funcs[frame.Function] = int(funcID)
+ newFuncs = append(newFuncs, newFunc{funcID, frame.Function, frame.File})
+ }
+ b.pbLine(tagLocation_Line, funcID, int64(frame.Line))
+ if !more {
+ break
+ }
+ frame, more = frames.Next()
+ }
if len(b.mem) > 0 {
i := sort.Search(len(b.mem), func(i int) bool {
return b.mem[i].end > addr
}
}
b.pb.endMessage(tagProfile_Location, start)
- b.flush()
- return id
-}
-// funcForPC returns the func ID and line number for addr.
-// It may emit to b.pb, so there must be no message encoding in progress.
-func (b *profileBuilder) funcForPC(addr uintptr) (funcID uint64, lineno int) {
- f := runtime.FuncForPC(addr)
- if f == nil {
- return 0, 0
- }
- file, lineno := f.FileLine(addr)
- funcID = uint64(b.funcs[f])
- if funcID != 0 {
- return funcID, lineno
+ // Write out functions we found during frame expansion.
+ for _, fn := range newFuncs {
+ start := b.pb.startMessage()
+ b.pb.uint64Opt(tagFunction_ID, fn.id)
+ b.pb.int64Opt(tagFunction_Name, b.stringIndex(fn.name))
+ b.pb.int64Opt(tagFunction_SystemName, b.stringIndex(fn.name))
+ b.pb.int64Opt(tagFunction_Filename, b.stringIndex(fn.file))
+ b.pb.endMessage(tagProfile_Function, start)
}
- funcID = uint64(len(b.funcs)) + 1
- b.funcs[f] = int(funcID)
- name := f.Name()
- start := b.pb.startMessage()
- b.pb.uint64Opt(tagFunction_ID, funcID)
- b.pb.int64Opt(tagFunction_Name, b.stringIndex(name))
- b.pb.int64Opt(tagFunction_SystemName, b.stringIndex(name))
- b.pb.int64Opt(tagFunction_Filename, b.stringIndex(file))
- b.pb.endMessage(tagProfile_Function, start)
b.flush()
- return funcID, lineno
+ return id
}
// newProfileBuilder returns a new profileBuilder.
strings: []string{""},
stringMap: map[string]int{"": 0},
locs: map[uintptr]int{},
- funcs: map[*runtime.Func]int{},
+ funcs: map[string]int{},
}
b.readMapping()
return b
locs = locs[:0]
for i, addr := range e.stk {
- // Addresses from stack traces point to the next instruction after
- // each call. Adjust by -1 to land somewhere on the actual call
- // (except for the leaf, which is not a call).
- if i > 0 {
- addr--
+ // Addresses from stack traces point to the
+ // next instruction after each call, except
+ // for the leaf, which points to where the
+ // signal occurred. locForPC expects return
+ // PCs, so increment the leaf address to look
+ // like a return PC.
+ if i == 0 {
+ addr++
}
l := b.locForPC(addr)
if l == 0 { // runtime.goexit