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) }