github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/internal/traceparser/tr.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 parses the trace files produced by runtime.StartTrace 6 package traceparser 7 8 import ( 9 "fmt" 10 "internal/traceparser/filebuf" 11 "io" 12 "strings" 13 ) 14 15 // Parsed is the result of parsing a trace file 16 type Parsed struct { 17 // Set by New 18 Name string // File's name 19 Size int64 // File's size 20 Count int64 // approximate number of all events 21 MaxTs int64 // range of all events, in nanoseconds 22 Strings map[uint64]string 23 Stacks map[uint32][]*Frame 24 Version int // version of the trace file from header 25 TicksPerSec int64 // from EvFrequency in trailer 26 minticks, maxticks int64 // from Init 27 r filebuf.Buf // implements io.Seek and io.Read 28 batches []batch // location of each Batch and time of start 29 timerGoids map[uint64]bool 30 // the following are per Parse 31 MinWant, MaxWant int64 // range wanted, from the arguments to Parse() 32 Err error // set by internal functions to stop further processing 33 Events []*Event // after Parse, the events from MinWant to MaxWant 34 Preflen int // how long a prefix we added 35 Ignored int // how many events we elided 36 Added int // how many events we added, not including the prefix 37 // internal processing variables 38 seenArgs map[uint64]*[]uint64 39 byproc map[int][]*Event 40 lastGs map[int]uint64 41 lastG uint64 42 lastP int 43 lastTs int64 44 } 45 46 func (p *Parsed) String() string { 47 ans := []string{} 48 ans = append(ans, fmt.Sprintf("%s Sz:%d Count:%d MaxTs:%d #strs:%d #stks:%d", 49 p.Name, p.Size, p.Count, p.MaxTs, len(p.Strings), len(p.Stacks))) 50 ans = append(ans, fmt.Sprintf("%d clock:%d ticks:(%d,%d) #b:%d", 51 p.Version, p.TicksPerSec, p.minticks, p.maxticks, len(p.batches))) 52 return strings.Join(ans, "\n\t") 53 } 54 55 // clean up after previous call to Parse 56 func (p *Parsed) clean() { 57 // some of these are redundant 58 p.Err = nil 59 p.Events = nil 60 p.Preflen = 0 61 p.Ignored = 0 62 p.Added = 0 63 p.seenArgs = nil // redundant, but safe 64 p.byproc = nil 65 p.lastGs = nil 66 p.lastG = 0 67 p.lastTs = 0 68 } 69 70 // Frame is a frame in a stack traces 71 type Frame struct { 72 PC uint64 73 Fn string 74 File string 75 Line int 76 } 77 78 // An Event is the parsed form of a single trace event 79 type Event struct { 80 Type byte 81 P int32 82 Ts int64 83 G uint64 84 StkID uint32 // key to Parsed.Stacks 85 Args [3]uint64 86 SArgs []string // EvUserLog has 2. Others are 1 or none 87 Link *Event 88 } 89 90 // Batch remembers the EvBatch events. PJW: keep an index of User events? 91 type batch struct { 92 Off int 93 P int64 94 Cycles int64 // as read from EvBatch 95 Nano int64 // start time of batch, set in commonInit() 96 raws []rawEvent // filled in during Parse() for those batches that overlap the desired interval 97 } 98 99 // rawEvent is a raw event parsed from batches that overlap the time interval 100 type rawEvent struct { // about 75 bytes 101 // the choice of what to share (args) and what to make unique per rawEvent 102 // (arg0, sarg) was done by measuring the space impact of various choices. 103 off uint32 // offset in batch (at batch.Off + off in file) 104 typ byte 105 arg0 uint64 106 args *[]uint64 // remainder of the args (frequently nil), shared 107 sarg string 108 } 109 110 func (r rawEvent) String() string { 111 if r.args != nil && len(*r.args) > 0 { 112 return fmt.Sprintf("[%s %d %v %s]", evname(r.typ), r.arg0, *r.args, r.sarg) 113 } 114 return fmt.Sprintf("[%s, %d, [], %s]", evname(r.typ), r.arg0, r.sarg) 115 } 116 117 // New scans the trace file, finding the number of events, the earliest and latest 118 // timestamps, and the stacks and strings referenced in the file. 119 func New(fname string) (*Parsed, error) { 120 fd, err := filebuf.New(fname) 121 if err != nil { 122 return nil, err 123 } 124 return commonInit(fd, fname) 125 } 126 127 // ParseError may be returned by New() or ParseBuffer() to make available 128 // some information in the case that the raw trace file seems to contain 129 // negative time stamps. (In P, Name, Size, count, Strings, Stacks, Versions are valid, 130 // and MaxTs or TicksPerSec is negative.) 131 type ParseError struct { 132 P *Parsed 133 Err error 134 } 135 136 func (pe ParseError) Error() string { 137 return pe.Err.Error() 138 } 139 140 func commonInit(fd filebuf.Buf, fname string) (*Parsed, error) { 141 ans := &Parsed{Name: fname, minticks: 1 << 62} // minticks can only decrease 142 var err error 143 defer func() { 144 if err != nil { 145 fd.Close() // try to clean up after error 146 } 147 }() 148 ans.Size = fd.Size() 149 ans.r = fd 150 // parseRaw here for header, trailer: clock, stacks, strings, 151 if err = ans.parseHeader(); err != nil { 152 return nil, err 153 } 154 if err = ans.scanFile(); err != nil { 155 return nil, err 156 } 157 // done with seenArgs 158 ans.seenArgs = nil 159 // convert the clicks in batches to nanoseconds 160 ans.toNanoseconds() 161 if ans.MaxTs <= 0 || ans.TicksPerSec <= 0 { 162 err := ParseError{ 163 P: ans, 164 Err: fmt.Errorf("corrupt trace file: negative time: (max TS=%d, ticks per sec=%d", 165 ans.MaxTs, ans.TicksPerSec), 166 } 167 return nil, err 168 } 169 return ans, nil 170 } 171 172 // Parse parses the events in the interval: start <= ts <= start+length. 173 // f, if not nil, will be called at various stages of the parse, each identified by the string 174 // argument. It could report on elapsed time, or memory usage, or whatever the user wants. 175 // The number of times it is called and the contents of the string argument are both 176 // changeable details of the implementation. Parse is not safe for concurrent use. 177 func (p *Parsed) Parse(start, length int64, f func(string)) error { 178 p.clean() 179 if f == nil { 180 f = func(string) {} // avoid any further testing for nil 181 } 182 183 p.MinWant = start 184 p.MaxWant = start + length 185 // arrange the slice of batches by P 186 byp := map[int64][]*batch{} 187 // PJW: keep track of the order the Ps occur and use that for batchify 188 for i, b := range p.batches { 189 byp[b.P] = append(byp[b.P], &p.batches[i]) 190 p.batches[i].raws = nil // reset from last call to Parse 191 } 192 // batchify the ones that overlap the time range 193 for _, v := range byp { 194 for i := 0; i < len(v); i++ { 195 b := v[i] 196 var bnext *batch 197 if i < len(v)-1 { 198 bnext = v[i+1] 199 } 200 if b.Nano >= p.MaxWant { 201 // starts too late 202 continue 203 } else if b.Nano <= p.MinWant && (bnext != nil && bnext.Nano <= p.MinWant) { 204 // entirely too early 205 continue 206 } 207 err := p.batchify(b) 208 if err != nil { 209 return err 210 } 211 } 212 } 213 f("batchify done") 214 return p.createEvents(f) 215 } 216 217 // ParseBuffer treats its argument as a trace file, and returns the 218 // result of parsing it 219 func ParseBuffer(rd io.Reader) (*Parsed, error) { 220 pr, err := filebuf.FromReader(rd) 221 if err != nil { 222 return nil, err 223 } 224 p, err := commonInit(pr, "<buf>") 225 if err != nil { 226 return nil, err 227 } 228 // need the version and the initial scan 229 err = p.Parse(0, 1<<62, nil) 230 if err != nil { 231 return nil, err 232 } 233 return p, nil 234 } 235 236 // called from commonInit to compute the nanosecond when batches start 237 func (p *Parsed) toNanoseconds() { 238 minCycles := p.minticks 239 freq := 1e9 / float64(p.TicksPerSec) 240 // Batches, and more to come. Don't call this twice! 241 for i, ex := range p.batches { 242 p.batches[i].Nano = int64(float64(ex.Cycles-minCycles) * freq) 243 } 244 p.MaxTs = int64(float64(p.maxticks-minCycles) * freq) 245 } 246 247 // argsAt returns the args of an event in the file and the offset for the next event. 248 // 249 // For EvString it returns off, nil, nil, and 250 // for EvUserLog it ignores the string argument, which must be read by the 251 // caller. 252 func (p *Parsed) argsAt(off int, check byte) (int, []uint64, error) { 253 off0 := off 254 r := p.r 255 loc, err := r.Seek(int64(off), 0) 256 if err != nil { 257 panic(err) 258 } 259 var buf [1]byte 260 n, err := r.Read(buf[:]) 261 if err != nil || n != 1 { 262 return 0, nil, fmt.Errorf("read failed at 0x%x, %d %v, loc=%d", 263 off, n, err, loc) 264 } 265 off += n 266 typ := buf[0] << 2 >> 2 267 narg := buf[0]>>6 + 1 268 inlineArgs := byte(4) 269 270 if typ == EvNone || typ >= EvCount || 271 EventDescriptions[typ].MinVersion > p.Version { 272 return 0, nil, fmt.Errorf("unk type %v at offset 0x%x", typ, off0) 273 } 274 if typ == EvString { // skip, wihtout error checking 275 _, off, err = readVal(r, off) 276 var ln uint64 277 ln, off, err = readVal(r, off) 278 off += int(ln) 279 return off, nil, nil 280 } 281 args := []uint64{} 282 if narg < inlineArgs { 283 for i := 0; i < int(narg); i++ { 284 var v uint64 285 v, off, err = readVal(r, off) 286 if err != nil { 287 err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err) 288 return 0, nil, err 289 } 290 args = append(args, v) 291 } 292 } else { 293 // More than inlineArgs args, the first value is length of the event in bytes. 294 var v uint64 295 v, off, err = readVal(r, off) 296 if err != nil { 297 err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err) 298 return 0, nil, err 299 } 300 evLen := v 301 off1 := off 302 for evLen > uint64(off-off1) { 303 v, off, err = readVal(r, off) 304 if err != nil { 305 err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err) 306 return 0, nil, err 307 } 308 args = append(args, v) 309 } 310 if evLen != uint64(off-off1) { 311 err = fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1) 312 return 0, nil, err 313 } 314 } 315 // This routine does not read the string argument. Callers must tread EvUserLog specially. 316 return off, args, nil 317 } 318 319 // read a string from r 320 func readStr(r io.Reader, off0 int) (s string, off int, err error) { 321 var sz uint64 322 sz, off, err = readVal(r, off0) 323 if err != nil || sz == 0 { 324 return "", off, err 325 } 326 if sz > 1e6 { 327 return "", off, fmt.Errorf("string at offset %d is too large (len=%d)", off, sz) 328 } 329 buf := make([]byte, sz) 330 n, err := io.ReadFull(r, buf) 331 if err != nil || sz != uint64(n) { 332 return "", off + n, fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, sz, err) 333 } 334 return string(buf), off + n, nil 335 } 336 337 // readVal reads unsigned base-128 value from r. 338 func readVal(r io.Reader, off0 int) (v uint64, off int, err error) { 339 off = off0 340 for i := 0; i < 10; i++ { 341 var buf [1]byte 342 var n int 343 n, err = r.Read(buf[:]) 344 if err != nil || n != 1 { 345 return 0, 0, fmt.Errorf("failed to read trace at offset %d: read %v, error %v", off0, n, err) 346 } 347 off++ 348 v |= uint64(buf[0]&0x7f) << (uint(i) * 7) 349 if buf[0]&0x80 == 0 { 350 return 351 } 352 } 353 return 0, 0, fmt.Errorf("bad value at offset 0x%x", off0) 354 } 355 356 // OSStats reports on the underlying i/o. If p was created by New, 357 // the fields report filesystem activity. If p was created by ParseBuffer, 358 // only Size is set. 359 func (p *Parsed) OSStats() filebuf.Stat { 360 return p.r.Stats() 361 } 362 363 func (ev *Event) String() string { 364 var tslink int64 365 if ev.Link != nil { 366 tslink = ev.Link.Ts 367 } 368 return fmt.Sprintf("[g:%d p:%d %s/%d %v %v %x ->%x]", 369 ev.G, ev.P, evname(ev.Type), ev.Type, 370 ev.Args, ev.SArgs, ev.Ts, tslink) 371 372 } 373 374 func evname(t byte) string { 375 if t >= EvCount || t < 0 { 376 return fmt.Sprintf("typ%d?", t) 377 } 378 return EventDescriptions[t].Name 379 } 380 381 // Close the underlying file. 382 func (p *Parsed) Close() error { 383 return p.r.Close() 384 } 385 386 // Event types in the trace. 387 // Verbatim copy from src/runtime/trace.go with the "trace" prefix removed. 388 const ( 389 EvNone = 0 // unused 390 EvBatch = 1 // start of per-P batch of events [pid, timestamp] 391 EvFrequency = 2 // contains tracer timer frequency [frequency (ticks per second)] 392 EvStack = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] 393 EvGomaxprocs = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] 394 EvProcStart = 5 // start of P [timestamp, thread id] 395 EvProcStop = 6 // stop of P [timestamp] 396 EvGCStart = 7 // GC start [timestamp, seq, stack id] 397 EvGCDone = 8 // GC done [timestamp] 398 EvGCSTWStart = 9 // GC mark termination start [timestamp, kind] 399 EvGCSTWDone = 10 // GC mark termination done [timestamp] 400 EvGCSweepStart = 11 // GC sweep start [timestamp, stack id] 401 EvGCSweepDone = 12 // GC sweep done [timestamp, swept, reclaimed] 402 EvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id] 403 EvGoStart = 14 // goroutine starts running [timestamp, goroutine id, seq] 404 EvGoEnd = 15 // goroutine ends [timestamp] 405 EvGoStop = 16 // goroutine stops (like in select{}) [timestamp, stack] 406 EvGoSched = 17 // goroutine calls Gosched [timestamp, stack] 407 EvGoPreempt = 18 // goroutine is preempted [timestamp, stack] 408 EvGoSleep = 19 // goroutine calls Sleep [timestamp, stack] 409 EvGoBlock = 20 // goroutine blocks [timestamp, stack] 410 EvGoUnblock = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack] 411 EvGoBlockSend = 22 // goroutine blocks on chan send [timestamp, stack] 412 EvGoBlockRecv = 23 // goroutine blocks on chan recv [timestamp, stack] 413 EvGoBlockSelect = 24 // goroutine blocks on select [timestamp, stack] 414 EvGoBlockSync = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack] 415 EvGoBlockCond = 26 // goroutine blocks on Cond [timestamp, stack] 416 EvGoBlockNet = 27 // goroutine blocks on network [timestamp, stack] 417 EvGoSysCall = 28 // syscall enter [timestamp, stack] 418 EvGoSysExit = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp] 419 EvGoSysBlock = 30 // syscall blocks [timestamp] 420 EvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] 421 EvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] 422 EvHeapAlloc = 33 // memstats.heap_live change [timestamp, heap_alloc] 423 EvNextGC = 34 // memstats.next_gc change [timestamp, next_gc] 424 EvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id] 425 EvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] 426 EvString = 37 // string dictionary entry [ID, length, string] 427 EvGoStartLocal = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id] 428 EvGoUnblockLocal = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack] 429 EvGoSysExitLocal = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp] 430 EvGoStartLabel = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id] 431 EvGoBlockGC = 42 // goroutine blocks on GC assist [timestamp, stack] 432 EvGCMarkAssistStart = 43 // GC mark assist start [timestamp, stack] 433 EvGCMarkAssistDone = 44 // GC mark assist done [timestamp] 434 EvUserTaskCreate = 45 // trace.NewContext [timestamp, internal task id, internal parent id, stack, name string] 435 EvUserTaskEnd = 46 // end of task [timestamp, internal task id, stack] 436 EvUserRegion = 47 // trace.WithSpan [timestamp, internal task id, mode(0:start, 1:end), stack, name string] 437 EvUserLog = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] 438 EvCount = 49 439 ) 440 441 // EventDescriptions describe the Events 442 var EventDescriptions = [EvCount]struct { 443 Name string 444 MinVersion int 445 Stack bool 446 Args []string 447 SArgs []string // string arguments 448 }{ 449 EvNone: {"None", 1005, false, []string{}, nil}, 450 EvBatch: {"Batch", 1005, false, []string{"p", "ticks"}, nil}, // in 1.5 format it was {"p", "seq", "ticks"} 451 EvFrequency: {"Frequency", 1005, false, []string{"freq"}, nil}, // in 1.5 format it was {"freq", "unused"} 452 EvStack: {"Stack", 1005, false, []string{"id", "siz"}, nil}, 453 EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}, nil}, 454 EvProcStart: {"ProcStart", 1005, false, []string{"thread"}, nil}, 455 EvProcStop: {"ProcStop", 1005, false, []string{}, nil}, 456 EvGCStart: {"GCStart", 1005, true, []string{"seq"}, nil}, // in 1.5 format it was {} 457 EvGCDone: {"GCDone", 1005, false, []string{}, nil}, 458 EvGCSTWStart: {"GCSTWStart", 1005, false, []string{"kindid"}, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) 459 EvGCSTWDone: {"GCSTWDone", 1005, false, []string{}, nil}, 460 EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}, nil}, 461 EvGCSweepDone: {"GCSweepDone", 1005, false, []string{"swept", "reclaimed"}, nil}, // before 1.9, format was {} 462 EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}, nil}, 463 EvGoStart: {"GoStart", 1005, false, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} 464 EvGoEnd: {"GoEnd", 1005, false, []string{}, nil}, 465 EvGoStop: {"GoStop", 1005, true, []string{}, nil}, 466 EvGoSched: {"GoSched", 1005, true, []string{}, nil}, 467 EvGoPreempt: {"GoPreempt", 1005, true, []string{}, nil}, 468 EvGoSleep: {"GoSleep", 1005, true, []string{}, nil}, 469 EvGoBlock: {"GoBlock", 1005, true, []string{}, nil}, 470 EvGoUnblock: {"GoUnblock", 1005, true, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} 471 EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}, nil}, 472 EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}, nil}, 473 EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}, nil}, 474 EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}, nil}, 475 EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}, nil}, 476 EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}, nil}, 477 EvGoSysCall: {"GoSysCall", 1005, true, []string{}, nil}, 478 EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}, nil}, 479 EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}, nil}, 480 EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}, nil}, 481 EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}, nil}, 482 EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}, nil}, 483 EvNextGC: {"NextGC", 1005, false, []string{"mem"}, nil}, 484 EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g"}, nil}, // in 1.5 format it was {"g", "unused"} 485 EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}, nil}, 486 EvString: {"String", 1007, false, []string{}, nil}, 487 EvGoStartLocal: {"GoStartLocal", 1007, false, []string{"g"}, nil}, 488 EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}, nil}, 489 EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}, nil}, 490 EvGoStartLabel: {"GoStartLabel", 1008, false, []string{"g", "seq", "labelid"}, []string{"label"}}, 491 EvGoBlockGC: {"GoBlockGC", 1008, true, []string{}, nil}, 492 EvGCMarkAssistStart: {"GCMarkAssistStart", 1009, true, []string{}, nil}, 493 EvGCMarkAssistDone: {"GCMarkAssistDone", 1009, false, []string{}, nil}, 494 EvUserTaskCreate: {"UserTaskCreate", 1011, true, []string{"taskid", "pid", "typeid"}, []string{"name"}}, 495 EvUserTaskEnd: {"UserTaskEnd", 1011, true, []string{"taskid"}, nil}, 496 EvUserRegion: {"UserRegion", 1011, true, []string{"taskid", "mode", "typeid"}, []string{"name"}}, 497 EvUserLog: {"UserLog", 1011, true, []string{"id", "keyid"}, []string{"category", "message"}}, 498 }