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 |