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  }