github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/ui/tracer/tracer.go (about)

     1  // Copyright 2017 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  // This package implements a trace file writer, whose files can be opened in
    16  // chrome://tracing.
    17  //
    18  // It implements the JSON Array Format defined here:
    19  // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
    20  package tracer
    21  
    22  import (
    23  	"bytes"
    24  	"compress/gzip"
    25  	"encoding/json"
    26  	"fmt"
    27  	"io"
    28  	"os"
    29  	"strings"
    30  	"sync"
    31  	"time"
    32  
    33  	"android/soong/ui/logger"
    34  )
    35  
    36  type Thread uint64
    37  
    38  const (
    39  	MainThread     = Thread(iota)
    40  	MaxInitThreads = Thread(iota)
    41  )
    42  
    43  type Tracer interface {
    44  	Begin(name string, thread Thread)
    45  	End(thread Thread)
    46  	Complete(name string, thread Thread, begin, end uint64)
    47  
    48  	ImportMicrofactoryLog(filename string)
    49  	ImportNinjaLog(thread Thread, filename string, startOffset time.Time)
    50  
    51  	NewThread(name string) Thread
    52  }
    53  
    54  type tracerImpl struct {
    55  	lock sync.Mutex
    56  	log  logger.Logger
    57  
    58  	buf  bytes.Buffer
    59  	file *os.File
    60  	w    io.WriteCloser
    61  
    62  	firstEvent bool
    63  	nextTid    uint64
    64  }
    65  
    66  var _ Tracer = &tracerImpl{}
    67  
    68  type viewerEvent struct {
    69  	Name  string      `json:"name,omitempty"`
    70  	Phase string      `json:"ph"`
    71  	Scope string      `json:"s,omitempty"`
    72  	Time  uint64      `json:"ts"`
    73  	Dur   uint64      `json:"dur,omitempty"`
    74  	Pid   uint64      `json:"pid"`
    75  	Tid   uint64      `json:"tid"`
    76  	ID    uint64      `json:"id,omitempty"`
    77  	Arg   interface{} `json:"args,omitempty"`
    78  }
    79  
    80  type nameArg struct {
    81  	Name string `json:"name"`
    82  }
    83  
    84  type nopCloser struct{ io.Writer }
    85  
    86  func (nopCloser) Close() error { return nil }
    87  
    88  // New creates a new Tracer, storing log in order to log errors later.
    89  // Events are buffered in memory until SetOutput is called.
    90  func New(log logger.Logger) *tracerImpl {
    91  	ret := &tracerImpl{
    92  		log: log,
    93  
    94  		firstEvent: true,
    95  		nextTid:    uint64(MaxInitThreads),
    96  	}
    97  	ret.startBuffer()
    98  
    99  	return ret
   100  }
   101  
   102  func (t *tracerImpl) startBuffer() {
   103  	t.w = nopCloser{&t.buf}
   104  	fmt.Fprintln(t.w, "[")
   105  
   106  	t.defineThread(MainThread, "main")
   107  }
   108  
   109  func (t *tracerImpl) close() {
   110  	if t.file != nil {
   111  		fmt.Fprintln(t.w, "]")
   112  
   113  		if err := t.w.Close(); err != nil {
   114  			t.log.Println("Error closing trace writer:", err)
   115  		}
   116  
   117  		if err := t.file.Close(); err != nil {
   118  			t.log.Println("Error closing trace file:", err)
   119  		}
   120  		t.file = nil
   121  		t.startBuffer()
   122  	}
   123  }
   124  
   125  // SetOutput creates the output file (rotating old files).
   126  func (t *tracerImpl) SetOutput(filename string) {
   127  	t.lock.Lock()
   128  	defer t.lock.Unlock()
   129  
   130  	t.close()
   131  
   132  	// chrome://tracing requires that compressed trace files end in .gz
   133  	if !strings.HasSuffix(filename, ".gz") {
   134  		filename += ".gz"
   135  	}
   136  
   137  	f, err := logger.CreateFileWithRotation(filename, 5)
   138  	if err != nil {
   139  		t.log.Println("Failed to create trace file:", err)
   140  		return
   141  	}
   142  	// Save the file, since closing the gzip Writer doesn't close the
   143  	// underlying file.
   144  	t.file = f
   145  	t.w = gzip.NewWriter(f)
   146  
   147  	// Write out everything that happened since the start
   148  	if _, err := io.Copy(t.w, &t.buf); err != nil {
   149  		t.log.Println("Failed to write trace buffer to file:", err)
   150  	}
   151  	t.buf = bytes.Buffer{}
   152  }
   153  
   154  // Close closes the output file. Any future events will be buffered until the
   155  // next call to SetOutput.
   156  func (t *tracerImpl) Close() {
   157  	t.lock.Lock()
   158  	defer t.lock.Unlock()
   159  
   160  	t.close()
   161  }
   162  
   163  func (t *tracerImpl) writeEvent(event *viewerEvent) {
   164  	t.lock.Lock()
   165  	defer t.lock.Unlock()
   166  
   167  	t.writeEventLocked(event)
   168  }
   169  
   170  func (t *tracerImpl) writeEventLocked(event *viewerEvent) {
   171  	bytes, err := json.Marshal(event)
   172  	if err != nil {
   173  		t.log.Println("Failed to marshal event:", err)
   174  		t.log.Verbosef("Event: %#v", event)
   175  		return
   176  	}
   177  
   178  	if !t.firstEvent {
   179  		fmt.Fprintln(t.w, ",")
   180  	} else {
   181  		t.firstEvent = false
   182  	}
   183  
   184  	if _, err = t.w.Write(bytes); err != nil {
   185  		t.log.Println("Trace write error:", err)
   186  	}
   187  }
   188  
   189  func (t *tracerImpl) defineThread(thread Thread, name string) {
   190  	t.writeEventLocked(&viewerEvent{
   191  		Name:  "thread_name",
   192  		Phase: "M",
   193  		Pid:   0,
   194  		Tid:   uint64(thread),
   195  		Arg: &nameArg{
   196  			Name: name,
   197  		},
   198  	})
   199  }
   200  
   201  // NewThread returns a new Thread with an unused tid, writing the name out to
   202  // the trace file.
   203  func (t *tracerImpl) NewThread(name string) Thread {
   204  	t.lock.Lock()
   205  	defer t.lock.Unlock()
   206  
   207  	ret := Thread(t.nextTid)
   208  	t.nextTid += 1
   209  
   210  	t.defineThread(ret, name)
   211  	return ret
   212  }
   213  
   214  // Begin starts a new Duration Event. More than one Duration Event may be active
   215  // at a time on each Thread, but they're nested.
   216  func (t *tracerImpl) Begin(name string, thread Thread) {
   217  	t.writeEvent(&viewerEvent{
   218  		Name:  name,
   219  		Phase: "B",
   220  		Time:  uint64(time.Now().UnixNano()) / 1000,
   221  		Pid:   0,
   222  		Tid:   uint64(thread),
   223  	})
   224  }
   225  
   226  // End finishes the most recent active Duration Event on the thread.
   227  func (t *tracerImpl) End(thread Thread) {
   228  	t.writeEvent(&viewerEvent{
   229  		Phase: "E",
   230  		Time:  uint64(time.Now().UnixNano()) / 1000,
   231  		Pid:   0,
   232  		Tid:   uint64(thread),
   233  	})
   234  }
   235  
   236  // Complete writes a Complete Event, which are like Duration Events, but include
   237  // a begin and end timestamp in the same event.
   238  func (t *tracerImpl) Complete(name string, thread Thread, begin, end uint64) {
   239  	t.writeEvent(&viewerEvent{
   240  		Name:  name,
   241  		Phase: "X",
   242  		Time:  begin / 1000,
   243  		Dur:   (end - begin) / 1000,
   244  		Pid:   0,
   245  		Tid:   uint64(thread),
   246  	})
   247  }