| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of app; | 5 part of app; |
| 6 | 6 |
| 7 /// A [Page] controls the user interface of Observatory. At any given time | 7 /// A [Page] controls the user interface of Observatory. At any given time |
| 8 /// one page will be the current page. Pages are registered at startup. | 8 /// one page will be the current page. Pages are registered at startup. |
| 9 /// When the user navigates within the application, each page is asked if it | 9 /// When the user navigates within the application, each page is asked if it |
| 10 /// can handle the current location, the first page to say yes, wins. | 10 /// can handle the current location, the first page to say yes, wins. |
| 11 abstract class Page extends Observable { | 11 abstract class Page extends Observable { |
| 12 final ObservatoryApplication app; | 12 final ObservatoryApplication app; |
| 13 | 13 |
| 14 @observable ObservatoryElement element; | 14 @observable ObservatoryElement element; |
| 15 @observable ObservableMap args; | 15 @observable ObservableMap args; |
| 16 | 16 |
| 17 Page(this.app); | 17 Page(this.app); |
| 18 | 18 |
| 19 /// Called when the page is installed, this callback must initialize | 19 /// Called when the page is installed, this callback must initialize |
| 20 /// [element]. | 20 /// [element]. |
| 21 void onInstall(); | 21 void onInstall(); |
| 22 | 22 |
| 23 /// Called when the page is uninstalled, this callback must clear | 23 /// Called when the page is uninstalled, this callback must clear |
| 24 /// [element]. | 24 /// [element]. |
| 25 void onUninstall() { | 25 void onUninstall() { |
| 26 element = null; | 26 element = null; |
| 27 } | 27 } |
| 28 | 28 |
| 29 /// Called when the page should update its state based on [url]. | 29 /// Called when the page should update its state based on [uri]. |
| 30 /// NOTE: Only called when the page is installed. | 30 /// NOTE: Only called when the page is installed. |
| 31 void visit(String url, Map argsMap) { | 31 void visit(Uri uri, Map argsMap) { |
| 32 args = toObservable(argsMap); | 32 args = toObservable(argsMap); |
| 33 _visit(url); | 33 _visit(uri); |
| 34 } | 34 } |
| 35 | 35 |
| 36 // Overridden by subclasses. | 36 // Overridden by subclasses. |
| 37 void _visit(String url); | 37 void _visit(Uri uri); |
| 38 | 38 |
| 39 /// Called to test whether this page can visit [url]. | 39 /// Called to test whether this page can visit [uri]. |
| 40 bool canVisit(String url); | 40 bool canVisit(Uri uri); |
| 41 } | 41 } |
| 42 | 42 |
| 43 /// A general service object viewer. | 43 /// A [SimplePage] matches a single uri path and displays a single element. |
| 44 class ServiceObjectPage extends Page { | 44 class SimplePage extends Page { |
| 45 ServiceObjectPage(app) : super(app); | 45 final String path; |
| 46 | |
| 47 void onInstall() { | |
| 48 if (element == null) { | |
| 49 /// Lazily create page. | |
| 50 element = new Element.tag('service-view'); | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 void _visit(String url) { | |
| 55 assert(element != null); | |
| 56 assert(canVisit(url)); | |
| 57 if (url == '') { | |
| 58 // Nothing requested. | |
| 59 return; | |
| 60 } | |
| 61 /// Request url from VM and display it. | |
| 62 app.vm.getDeprecated(url).then((obj) { | |
| 63 ServiceObjectViewElement serviceElement = element; | |
| 64 serviceElement.object = obj; | |
| 65 }).catchError((e) { | |
| 66 Logger.root.severe('ServiceObjectPage visit error: $e'); | |
| 67 }); | |
| 68 } | |
| 69 | |
| 70 /// Catch all. | |
| 71 bool canVisit(String url) => true; | |
| 72 } | |
| 73 | |
| 74 class IsolateSuffixPage extends Page { | |
| 75 final String pagePrefix; | |
| 76 final String elementTagName; | 46 final String elementTagName; |
| 77 String _isolateId; | 47 SimplePage(this.path, this.elementTagName, app) : super(app); |
| 78 String get isolateId => _isolateId; | |
| 79 IsolateSuffixPage(this.pagePrefix, this.elementTagName, app) : super(app); | |
| 80 | 48 |
| 81 void onInstall() { | 49 void onInstall() { |
| 82 if (element == null) { | 50 if (element == null) { |
| 83 element = new Element.tag(elementTagName); | 51 element = new Element.tag(elementTagName); |
| 84 } | 52 } |
| 85 } | 53 } |
| 86 | 54 |
| 87 void _visit(String url) { | 55 void _visit(Uri uri) { |
| 88 assert(url != null); | 56 assert(uri != null); |
| 89 assert(canVisit(url)); | 57 assert(canVisit(uri)); |
| 90 _isolateId = url.substring(pagePrefix.length); | |
| 91 } | 58 } |
| 92 | 59 |
| 93 Future<Isolate> getIsolate() { | 60 Future<Isolate> getIsolate(Uri uri) { |
| 94 return app.vm.getIsolate(isolateId).catchError((e) { | 61 return app.vm.getIsolate(uri.queryParameters['isolateId']).catchError((e) { |
| 95 Logger.root.severe('$pagePrefix visit error: $e'); | 62 Logger.root.severe('$path visit error: $e'); |
| 96 return e; | 63 return e; |
| 97 }); | 64 }); |
| 98 } | 65 } |
| 99 | 66 |
| 100 bool canVisit(String url) => url.startsWith(pagePrefix); | 67 bool canVisit(Uri uri) => uri.path == path; |
| 68 } |
| 69 |
| 70 /// Error page for unrecognized paths. |
| 71 class ErrorPage extends Page { |
| 72 ErrorPage(app) : super(app); |
| 73 |
| 74 void onInstall() { |
| 75 if (element == null) { |
| 76 // Lazily create page. |
| 77 element = new Element.tag('general-error'); |
| 78 } |
| 79 } |
| 80 |
| 81 void _visit(Uri uri) { |
| 82 assert(element != null); |
| 83 assert(canVisit(uri)); |
| 84 |
| 85 /* |
| 86 if (uri.path == '') { |
| 87 // Nothing requested. |
| 88 return; |
| 89 } |
| 90 */ |
| 91 |
| 92 if (element != null) { |
| 93 GeneralErrorElement serviceElement = element; |
| 94 serviceElement.message = "Path '${uri.path}' not found"; |
| 95 } |
| 96 } |
| 97 |
| 98 /// Catch all. |
| 99 bool canVisit(Uri uri) => true; |
| 100 } |
| 101 |
| 102 /// Top-level vm info page. |
| 103 class VMPage extends SimplePage { |
| 104 VMPage(app) : super('vm', 'service-view', app); |
| 105 |
| 106 void _visit(Uri uri) { |
| 107 super._visit(uri); |
| 108 app.vm.reload().then((vm) { |
| 109 if (element != null) { |
| 110 ServiceObjectViewElement serviceElement = element; |
| 111 serviceElement.object = vm; |
| 112 } |
| 113 }).catchError((e) { |
| 114 Logger.root.severe('VMPage visit error: $e'); |
| 115 }); |
| 116 } |
| 117 } |
| 118 |
| 119 class FlagsPage extends SimplePage { |
| 120 FlagsPage(app) : super('flags', 'flag-list', app); |
| 121 |
| 122 void _visit(Uri uri) { |
| 123 super._visit(uri); |
| 124 app.vm.getFlagList().then((flags) { |
| 125 if (element != null) { |
| 126 FlagListElement serviceElement = element; |
| 127 serviceElement.flagList = flags; |
| 128 } |
| 129 }).catchError((e) { |
| 130 Logger.root.severe('FlagsPage visit error: $e'); |
| 131 }); |
| 132 } |
| 133 } |
| 134 |
| 135 class InspectPage extends SimplePage { |
| 136 InspectPage(app) : super('inspect', 'service-view', app); |
| 137 |
| 138 void _visit(Uri uri) { |
| 139 super._visit(uri); |
| 140 getIsolate(uri).then((isolate) { |
| 141 var objectId = uri.queryParameters['objectId']; |
| 142 if (objectId == null) { |
| 143 isolate.reload().then(_visitObject); |
| 144 } else { |
| 145 isolate.getObject(objectId).then(_visitObject); |
| 146 } |
| 147 }); |
| 148 } |
| 149 |
| 150 void _visitObject(obj) { |
| 151 if (element != null) { |
| 152 ServiceObjectViewElement serviceElement = element; |
| 153 serviceElement.object = obj; |
| 154 } |
| 155 } |
| 101 } | 156 } |
| 102 | 157 |
| 103 | 158 |
| 104 /// Class tree page. | 159 /// Class tree page. |
| 105 class ClassTreePage extends IsolateSuffixPage { | 160 class ClassTreePage extends SimplePage { |
| 106 ClassTreePage(app) : super('class-tree/', 'class-tree', app); | 161 ClassTreePage(app) : super('class-tree', 'class-tree', app); |
| 107 | 162 |
| 108 void _visit(String url) { | 163 void _visit(Uri uri) { |
| 109 super._visit(url); | 164 super._visit(uri); |
| 110 getIsolate().then((isolate) { | 165 getIsolate(uri).then((isolate) { |
| 111 if (element != null) { | 166 if (element != null) { |
| 112 /// Update the page. | 167 /// Update the page. |
| 113 ClassTreeElement page = element; | 168 ClassTreeElement page = element; |
| 114 page.isolate = isolate; | 169 page.isolate = isolate; |
| 115 } | 170 } |
| 116 }); | 171 }); |
| 117 } | 172 } |
| 118 } | 173 } |
| 119 | 174 |
| 120 class DebuggerPage extends IsolateSuffixPage { | 175 class DebuggerPage extends SimplePage { |
| 121 DebuggerPage(app) : super('debugger/', 'debugger-page', app); | 176 DebuggerPage(app) : super('debugger', 'debugger-page', app); |
| 122 | 177 |
| 123 void _visit(String url) { | 178 void _visit(Uri uri) { |
| 124 super._visit(url); | 179 super._visit(uri); |
| 125 getIsolate().then((isolate) { | 180 getIsolate(uri).then((isolate) { |
| 126 if (element != null) { | 181 if (element != null) { |
| 127 /// Update the page. | 182 /// Update the page. |
| 128 DebuggerPageElement page = element; | 183 DebuggerPageElement page = element; |
| 129 page.isolate = isolate; | 184 page.isolate = isolate; |
| 130 } | 185 } |
| 131 }); | 186 }); |
| 132 } | 187 } |
| 133 } | 188 } |
| 134 | 189 |
| 135 class CpuProfilerPage extends IsolateSuffixPage { | 190 class CpuProfilerPage extends SimplePage { |
| 136 CpuProfilerPage(app) : super('profiler/', 'cpu-profile', app); | 191 CpuProfilerPage(app) : super('profiler', 'cpu-profile', app); |
| 137 | 192 |
| 138 void _visit(String url) { | 193 void _visit(Uri uri) { |
| 139 super._visit(url); | 194 super._visit(uri); |
| 140 getIsolate().then((isolate) { | 195 getIsolate(uri).then((isolate) { |
| 141 if (element != null) { | 196 if (element != null) { |
| 142 /// Update the page. | 197 /// Update the page. |
| 143 CpuProfileElement page = element; | 198 CpuProfileElement page = element; |
| 144 page.isolate = isolate; | 199 page.isolate = isolate; |
| 145 } | 200 } |
| 146 }); | 201 }); |
| 147 } | 202 } |
| 148 } | 203 } |
| 149 | 204 |
| 150 class AllocationProfilerPage extends IsolateSuffixPage { | 205 class AllocationProfilerPage extends SimplePage { |
| 151 AllocationProfilerPage(app) | 206 AllocationProfilerPage(app) |
| 152 : super('allocation-profiler/', 'heap-profile', app); | 207 : super('allocation-profiler', 'heap-profile', app); |
| 153 | 208 |
| 154 void _visit(String url) { | 209 void _visit(Uri uri) { |
| 155 super._visit(url); | 210 super._visit(uri); |
| 156 getIsolate().then((isolate) { | 211 getIsolate(uri).then((isolate) { |
| 157 if (element != null) { | 212 if (element != null) { |
| 158 /// Update the page. | 213 /// Update the page. |
| 159 HeapProfileElement page = element; | 214 HeapProfileElement page = element; |
| 160 page.isolate = isolate; | 215 page.isolate = isolate; |
| 161 } | 216 } |
| 162 }); | 217 }); |
| 163 } | 218 } |
| 164 } | 219 } |
| 165 | 220 |
| 166 class HeapMapPage extends IsolateSuffixPage { | 221 class HeapMapPage extends SimplePage { |
| 167 HeapMapPage(app) : super('heap-map/', 'heap-map', app); | 222 HeapMapPage(app) : super('heap-map', 'heap-map', app); |
| 168 | 223 |
| 169 void _visit(String url) { | 224 void _visit(Uri uri) { |
| 170 super._visit(url); | 225 super._visit(uri); |
| 171 getIsolate().then((isolate) { | 226 getIsolate(uri).then((isolate) { |
| 172 if (element != null) { | 227 if (element != null) { |
| 173 /// Update the page. | 228 /// Update the page. |
| 174 HeapMapElement page = element; | 229 HeapMapElement page = element; |
| 175 page.isolate = isolate; | 230 page.isolate = isolate; |
| 176 } | 231 } |
| 177 }); | 232 }); |
| 178 } | 233 } |
| 179 } | 234 } |
| 180 | 235 |
| 181 class ErrorViewPage extends Page { | 236 class ErrorViewPage extends Page { |
| 182 ErrorViewPage(app) : super(app); | 237 ErrorViewPage(app) : super(app); |
| 183 | 238 |
| 184 void onInstall() { | 239 void onInstall() { |
| 185 if (element == null) { | 240 if (element == null) { |
| 186 /// Lazily create page. | 241 /// Lazily create page. |
| 187 element = new Element.tag('service-view'); | 242 element = new Element.tag('service-view'); |
| 188 } | 243 } |
| 189 } | 244 } |
| 190 | 245 |
| 191 void _visit(String url) { | 246 void _visit(Uri uri) { |
| 192 assert(element != null); | 247 assert(element != null); |
| 193 assert(canVisit(url)); | 248 assert(canVisit(uri)); |
| 194 (element as ServiceObjectViewElement).object = app.lastErrorOrException; | 249 (element as ServiceObjectViewElement).object = app.lastErrorOrException; |
| 195 } | 250 } |
| 196 | 251 |
| 197 bool canVisit(String url) => url.startsWith('error/'); | 252 // TODO(turnidge): How to test this page? |
| 253 bool canVisit(Uri uri) => uri.path.startsWith('error/'); |
| 198 } | 254 } |
| 199 | 255 |
| 200 class VMConnectPage extends Page { | 256 class VMConnectPage extends Page { |
| 201 VMConnectPage(app) : super(app); | 257 VMConnectPage(app) : super(app); |
| 202 | 258 |
| 203 void onInstall() { | 259 void onInstall() { |
| 204 if (element == null) { | 260 if (element == null) { |
| 205 element = new Element.tag('vm-connect'); | 261 element = new Element.tag('vm-connect'); |
| 206 } | 262 } |
| 207 assert(element != null); | 263 assert(element != null); |
| 208 } | 264 } |
| 209 | 265 |
| 210 void _visit(String url) { | 266 void _visit(Uri uri) { |
| 211 assert(element != null); | 267 assert(element != null); |
| 212 assert(canVisit(url)); | 268 assert(canVisit(uri)); |
| 213 } | 269 } |
| 214 | 270 |
| 215 bool canVisit(String url) => url.startsWith('vm-connect/'); | 271 // TODO(turnidge): Update this to not have the trailing slash. |
| 272 bool canVisit(Uri uri) => uri.path.startsWith('vm-connect/'); |
| 216 } | 273 } |
| 217 | 274 |
| 218 class MetricsPage extends Page { | 275 class MetricsPage extends Page { |
| 219 static RegExp _matcher = new RegExp(r'isolates/.*/metrics'); | |
| 220 static RegExp _isolateMatcher = new RegExp(r'isolates/.*/'); | |
| 221 | |
| 222 // Page state, retained as long as ObservatoryApplication. | 276 // Page state, retained as long as ObservatoryApplication. |
| 223 String selectedMetricId; | 277 String selectedMetricId; |
| 224 | 278 |
| 225 final Map<int, MetricPoller> pollers = new Map<int, MetricPoller>(); | 279 final Map<int, MetricPoller> pollers = new Map<int, MetricPoller>(); |
| 226 | 280 |
| 227 // 8 seconds, 4 seconds, 2 seconds, 1 second, and one hundred milliseconds. | 281 // 8 seconds, 4 seconds, 2 seconds, 1 second, and one hundred milliseconds. |
| 228 static final List<int> POLL_PERIODS = [8000, | 282 static final List<int> POLL_PERIODS = [8000, |
| 229 4000, | 283 4000, |
| 230 2000, | 284 2000, |
| 231 1000, | 285 1000, |
| (...skipping 27 matching lines...) Expand all Loading... |
| 259 } | 313 } |
| 260 var poller = pollers[refreshPeriod]; | 314 var poller = pollers[refreshPeriod]; |
| 261 if (poller != null) { | 315 if (poller != null) { |
| 262 poller.metrics.add(metric); | 316 poller.metrics.add(metric); |
| 263 metric.poller = poller; | 317 metric.poller = poller; |
| 264 return; | 318 return; |
| 265 } | 319 } |
| 266 throw new FallThroughError(); | 320 throw new FallThroughError(); |
| 267 } | 321 } |
| 268 | 322 |
| 269 String _isolateId(String url) { | 323 void _visit(Uri uri) { |
| 270 // Grab isolate prefix. | |
| 271 String isolateId = _isolateMatcher.stringMatch(url); | |
| 272 // Remove the trailing slash. | |
| 273 return isolateId.substring(0, isolateId.length - 1); | |
| 274 } | |
| 275 | |
| 276 void _visit(String url) { | |
| 277 assert(element != null); | 324 assert(element != null); |
| 278 assert(canVisit(url)); | 325 assert(canVisit(uri)); |
| 279 app.vm.getIsolate(_isolateId(url)).then((i) { | 326 app.vm.getIsolate(uri.queryParameters['isolateId']).then((i) { |
| 280 (element as MetricsPageElement).isolate = i; | 327 (element as MetricsPageElement).isolate = i; |
| 281 }); | 328 }); |
| 282 } | 329 } |
| 283 | 330 |
| 284 bool canVisit(String url) => _matcher.hasMatch(url); | 331 bool canVisit(Uri uri) => uri.path == 'metrics'; |
| 285 } | 332 } |
| OLD | NEW |