Home | History | Annotate | Download | only in tracer
      1 // Copyright 2016 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package tracer
     16 
     17 import (
     18 	"bufio"
     19 	"os"
     20 	"sort"
     21 	"strconv"
     22 	"strings"
     23 	"time"
     24 )
     25 
     26 type eventEntry struct {
     27 	Name  string
     28 	Begin uint64
     29 	End   uint64
     30 }
     31 
     32 func (t *tracerImpl) importEvents(entries []*eventEntry) {
     33 	sort.Slice(entries, func(i, j int) bool {
     34 		return entries[i].Begin < entries[j].Begin
     35 	})
     36 
     37 	cpus := []uint64{}
     38 	for _, entry := range entries {
     39 		tid := -1
     40 		for cpu, endTime := range cpus {
     41 			if endTime <= entry.Begin {
     42 				tid = cpu
     43 				cpus[cpu] = entry.End
     44 				break
     45 			}
     46 		}
     47 		if tid == -1 {
     48 			tid = len(cpus)
     49 			cpus = append(cpus, entry.End)
     50 		}
     51 
     52 		t.writeEvent(&viewerEvent{
     53 			Name:  entry.Name,
     54 			Phase: "X",
     55 			Time:  entry.Begin,
     56 			Dur:   entry.End - entry.Begin,
     57 			Pid:   1,
     58 			Tid:   uint64(tid),
     59 		})
     60 	}
     61 }
     62 
     63 // ImportNinjaLog reads a .ninja_log file from ninja and writes the events out
     64 // to the trace.
     65 //
     66 // startOffset is when the ninja process started, and is used to position the
     67 // relative times from the ninja log into the trace. It's also used to skip
     68 // reading the ninja log if nothing was run.
     69 func (t *tracerImpl) ImportNinjaLog(thread Thread, filename string, startOffset time.Time) {
     70 	t.Begin("ninja log import", thread)
     71 	defer t.End(thread)
     72 
     73 	if stat, err := os.Stat(filename); err != nil {
     74 		t.log.Println("Missing ninja log:", err)
     75 		return
     76 	} else if stat.ModTime().Before(startOffset) {
     77 		t.log.Verboseln("Ninja log not modified, not importing any entries.")
     78 		return
     79 	}
     80 
     81 	f, err := os.Open(filename)
     82 	if err != nil {
     83 		t.log.Println("Error opening ninja log:", err)
     84 		return
     85 	}
     86 	defer f.Close()
     87 
     88 	s := bufio.NewScanner(f)
     89 	header := true
     90 	entries := []*eventEntry{}
     91 	prevEnd := 0
     92 	offset := uint64(startOffset.UnixNano()) / 1000
     93 	for s.Scan() {
     94 		if header {
     95 			hdr := s.Text()
     96 			if hdr != "# ninja log v5" {
     97 				t.log.Printf("Unknown ninja log header: %q", hdr)
     98 				return
     99 			}
    100 			header = false
    101 			continue
    102 		}
    103 
    104 		fields := strings.Split(s.Text(), "\t")
    105 		begin, err := strconv.Atoi(fields[0])
    106 		if err != nil {
    107 			t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
    108 			return
    109 		}
    110 		end, err := strconv.Atoi(fields[1])
    111 		if err != nil {
    112 			t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
    113 			return
    114 		}
    115 		if end < prevEnd {
    116 			entries = nil
    117 		}
    118 		prevEnd = end
    119 		entries = append(entries, &eventEntry{
    120 			Name:  fields[3],
    121 			Begin: offset + uint64(begin)*1000,
    122 			End:   offset + uint64(end)*1000,
    123 		})
    124 	}
    125 	if err := s.Err(); err != nil {
    126 		t.log.Println("Unable to parse ninja log:", err)
    127 		return
    128 	}
    129 
    130 	t.importEvents(entries)
    131 }
    132