blob: a094d393c064bea02e3909e2b02b74d62b787de2 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library tracing;
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'dart:io';
import 'dart:isolate';
import 'package:common/src/trace_provider_impl.dart';
export 'package:common/src/trace_provider_impl.dart' show TraceSendTiming;
import 'package:mojo/application.dart';
import 'package:mojo/core.dart';
import 'package:mojo_services/tracing/tracing.mojom.dart';
// Phases indicating the nature of the event in the trace log.
// These should be in sync with definitions in
// //base/trace_event/trace_event.h
const traceEventInstant = "I";
const traceEventPhaseBegin = "B";
const traceEventPhaseEnd = "E";
const traceEventPhaseAsyncBegin = "S";
const traceEventPhaseAsyncEnd = "F";
const traceEventDuration = "X";
// TracingHelper is used by Dart code running in the Mojo shell in order
// to perform tracing.
class TracingHelper {
TraceProviderImpl _impl;
int _tid;
static TracingHelper _tracing;
// Construct an instance of TracingHelper from within your application's
// |initialize()| method. |appName| will be used to form a thread identifier
// for use in trace messages. If |appName| is longer than 20 characters then
// only the last 20 characters of |appName| will be used.
TracingHelper.fromApplication(Application app, String appName,
[TraceSendTiming timing = TraceSendTiming.IMMEDIATE]) {
// Masked because the tid is expected to be a 32-bit int.
_tid = [appName, Isolate.current]
.fold(7, (hash, element) => 31 * hash + element.hashCode) &
0x7fffffff;
_impl = new TraceProviderImpl(timing);
ApplicationConnection connection = app.connectToApplication("mojo:tracing");
connection.provideService(TraceProviderName, (e) {
_impl.connect(e);
});
assert(_tracing == null);
_tracing = this;
}
// Factory to return the singleton instance of the TracingHelper. The isolate
// must have constructed the object using TracingHelper.fromApplication
// atleast once before using this factory.
factory TracingHelper() {
assert(_tracing != null);
return _tracing;
}
bool isActive() {
return (_impl != null) && _impl.isActive();
}
// Invoke this at the beginning of a synchronous function whose
// duration you wish to trace. Invoke |end()| on the returned object.
FunctionTrace begin(String functionName, String categories,
{Map<String, String> args}) {
return _beginFunction(functionName, categories, traceEventPhaseBegin,
args: args);
}
// Invoke this right before an asynchronous function whose duration
// you wish to trace. Invoke |end()| on the returned object.
FunctionTrace beginAsync(String functionName, String categories,
{Map<String, String> args}) {
return _beginFunction(functionName, categories, traceEventPhaseAsyncBegin,
args: args);
}
void traceInstant(String name, String categories,
{Map<String, String> args}) {
_sendTraceMessage(name, categories, traceEventInstant, 0, args: args);
}
void traceDuration(String name, String categories, int start, int end,
{Map<String, String> args}) {
_sendTraceMessage(name, categories, traceEventDuration, 0,
args: args, start: start, duration: end - start);
}
FunctionTrace _beginFunction(
String functionName, String categories, String phase,
{Map<String, String> args}) {
assert(functionName != null);
final trace = new _FunctionTraceImpl(
this, isActive() ? functionName : null, categories, phase);
_sendTraceMessage(functionName, categories, phase, trace.hashCode,
args: args);
return trace;
}
void _endFunction(
String functionName, String categories, String phase, int traceId) {
_sendTraceMessage(functionName, categories, phase, traceId);
}
void _sendTraceMessage(
String name, String categories, String phase, int traceId,
{Map<String, String> args, int start, int duration}) {
if (isActive()) {
var time = (start != null) ? start : getTimeTicksNow();
var map = {};
map["name"] = name;
map["cat"] = categories;
map["ph"] = phase;
map["ts"] = time;
map["pid"] = pid;
map["tid"] = _tid;
map["id"] = traceId;
if (duration != null) {
map["dur"] = duration;
}
if (args != null) {
map["args"] = args;
}
_impl.sendTraceMessage(JSON.encode(map));
}
}
// A convenience method that wraps a closure in a begin-end pair of
// tracing calls.
dynamic trace(String functionName, String categories, closure(),
{Map<String, String> args}) {
FunctionTrace ft = begin(functionName, categories, args: args);
final returnValue = closure();
ft.end();
return returnValue;
}
// A convenience method that wraps a closure in a begin-end pair of
// async tracing calls. The return value should either be returned or awaited.
Future traceAsync(String functionName, String categories, Future closure(),
{Map<String, String> args}) {
FunctionTrace ft = beginAsync(functionName, categories, args: args);
final Future returnValue = closure();
returnValue.whenComplete(ft.end);
return returnValue;
}
}
// An instance of FunctionTrace is returned from |begin()|, |beginAsync()|.
// Invoke |end()| to end the trace from every exit point in the function you are
// tracing.
abstract class FunctionTrace {
void end();
}
class _FunctionTraceImpl implements FunctionTrace {
TracingHelper _tracing;
String _functionName;
String _categories;
String _beginPhase;
_FunctionTraceImpl(
this._tracing, this._functionName, this._categories, this._beginPhase) {
assert(_beginPhase == traceEventPhaseBegin ||
_beginPhase == traceEventPhaseAsyncBegin);
}
@override
void end() {
if (_functionName != null) {
if (_beginPhase == traceEventPhaseBegin) {
_tracing._endFunction(
_functionName, _categories, traceEventPhaseEnd, hashCode);
} else if (_beginPhase == traceEventPhaseAsyncBegin) {
_tracing._endFunction(
_functionName, _categories, traceEventPhaseAsyncEnd, hashCode);
}
}
}
}