op2.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package main
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. /*
  9. first word
  10. high 8 bits is ee, which is an invalid address on amd64.
  11. next 8 bits is protocol version
  12. next 16 bits is unused, MBZ. Later, we can make it a packet type.
  13. next 16 bits is core id
  14. next 8 bits is unused
  15. next 8 bits is # words following.
  16. second word is time in ns. (soon to be tsc ticks)
  17. Third and following words are PCs, there must be at least one of them.
  18. */
  19. type sample struct {
  20. Wordcount,_ uint8
  21. Coreid uint16
  22. _ uint16
  23. Version, Marker uint8
  24. Ns uint64
  25. }
  26. type backtrace struct {
  27. Pcs []uint64
  28. }
  29. /* the docs lie. Perl expects Count to be zero. I only wasted a day figuring this out. */
  30. type hdr struct {
  31. Count, Slots, Format, Period, Padding uint64
  32. }
  33. type record struct {
  34. Count, Size uint64
  35. Pcs []uint64
  36. }
  37. type trailer struct {
  38. Zero, One, Zeroh uint64
  39. }
  40. func main() {
  41. var s sample
  42. r := io.Reader(os.Stdin)
  43. w := io.Writer(os.Stdout)
  44. records := make(map[string]uint64, 16384)
  45. backtraces := make(map[string][]uint64,1024)
  46. /* ignore the documentation, it's wrong, first word must be zero.
  47. * the perl code that figures word length depends on it.
  48. */
  49. hdr := hdr{0,3,0,10000,0}
  50. trailer := trailer{0,1,0}
  51. start := uint64(0)
  52. end := start
  53. nsamples := end
  54. for binary.Read(r, binary.LittleEndian, &s) == nil {
  55. numpcs := int(s.Wordcount)
  56. bt := make([]uint64, numpcs)
  57. binary.Read(r, binary.LittleEndian, &bt)
  58. //fmt.Printf("%v\n", bt)
  59. record := ""
  60. /* Fix the symbols. pprof was unhappy about the 0xfffffff.
  61. * N.B. The fact that we have to mess with the bt values
  62. * is the reason we did not write a stringer for bt.
  63. */
  64. for i := range(bt) {
  65. bt[i] = bt[i] & ((uint64(1)<<32)-1)
  66. record = record + fmt.Sprintf("0x%x ",bt[i])
  67. }
  68. records[record]++
  69. backtraces[record] = bt
  70. //fmt.Printf("%v %d %d %x %v record %v\n", s, s.Wordcount, s.Coreid, s.Ns, bt, record)
  71. /* how sad, once we go to ticks this gets ugly. */
  72. if start == 0 {
  73. start = s.Ns
  74. }
  75. end = s.Ns
  76. nsamples ++
  77. }
  78. /* we'll need to fix this once we go to ticks. */
  79. hdr.Period = (end - start) / nsamples
  80. hdr.Count = uint64(0) // !@$@!#$!@#$len(records))
  81. //fmt.Printf("start %v end %v nsamples %d period %d\n", start, end, nsamples, hdr.Period)
  82. binary.Write(w, binary.LittleEndian, &hdr)
  83. out := make([]uint64, 2)
  84. /* note that the backtrace length varies. But we're good with that. */
  85. for key, v := range(records) {
  86. bt := backtraces[key]
  87. out[0] = v
  88. out[1] = uint64(len(bt))
  89. dump := append(out, bt...)
  90. //fmt.Printf("dump %v\n", dump)
  91. binary.Write(w, binary.LittleEndian, &dump)
  92. }
  93. binary.Write(w, binary.LittleEndian, &trailer)
  94. }