Index: mojo/public/dart/third_party/analyzer/lib/instrumentation/instrumentation.dart |
diff --git a/mojo/public/dart/third_party/analyzer/lib/instrumentation/instrumentation.dart b/mojo/public/dart/third_party/analyzer/lib/instrumentation/instrumentation.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..40db35e549652c429e6dac3b240f6d868c1ce9d9 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/analyzer/lib/instrumentation/instrumentation.dart |
@@ -0,0 +1,374 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library instrumentation; |
+ |
+import 'dart:convert'; |
+ |
+import 'package:analyzer/task/model.dart'; |
+ |
+/** |
+ * A container with analysis performance constants. |
+ */ |
+class AnalysisPerformanceKind { |
+ static const String FULL = 'analysis_full'; |
+ static const String INCREMENTAL = 'analysis_incremental'; |
+} |
+ |
+/** |
+ * The interface used by client code to communicate with an instrumentation |
+ * server. |
+ */ |
+abstract class InstrumentationServer { |
+ /** |
+ * Pass the given [message] to the instrumentation server so that it will be |
+ * logged with other messages. |
+ * |
+ * This method should be used for most logging. |
+ */ |
+ void log(String message); |
+ |
+ /** |
+ * Pass the given [message] to the instrumentation server so that it will be |
+ * logged with other messages. |
+ * |
+ * This method should only be used for logging high priority messages, such as |
+ * exceptions that cause the server to shutdown. |
+ */ |
+ void logWithPriority(String message); |
+ |
+ /** |
+ * Signal that the client is done communicating with the instrumentation |
+ * server. This method should be invoked exactly one time and no other methods |
+ * should be invoked on this instance after this method has been invoked. |
+ */ |
+ void shutdown(); |
+} |
+ |
+/** |
+ * The interface used by client code to communicate with an instrumentation |
+ * server by wrapping an [InstrumentationServer]. |
+ */ |
+class InstrumentationService { |
+ /** |
+ * An instrumentation service that will not log any instrumentation data. |
+ */ |
+ static final InstrumentationService NULL_SERVICE = |
+ new InstrumentationService(null); |
+ |
+ static const String TAG_ANALYSIS_TASK = 'Task'; |
+ static const String TAG_ERROR = 'Err'; |
+ static const String TAG_EXCEPTION = 'Ex'; |
+ static const String TAG_FILE_READ = 'Read'; |
+ static const String TAG_LOG_ENTRY = 'Log'; |
+ static const String TAG_NOTIFICATION = 'Noti'; |
+ static const String TAG_PERFORMANCE = 'Perf'; |
+ static const String TAG_REQUEST = 'Req'; |
+ static const String TAG_RESPONSE = 'Res'; |
+ static const String TAG_SUBPROCESS_START = 'SPStart'; |
+ static const String TAG_SUBPROCESS_RESULT = 'SPResult'; |
+ static const String TAG_VERSION = 'Ver'; |
+ static const String TAG_WATCH_EVENT = 'Watch'; |
+ |
+ /** |
+ * The instrumentation server used to communicate with the server, or `null` |
+ * if instrumentation data should not be logged. |
+ */ |
+ InstrumentationServer _instrumentationServer; |
+ |
+ /** |
+ * Counter used to generate unique ID's for [logSubprocessStart]. |
+ */ |
+ int _subprocessCounter = 0; |
+ |
+ /** |
+ * Initialize a newly created instrumentation service to comunicate with the |
+ * given [instrumentationServer]. |
+ */ |
+ InstrumentationService(this._instrumentationServer); |
+ |
+ /** |
+ * Return `true` if this [InstrumentationService] was initialized with a |
+ * non-`null` server (and hence instrumentation is active). |
+ */ |
+ bool get isActive => _instrumentationServer != null; |
+ |
+ /** |
+ * The current time, expressed as a decimal encoded number of milliseconds. |
+ */ |
+ String get _timestamp => new DateTime.now().millisecondsSinceEpoch.toString(); |
+ |
+ /** |
+ * Log that the given analysis [task] is being performed in the given |
+ * [context]. |
+ */ |
+ void logAnalysisTask(String context, dynamic task) { |
+ // TODO(brianwilkerson) When the old task model is removed, change the |
+ // parameter type to AnalysisTask. |
+ if (_instrumentationServer != null) { |
+ String description = |
+ (task is AnalysisTask) ? task.description : task.toString(); |
+ _instrumentationServer |
+ .log(_join([TAG_ANALYSIS_TASK, context, description])); |
+ } |
+ } |
+ |
+ /** |
+ * Log the fact that an error, described by the given [message], has occurred. |
+ */ |
+ void logError(String message) { |
+ _log(TAG_ERROR, message); |
+ } |
+ |
+ /** |
+ * Log that the given non-priority [exception] was thrown, with the given |
+ * [stackTrace]. |
+ */ |
+ void logException(dynamic exception, StackTrace stackTrace) { |
+ if (_instrumentationServer != null) { |
+ String message = _toString(exception); |
+ String trace = _toString(stackTrace); |
+ _instrumentationServer.log(_join([TAG_EXCEPTION, message, trace])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that the contents of the file with the given [path] were read. The file |
+ * had the given [content] and [modificationTime]. |
+ */ |
+ void logFileRead(String path, int modificationTime, String content) { |
+ if (_instrumentationServer != null) { |
+ String timeStamp = _toString(modificationTime); |
+ _instrumentationServer |
+ .log(_join([TAG_FILE_READ, path, timeStamp, content])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that a log entry that was written to the analysis engine's log. The log |
+ * entry has the given [level] and [message], and was created at the given |
+ * [time]. |
+ */ |
+ void logLogEntry(String level, DateTime time, String message, Object exception, StackTrace stackTrace) { |
+ if (_instrumentationServer != null) { |
+ String timeStamp = |
+ time == null ? 'null' : time.millisecondsSinceEpoch.toString(); |
+ String exceptionText = exception.toString(); |
+ String stackTraceText = stackTrace.toString(); |
+ _instrumentationServer |
+ .log(_join([TAG_LOG_ENTRY, level, timeStamp, message, exceptionText, stackTraceText])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that a notification has been sent to the client. |
+ */ |
+ void logNotification(String notification) { |
+ _log(TAG_NOTIFICATION, notification); |
+ } |
+ |
+ /** |
+ * Log the given performance fact. |
+ */ |
+ void logPerformance(String kind, Stopwatch sw, String message) { |
+ sw.stop(); |
+ String elapsed = sw.elapsedMilliseconds.toString(); |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer |
+ .log(_join([TAG_PERFORMANCE, kind, elapsed, message])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that the given priority [exception] was thrown, with the given |
+ * [stackTrace]. |
+ */ |
+ void logPriorityException(dynamic exception, StackTrace stackTrace) { |
+ if (_instrumentationServer != null) { |
+ String message = _toString(exception); |
+ String trace = _toString(stackTrace); |
+ _instrumentationServer |
+ .logWithPriority(_join([TAG_EXCEPTION, message, trace])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that a request has been sent to the client. |
+ */ |
+ void logRequest(String request) { |
+ _log(TAG_REQUEST, request); |
+ } |
+ |
+ /** |
+ * Log that a response has been sent to the client. |
+ */ |
+ void logResponse(String response) { |
+ _log(TAG_RESPONSE, response); |
+ } |
+ |
+ /** |
+ * Log the result of executing a subprocess. [subprocessId] should be the |
+ * unique IDreturned by [logSubprocessStart]. |
+ */ |
+ void logSubprocessResult( |
+ int subprocessId, int exitCode, String stdout, String stderr) { |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer.log(_join([ |
+ TAG_SUBPROCESS_RESULT, |
+ subprocessId.toString(), |
+ exitCode.toString(), |
+ JSON.encode(stdout), |
+ JSON.encode(stderr) |
+ ])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that the given subprocess is about to be executed. Returns a unique |
+ * identifier that can be used to identify the subprocess for later log |
+ * entries. |
+ */ |
+ int logSubprocessStart( |
+ String executablePath, List<String> arguments, String workingDirectory) { |
+ int subprocessId = _subprocessCounter++; |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer.log(_join([ |
+ TAG_SUBPROCESS_START, |
+ subprocessId.toString(), |
+ executablePath, |
+ workingDirectory, |
+ JSON.encode(arguments) |
+ ])); |
+ } |
+ return subprocessId; |
+ } |
+ |
+ /** |
+ * Signal that the client has started analysis server. |
+ * This method should be invoked exactly one time. |
+ */ |
+ void logVersion(String uuid, String clientId, String clientVersion, |
+ String serverVersion, String sdkVersion) { |
+ String normalize(String value) => |
+ value != null && value.length > 0 ? value : 'unknown'; |
+ |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer.logWithPriority(_join([ |
+ TAG_VERSION, |
+ uuid, |
+ normalize(clientId), |
+ normalize(clientVersion), |
+ serverVersion, |
+ sdkVersion |
+ ])); |
+ } |
+ } |
+ |
+ /** |
+ * Log that the file system watcher sent an event. The [folderPath] is the |
+ * path to the folder containing the changed file, the [filePath] is the path |
+ * of the file that changed, and the [changeType] indicates what kind of |
+ * change occurred. |
+ */ |
+ void logWatchEvent(String folderPath, String filePath, String changeType) { |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer |
+ .log(_join([TAG_WATCH_EVENT, folderPath, filePath, changeType])); |
+ } |
+ } |
+ |
+ /** |
+ * Signal that the client is done communicating with the instrumentation |
+ * server. This method should be invoked exactly one time and no other methods |
+ * should be invoked on this instance after this method has been invoked. |
+ */ |
+ void shutdown() { |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer.shutdown(); |
+ _instrumentationServer = null; |
+ } |
+ } |
+ |
+ /** |
+ * Write an escaped version of the given [field] to the given [buffer]. |
+ */ |
+ void _escape(StringBuffer buffer, String field) { |
+ int index = field.indexOf(':'); |
+ if (index < 0) { |
+ buffer.write(field); |
+ return; |
+ } |
+ int start = 0; |
+ while (index >= 0) { |
+ buffer.write(field.substring(start, index)); |
+ buffer.write('::'); |
+ start = index + 1; |
+ index = field.indexOf(':', start); |
+ } |
+ buffer.write(field.substring(start)); |
+ } |
+ |
+ /** |
+ * Return the result of joining the values of the given fields, escaping the |
+ * separator character by doubling it. |
+ */ |
+ String _join(List<String> fields) { |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write(_timestamp); |
+ for (String field in fields) { |
+ buffer.write(':'); |
+ _escape(buffer, field); |
+ } |
+ return buffer.toString(); |
+ } |
+ |
+ /** |
+ * Log the given message with the given tag. |
+ */ |
+ void _log(String tag, String message) { |
+ if (_instrumentationServer != null) { |
+ _instrumentationServer.log(_join([tag, message])); |
+ } |
+ } |
+ |
+ /** |
+ * Convert the given [object] to a string. |
+ */ |
+ String _toString(Object object) { |
+ if (object == null) { |
+ return 'null'; |
+ } |
+ return object.toString(); |
+ } |
+} |
+ |
+/** |
+ * An [InstrumentationServer] that sends messages to multiple instances. |
+ */ |
+class MulticastInstrumentationServer implements InstrumentationServer { |
+ final List<InstrumentationServer> _servers; |
+ |
+ MulticastInstrumentationServer(this._servers); |
+ |
+ @override |
+ void log(String message) { |
+ for (InstrumentationServer server in _servers) { |
+ server.log(message); |
+ } |
+ } |
+ |
+ @override |
+ void logWithPriority(String message) { |
+ for (InstrumentationServer server in _servers) { |
+ server.logWithPriority(message); |
+ } |
+ } |
+ |
+ @override |
+ void shutdown() { |
+ for (InstrumentationServer server in _servers) { |
+ server.shutdown(); |
+ } |
+ } |
+} |