108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
/*
|
|
first word
|
|
high 8 bits is ee, which is an invalid address on amd64.
|
|
next 8 bits is protocol version
|
|
next 16 bits is unused, MBZ. Later, we can make it a packet type.
|
|
next 16 bits is core id
|
|
next 8 bits is unused
|
|
next 8 bits is # words following.
|
|
|
|
second word is time in ns. (soon to be tsc ticks)
|
|
|
|
Third and following words are PCs, there must be at least one of them.
|
|
*/
|
|
type sample struct {
|
|
Wordcount, _ uint8
|
|
Coreid uint16
|
|
_ uint16
|
|
Version, Marker uint8
|
|
Ns uint64
|
|
}
|
|
|
|
type backtrace struct {
|
|
Pcs []uint64
|
|
}
|
|
|
|
/* the docs lie. Perl expects Count to be zero. I only wasted a day figuring this out. */
|
|
type hdr struct {
|
|
Count, Slots, Format, Period, Padding uint64
|
|
}
|
|
|
|
type record struct {
|
|
Count, Size uint64
|
|
Pcs []uint64
|
|
}
|
|
|
|
type trailer struct {
|
|
Zero, One, Zeroh uint64
|
|
}
|
|
|
|
func main() {
|
|
var s sample
|
|
|
|
r := io.Reader(os.Stdin)
|
|
w := io.Writer(os.Stdout)
|
|
|
|
records := make(map[string]uint64, 16384)
|
|
backtraces := make(map[string][]uint64, 1024)
|
|
|
|
/* ignore the documentation, it's wrong, first word must be zero.
|
|
* the perl code that figures word length depends on it.
|
|
*/
|
|
hdr := hdr{0, 3, 0, 10000, 0}
|
|
trailer := trailer{0, 1, 0}
|
|
start := uint64(0)
|
|
end := start
|
|
nsamples := end
|
|
for binary.Read(r, binary.LittleEndian, &s) == nil {
|
|
numpcs := int(s.Wordcount)
|
|
bt := make([]uint64, numpcs)
|
|
binary.Read(r, binary.LittleEndian, &bt)
|
|
//fmt.Printf("%v\n", bt)
|
|
record := ""
|
|
/* Fix the symbols. pprof was unhappy about the 0xfffffff.
|
|
* N.B. The fact that we have to mess with the bt values
|
|
* is the reason we did not write a stringer for bt.
|
|
*/
|
|
for i := range bt {
|
|
bt[i] = bt[i] & ((uint64(1) << 32) - 1)
|
|
record = record + fmt.Sprintf("0x%x ", bt[i])
|
|
}
|
|
records[record]++
|
|
backtraces[record] = bt
|
|
//fmt.Printf("%v %d %d %x %v record %v\n", s, s.Wordcount, s.Coreid, s.Ns, bt, record)
|
|
/* how sad, once we go to ticks this gets ugly. */
|
|
if start == 0 {
|
|
start = s.Ns
|
|
}
|
|
end = s.Ns
|
|
nsamples++
|
|
}
|
|
/* we'll need to fix this once we go to ticks. */
|
|
hdr.Period = (end - start) / nsamples
|
|
hdr.Count = uint64(0) // !@$@!#$!@#$len(records))
|
|
//fmt.Printf("start %v end %v nsamples %d period %d\n", start, end, nsamples, hdr.Period)
|
|
binary.Write(w, binary.LittleEndian, &hdr)
|
|
out := make([]uint64, 2)
|
|
/* note that the backtrace length varies. But we're good with that. */
|
|
for key, v := range records {
|
|
bt := backtraces[key]
|
|
out[0] = v
|
|
out[1] = uint64(len(bt))
|
|
dump := append(out, bt...)
|
|
//fmt.Printf("dump %v\n", dump)
|
|
binary.Write(w, binary.LittleEndian, &dump)
|
|
}
|
|
binary.Write(w, binary.LittleEndian, &trailer)
|
|
|
|
}
|