github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/internal/traceparser/file.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package traceparser 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 ) 12 13 // scan the trace file finding the header, starts of batches, and the trailer. 14 // the trailer contains strings, stacks, and the clock frequency 15 16 // There are two ways of thinking about the raw trace file. It starts with a 16 17 // byte header "go 1.11 trace\0\0\0" 18 // From the point of 19 // view of the runtime, there is a collection of initializations for each goroutine. 20 // These consist of an EvGoCreate, possibly followed by one of EvGoWaiting or 21 // EvGoInSyscall if the go routine is waiting or in a syscall. 22 // Then there is an EvProcStart for the first running goroutine, so there's a running P, 23 // and then an EvGoStart for the first running goroutine. Then as the program runs, the 24 // runtime emits trace events. Finally when the tracing stops, the runtime emits a footer 25 // consisting of an EvFrequency (to convert ticks to nanoseconds) and some EvTimerGoroutines, 26 // followed by EvStacks for all the stack frames. 27 // 28 // In the file, the header and footer are as described, but all the events in between come 29 // in batches headed by EvBatch with the same P, and have to be rearranged into timestamp order. 30 31 // New() scans once through the file to find the beginnings of all the batches (EvBatch) and 32 // processes the footer extracting the strings and stacks. 33 // Parse() finds the batches that overlap the desired time interval, and processes them into 34 // events, dropping those outside the desired time interval. But it has to derive the missing 35 // initializations from the events it sees, as it has no other access to the state of the runtime. 36 // This is done in robust.go. 37 38 // In more detail, scanFile() is called by commonInit() which is called by either New() or ParseBuffer(). 39 // It extracts the strings, the stacks, and remembers the locations of the Batches (all saved in *Parsed). 40 41 // Parse first computes the rawEvents for the batches that overlap the requested interval. 42 // It then calls createEvents() (events.go) which produces Events. 43 44 func (p *Parsed) parseHeader() error { 45 p.r.Seek(0, 0) 46 var buf [16]byte 47 n, err := p.r.Read(buf[:]) 48 if n != 16 || err != nil { 49 return fmt.Errorf("failed to red header: read %d bytes, not 16 %v", n, err) 50 } 51 // by hand. there are only 6 or so legitimate values; we could search for a match 52 if buf[0] != 'g' || buf[1] != 'o' || buf[2] != ' ' || 53 buf[3] < '1' || buf[3] > '9' || 54 buf[4] != '.' || 55 buf[5] < '1' || buf[5] > '9' { 56 return fmt.Errorf("not a trace file") 57 } 58 ver := int(buf[5] - '0') 59 i := 0 60 for ; buf[6+i] >= '0' && buf[6+i] <= '9' && i < 2; i++ { 61 ver = ver*10 + int(buf[6+i]-'0') 62 } 63 ver += int(buf[3]-'0') * 1000 64 if !bytes.Equal(buf[6+i:], []byte(" trace\x00\x00\x00\x00")[:10-i]) { 65 return fmt.Errorf("not a trace file") 66 } 67 p.Version = ver 68 // PJW: reject 1005 and 1007? They need symbolization, which we don't do. 69 // Further, doing these would require 1.7 or earlier binaries. 70 switch ver { 71 case 1005, 1007: 72 break // no longer supported 73 case 1008, 1009: 74 return nil 75 case 1010, 1011: 76 return nil 77 } 78 return fmt.Errorf("%d unsupported version", ver) 79 } 80 81 func (p *Parsed) scanFile() error { 82 r := p.r 83 // fill in the following values for sure 84 strings := make(map[uint64]string) 85 p.Strings = strings // ok to save maps immediately 86 timerGoIDs := make(map[uint64]bool) 87 p.timerGoids = timerGoIDs 88 stacks := make(map[uint32][]*Frame) 89 framer := make(map[Frame]*Frame) // uniqify *Frame 90 p.Stacks = stacks 91 footerLoc := 0 92 93 var buf [1]byte 94 off := 16 // skip the header 95 n, err := r.Seek(int64(off), 0) 96 if err != nil || n != int64(off) { 97 return fmt.Errorf("Seek to %d got %d, err=%v", off, n, err) 98 } 99 var batchts int64 // from preceding batch 100 var lastEv byte 101 for { 102 off0 := off 103 n, err := r.Read(buf[:1]) 104 if err == io.EOF { 105 break 106 } else if err != nil || n != 1 { 107 return fmt.Errorf("read failed at 0x%x, n=%d, %v", 108 off, n, err) 109 } 110 off += n 111 typ := buf[0] << 2 >> 2 112 if typ == EvNone || typ >= EvCount || 113 EventDescriptions[typ].MinVersion > p.Version { 114 err = fmt.Errorf("unknown event type %v at offset 0x%x, pass 1", typ, off0) 115 return err 116 } 117 // extract and save the strings 118 if typ == EvString { 119 // String dictionary entry [ID, length, string]. 120 var id uint64 121 id, off, err = readVal(r, off) 122 if err != nil { 123 return err 124 } 125 if id == 0 { 126 err = fmt.Errorf("string at offset %d has invalid id 0", off) 127 return err 128 } 129 if strings[id] != "" { 130 err = fmt.Errorf("string at offset %d has duplicate id %v", off, id) 131 return err 132 } 133 var ln uint64 134 ln, off, err = readVal(r, off) 135 if err != nil { 136 return err 137 } 138 if ln == 0 { 139 err = fmt.Errorf("string at offset %d has invalid length 0", off) 140 return err 141 } 142 if ln > 1e6 { 143 err = fmt.Errorf("string at offset %d has too large length %v", off, ln) 144 return err 145 } 146 buf := make([]byte, ln) 147 var n int 148 n, err = io.ReadFull(r, buf) 149 if err != nil { 150 err = fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, ln, err) 151 return err 152 } 153 off += n 154 strings[id] = string(buf) 155 lastEv = EvString 156 continue 157 } 158 p.Count++ 159 if typ == EvFrequency { 160 // found footer, remember location, save value 161 footerLoc = off0 162 } 163 var args []uint64 164 off, args, err = p.argsAt(off0, typ) 165 if err != nil { 166 err = fmt.Errorf("argsAt error %v; off=%d off0=%d %s", 167 err, off, off0, evname(typ)) 168 return err 169 } 170 r.Seek(int64(off), 0) 171 if typ == EvUserLog { 172 _, off, err = readStr(r, off) 173 if err != nil { 174 return err 175 } 176 } 177 if len(args) == 0 { // can't happen in well-formed trace file 178 return fmt.Errorf("len(args)==0 off=0x%x typ=%s", off, evname(typ)) 179 } 180 switch typ { 181 case EvBatch: 182 if footerLoc == 0 { 183 // EvBatch in footer is just to have a header for stacks 184 locp := int64(args[0]) 185 p.batches = append(p.batches, 186 batch{Off: off0, P: locp, Cycles: int64(args[1])}) 187 // at this point we know when the previous batch ended!! 188 batchts = int64(args[1]) 189 if batchts > p.maxticks { 190 p.maxticks = batchts 191 } 192 } 193 case EvFrequency: 194 p.TicksPerSec = int64(args[0]) 195 case EvTimerGoroutine: 196 timerGoIDs[args[0]] = true 197 case EvStack: 198 if len(args) < 2 { 199 return fmt.Errorf("EvStack has too few args %d at 0x%x", 200 len(args), off0) 201 } 202 size := args[1] 203 if size > 1000 { 204 return fmt.Errorf("EvStack has %d frames at 0x%x", 205 size, off0) 206 } 207 want := 2 + 4*size 208 if uint64(len(args)) != want { 209 return fmt.Errorf("EvStack wants %d args, got %d, at 0x%x", 210 len(args), want, off0) 211 } 212 id := args[0] 213 if id != 0 && size > 0 { 214 stk := make([]*Frame, size) 215 for i := 0; i < int(size); i++ { 216 pc := args[2+i*4+0] 217 fn := args[2+i*4+1] 218 file := args[2+i*4+2] 219 line := args[2+i*4+3] 220 stk[i] = &Frame{PC: pc, Fn: strings[fn], File: strings[file], Line: int(line)} 221 if _, ok := framer[*stk[i]]; !ok { 222 framer[*stk[i]] = stk[i] 223 } 224 stk[i] = framer[*stk[i]] 225 } 226 stacks[uint32(id)] = stk 227 } 228 default: 229 if lastEv == EvBatch { 230 // p.MinTsVal is set by the first real event, not the first EvBatch 231 x := batchts + int64(args[0]) 232 if x < p.minticks { 233 p.minticks = x 234 } 235 } 236 batchts += int64(args[0]) 237 if batchts > p.maxticks { 238 p.maxticks = batchts 239 } 240 } 241 lastEv = typ 242 } 243 if footerLoc <= 0 { 244 return fmt.Errorf("malformed trace file, no EvFrequency") 245 } 246 return nil 247 }