blob: 470638b23ff280a44e946672942a1b81a61249ab [file] [log] [blame]
// 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.
/// Instrument your code with counters, gauges, and more.
library dart.profiler;
import 'dart:convert';
/// A UserTag can be used to group samples in the Observatory profiler.
abstract class UserTag {
/// The maximum number of UserTag instances that can be created by a program.
static const MAX_USER_TAGS = 64;
factory UserTag(String label) => new _FakeUserTag(label);
/// Label of [this].
String get label;
/// Make [this] the current tag for the isolate. Returns the current tag
/// before setting.
UserTag makeCurrent();
/// The default [UserTag] with label 'Default'.
static UserTag get defaultTag => _FakeUserTag._defaultTag;
}
// This is a fake implementation of UserTag so that code can compile and run
// in dart2js.
class _FakeUserTag implements UserTag {
static Map _instances = {};
_FakeUserTag.real(this.label);
factory _FakeUserTag(String label) {
// Canonicalize by name.
var existingTag = _instances[label];
if (existingTag != null) {
return existingTag;
}
// Throw an exception if we've reached the maximum number of user tags.
if (_instances.length == UserTag.MAX_USER_TAGS) {
throw new UnsupportedError(
'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
}
// Create a new instance and add it to the instance map.
var instance = new _FakeUserTag.real(label);
_instances[label] = instance;
return instance;
}
final String label;
UserTag makeCurrent() {
var old = _currentTag;
_currentTag = this;
return old;
}
static final UserTag _defaultTag = new _FakeUserTag('Default');
}
var _currentTag = _FakeUserTag._defaultTag;
/// Returns the current [UserTag] for the isolate.
UserTag getCurrentTag() {
return _currentTag;
}
/// Abstract [Metric] class. Metric names must be unique, are hierarchical,
/// and use periods as separators. For example, 'a.b.c'. Uniqueness is only
/// enforced when a Metric is registered. The name of a metric cannot contain
/// the slash ('/') character.
abstract class Metric {
/// [name] of this metric.
final String name;
/// [description] of this metric.
final String description;
Metric(this.name, this.description) {
if ((name == 'vm') || name.contains('/')) {
throw new ArgumentError('Invalid Metric name.');
}
}
Map _toJSON();
}
/// A measured value with a min and max. Initial value is min. Value will
/// be clamped to the interval [min, max].
class Gauge extends Metric {
final double min;
final double max;
double _value;
double get value => _value;
set value(double v) {
if (v < min) {
v = min;
} else if (v > max) {
v = max;
}
_value = v;
}
Gauge(String name, String description, this.min, this.max)
: super(name, description) {
if (min is! double) {
throw new ArgumentError('min must be a double');
}
if (max is! double) {
throw new ArgumentError('max must be a double');
}
if (!(min < max)) {
throw new ArgumentError('min must be less than max');
}
_value = min;
}
Map _toJSON() {
var map = {
'type': 'Gauge',
'id': 'metrics/$name',
'name': name,
'description': description,
'value': value,
'min': min,
'max': max,
};
return map;
}
}
/// A changing value. Initial value is 0.0.
class Counter extends Metric {
Counter(String name, String description)
: super(name, description);
double _value = 0.0;
double get value => _value;
set value(double v) {
_value = v;
}
Map _toJSON() {
var map = {
'type': 'Counter',
'id': 'metrics/$name',
'name': name,
'description': description,
'value': value,
};
return map;
}
}
class Metrics {
static final Map<String, Metric> _metrics = new Map<String, Metric>();
/// Register [Metric]s to make them visible to Observatory.
static void register(Metric metric) {
if (metric is! Metric) {
throw new ArgumentError('metric must be a Metric');
}
if (_metrics[metric.name] != null) {
throw new ArgumentError('Registered metrics have unique names');
}
_metrics[metric.name] = metric;
}
/// Deregister [Metric]s to make them not visible to Observatory.
static void deregister(Metric metric) {
if (metric is! Metric) {
throw new ArgumentError('metric must be a Metric');
}
_metrics.remove(metric.name);
}
static String _printMetric(String id) {
var metric = _metrics[id];
if (metric == null) {
return null;
}
return JSON.encode(metric._toJSON());
}
static String _printMetrics() {
var metrics = [];
for (var metric in _metrics.values) {
metrics.add(metric._toJSON());
}
var map = {
'type': 'MetricList',
'metrics': metrics,
};
return JSON.encode(map);
}
}