| // 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); |
| } |
| } |
| } |
| } |