blob: d87d6fb6339416b0ed32c2fe8d92ee40876a3f16 [file] [log] [blame]
// Copyright (c) 2013, 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.
part of app;
/// The observatory application. Instances of this are created and owned
/// by the observatory_application custom element.
class ObservatoryApplication extends Observable {
static ObservatoryApplication app;
final _pageRegistry = new List<Page>();
@observable Page currentPage;
@observable final LocationManager locationManager;
VM _vm;
VM get vm => _vm;
set vm(VM vm) {
if (_vm == vm) {
// Do nothing.
return;
}
if (_vm != null) {
// Disconnect from current VM.
notifications.clear();
_vm.disconnect();
}
if (vm != null) {
Logger.root.info('Registering new VM callbacks');
vm.onConnect.then(_vmConnected);
vm.onDisconnect.then(_vmDisconnected);
vm.errors.stream.listen(_onError);
vm.events.stream.listen(_onEvent);
vm.exceptions.stream.listen(_onException);
}
_vm = vm;
}
final TargetManager targets;
@reflectable final ObservatoryApplicationElement rootElement;
TraceViewElement _traceView = null;
@reflectable ServiceObject lastErrorOrException;
@observable ObservableList<ServiceEvent> notifications =
new ObservableList<ServiceEvent>();
void _initOnce(bool chromium) {
assert(app == null);
app = this;
_registerPages();
Analytics.initialize();
locationManager._init(this);
}
void removePauseEvents(Isolate isolate) {
bool isPauseEvent(var event) {
return (event.eventType == ServiceEvent.kPauseStart ||
event.eventType == ServiceEvent.kPauseExit ||
event.eventType == ServiceEvent.kPauseBreakpoint ||
event.eventType == ServiceEvent.kPauseInterrupted ||
event.eventType == ServiceEvent.kPauseException);
}
notifications.removeWhere((oldEvent) {
return (oldEvent.isolate == isolate &&
isPauseEvent(oldEvent));
});
}
void _onEvent(ServiceEvent event) {
switch(event.eventType) {
case ServiceEvent.kIsolateStart:
case ServiceEvent.kIsolateUpdate:
case ServiceEvent.kGraph:
case ServiceEvent.kBreakpointAdded:
case ServiceEvent.kBreakpointResolved:
case ServiceEvent.kBreakpointRemoved:
case ServiceEvent.kGC:
// Ignore for now.
break;
case ServiceEvent.kIsolateExit:
case ServiceEvent.kResume:
removePauseEvents(event.isolate);
break;
case ServiceEvent.kPauseStart:
case ServiceEvent.kPauseExit:
case ServiceEvent.kPauseBreakpoint:
case ServiceEvent.kPauseInterrupted:
case ServiceEvent.kPauseException:
removePauseEvents(event.isolate);
notifications.add(event);
break;
default:
// Ignore unrecognized events.
Logger.root.severe('Unrecognized event: $event');
break;
}
}
void _registerPages() {
_pageRegistry.add(new VMPage(this));
_pageRegistry.add(new FlagsPage(this));
_pageRegistry.add(new InspectPage(this));
_pageRegistry.add(new ClassTreePage(this));
_pageRegistry.add(new DebuggerPage(this));
_pageRegistry.add(new CpuProfilerPage(this));
_pageRegistry.add(new AllocationProfilerPage(this));
_pageRegistry.add(new HeapMapPage(this));
_pageRegistry.add(new VMConnectPage(this));
_pageRegistry.add(new ErrorViewPage(this));
_pageRegistry.add(new MetricsPage(this));
// Note that ErrorPage must be the last entry in the list as it is
// the catch all.
_pageRegistry.add(new ErrorPage(this));
}
void _onError(ServiceError error) {
lastErrorOrException = error;
_visit('error/', null);
}
void _onException(ServiceException exception) {
lastErrorOrException = exception;
if (exception.kind == 'NetworkException') {
// Got a network exception, visit the vm-connect page.
this.vm = null;
locationManager.go(locationManager.makeLink('/vm-connect/'));
} else {
_visit('error/', null);
}
}
void _visit(String url, String args) {
var argsMap;
if (args == null) {
argsMap = {};
} else {
argsMap = Uri.splitQueryString(args);
}
if (argsMap['trace'] != null) {
var traceArg = argsMap['trace'];
if (traceArg == 'on') {
Tracer.start();
} else if (traceArg == 'off') {
Tracer.stop();
}
}
if (Tracer.current != null) {
Tracer.current.reset();
}
if (_traceView != null) {
_traceView.tracer = Tracer.current;
}
Uri uri = Uri.parse(url);
for (var i = 0; i < _pageRegistry.length; i++) {
var page = _pageRegistry[i];
if (page.canVisit(uri)) {
_installPage(page);
page.visit(uri, argsMap);
return;
}
}
throw new FallThroughError();
}
/// Set the Observatory application page.
void _installPage(Page page) {
assert(page != null);
if (currentPage == page) {
// Already isntalled.
return;
}
if (currentPage != null) {
Logger.root.info('Uninstalling page: $currentPage');
currentPage.onUninstall();
// Clear children.
rootElement.children.clear();
}
Logger.root.info('Installing page: $page');
try {
page.onInstall();
} catch (e) {
Logger.root.severe('Failed to install page: $e');
}
// Add new page.
rootElement.children.add(page.element);
// Add tracing support.
_traceView = new Element.tag('trace-view');
_traceView.tracer = Tracer.current;
rootElement.children.add(_traceView);
// Remember page.
currentPage = page;
}
ObservatoryApplication(this.rootElement) :
locationManager = new HashLocationManager(),
targets = new TargetManager() {
vm = new WebSocketVM(targets.defaultTarget);
_initOnce(false);
}
void _removeDisconnectEvents() {
notifications.removeWhere((oldEvent) {
return (oldEvent.eventType == ServiceEvent.kVMDisconnected);
});
}
_vmConnected(VM vm) {
if (vm is WebSocketVM) {
targets.add(vm.target);
}
_removeDisconnectEvents();
}
_vmDisconnected(VM vm) {
if (this.vm != vm) {
// This disconnect event occured *after* a new VM was installed.
return;
}
this.vm = null;
notifications.add(new ServiceEvent.vmDisconencted());
}
}