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