import.html revision 972bd9a9d2c6597a0145a675cbfa527d0510b048
1<!DOCTYPE html> 2<!-- 3Copyright 2015 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel='import' href='/tracing/base/base.html'> 9<link rel="import" href="/tracing/importer/empty_importer.html"> 10<link rel="import" href="/tracing/importer/importer.html"> 11 12<script> 13'use strict'; 14 15tr.exportTo('tr.importer', function() { 16 function ImportOptions() { 17 this.shiftWorldToZero = true; 18 this.pruneEmptyContainers = true; 19 this.showImportWarnings = true; 20 21 // Callback called after 22 // importers run in which more data can be added to the model, before it is 23 // finalized. 24 this.customizeModelCallback = undefined; 25 26 var auditorTypes = tr.c.Auditor.getAllRegisteredTypeInfos(); 27 this.auditorConstructors = auditorTypes.map(function(typeInfo) { 28 return typeInfo.constructor; 29 }); 30 } 31 32 function Import(model, opt_options) { 33 if (model === undefined) 34 throw new Error('Must provide model to import into.'); 35 36 // TODO(dsinclair): Check the model is empty. 37 38 this.importing_ = false; 39 this.importOptions_ = opt_options || new ImportOptions(); 40 41 this.model_ = model; 42 this.model_.importOptions = this.importOptions_; 43 } 44 45 Import.prototype = { 46 __proto__: Object.prototype, 47 48 /** 49 * Imports the provided traces into the model. The eventData type 50 * is undefined and will be passed to all the importers registered 51 * via Importer.register. The first importer that returns true 52 * for canImport(events) will be used to import the events. 53 * 54 * The primary trace is provided via the eventData variable. If multiple 55 * traces are to be imported, specify the first one as events, and the 56 * remainder in the opt_additionalEventData array. 57 * 58 * @param {Array} traces An array of eventData to be imported. Each 59 * eventData should correspond to a single trace file and will be handled by 60 * a separate importer. 61 */ 62 importTraces: function(traces) { 63 var progressMeter = { 64 update: function(msg) {} 65 }; 66 67 tr.b.Task.RunSynchronously( 68 this.createImportTracesTask(progressMeter, traces)); 69 }, 70 71 /** 72 * Imports a trace with the usual options from importTraces, but 73 * does so using idle callbacks, putting up an import dialog 74 * during the import process. 75 */ 76 importTracesWithProgressDialog: function(traces) { 77 if (tr.isHeadless) 78 throw new Error('Cannot use this method in headless mode.'); 79 80 var overlay = tr.ui.b.Overlay(); 81 overlay.title = 'Importing...'; 82 overlay.userCanClose = false; 83 overlay.msgEl = document.createElement('div'); 84 overlay.appendChild(overlay.msgEl); 85 overlay.msgEl.style.margin = '20px'; 86 overlay.update = function(msg) { 87 this.msgEl.textContent = msg; 88 } 89 overlay.visible = true; 90 91 var promise = 92 tr.b.Task.RunWhenIdle(this.createImportTracesTask(overlay, traces)); 93 promise.then( 94 function() { overlay.visible = false; }, 95 function(err) { overlay.visible = false; } 96 ); 97 return promise; 98 }, 99 100 /** 101 * Creates a task that will import the provided traces into the model, 102 * updating the progressMeter as it goes. Parameters are as defined in 103 * importTraces. 104 */ 105 createImportTracesTask: function(progressMeter, traces) { 106 if (this.importing_) 107 throw new Error('Already importing.'); 108 this.importing_ = true; 109 110 // Just some simple setup. It is useful to have a no-op first 111 // task so that we can set up the lastTask = lastTask.after() 112 // pattern that follows. 113 var importTask = new tr.b.Task(function() { 114 progressMeter.update('I will now import your traces for you...'); 115 }, this); 116 var lastTask = importTask; 117 118 var importers = []; 119 120 lastTask = lastTask.after(function() { 121 // Copy the traces array, we may mutate it. 122 traces = traces.slice(0); 123 progressMeter.update('Creating importers...'); 124 // Figure out which importers to use. 125 for (var i = 0; i < traces.length; ++i) 126 importers.push(this.createImporter_(traces[i])); 127 128 // Some traces have other traces inside them. Before doing the full 129 // import, ask the importer if it has any subtraces, and if so, create 130 // importers for them, also. 131 for (var i = 0; i < importers.length; i++) { 132 var subtraces = importers[i].extractSubtraces(); 133 for (var j = 0; j < subtraces.length; j++) { 134 try { 135 traces.push(subtraces[j]); 136 importers.push(this.createImporter_(subtraces[j])); 137 } catch (error) { 138 // TODO(kphanee): Log the subtrace file which has failed. 139 console.warn(error.name + ': ' + error.message); 140 continue; 141 } 142 } 143 } 144 145 if (traces.length && !this.hasEventDataDecoder_(importers)) { 146 throw new Error( 147 'Could not find an importer for the provided eventData.'); 148 } 149 150 // Sort them on priority. This ensures importing happens in a 151 // predictable order, e.g. ftrace_importer before 152 // trace_event_importer. 153 importers.sort(function(x, y) { 154 return x.importPriority - y.importPriority; 155 }); 156 }, this); 157 158 // Run the import. 159 lastTask = lastTask.after(function(task) { 160 importers.forEach(function(importer, index) { 161 task.subTask(function() { 162 progressMeter.update( 163 'Importing ' + (index + 1) + ' of ' + importers.length); 164 importer.importEvents(); 165 }, this); 166 }, this); 167 }, this); 168 169 // Run the cusomizeModelCallback if needed. 170 if (this.importOptions_.customizeModelCallback) { 171 lastTask = lastTask.after(function(task) { 172 this.importOptions_.customizeModelCallback(this.model_); 173 }, this); 174 } 175 176 // Import sample data. 177 lastTask = lastTask.after(function(task) { 178 importers.forEach(function(importer, index) { 179 progressMeter.update( 180 'Importing sample data ' + (index + 1) + '/' + importers.length); 181 importer.importSampleData(); 182 }, this); 183 }, this); 184 185 // Autoclose open slices and create subSlices. 186 lastTask = lastTask.after(function() { 187 progressMeter.update('Autoclosing open slices...'); 188 this.model_.autoCloseOpenSlices(); 189 this.model_.createSubSlices(); 190 }, this); 191 192 // Finalize import. 193 lastTask = lastTask.after(function(task) { 194 importers.forEach(function(importer, index) { 195 progressMeter.update( 196 'Finalizing import ' + (index + 1) + '/' + importers.length); 197 importer.finalizeImport(); 198 }, this); 199 }, this); 200 201 // Run preinit. 202 lastTask = lastTask.after(function() { 203 progressMeter.update('Initializing objects (step 1/2)...'); 204 this.model_.preInitializeObjects(); 205 }, this); 206 207 // Prune empty containers. 208 if (this.importOptions_.pruneEmptyContainers) { 209 lastTask = lastTask.after(function() { 210 progressMeter.update('Pruning empty containers...'); 211 this.model_.pruneEmptyContainers(); 212 }, this); 213 } 214 215 // Merge kernel and userland slices on each thread. 216 lastTask = lastTask.after(function() { 217 progressMeter.update('Merging kernel with userland...'); 218 this.model_.mergeKernelWithUserland(); 219 }, this); 220 221 // Create auditors 222 var auditors = []; 223 lastTask = lastTask.after(function() { 224 progressMeter.update('Adding arbitrary data to model...'); 225 auditors = this.importOptions_.auditorConstructors.map( 226 function(auditorConstructor) { 227 return new auditorConstructor(this.model_); 228 }, this); 229 auditors.forEach(function(auditor) { 230 auditor.runAnnotate(); 231 }); 232 }, this); 233 234 lastTask = lastTask.after(function() { 235 progressMeter.update('Computing final world bounds...'); 236 this.model_.computeWorldBounds(this.importOptions_.shiftWorldToZero); 237 }, this); 238 239 // Build the flow event interval tree. 240 lastTask = lastTask.after(function() { 241 progressMeter.update('Building flow event map...'); 242 this.model_.buildFlowEventIntervalTree(); 243 }, this); 244 245 // Join refs. 246 lastTask = lastTask.after(function() { 247 progressMeter.update('Joining object refs...'); 248 for (var i = 0; i < importers.length; i++) 249 importers[i].joinRefs(); 250 }, this); 251 252 // Delete any undeleted objects. 253 lastTask = lastTask.after(function() { 254 progressMeter.update('Cleaning up undeleted objects...'); 255 this.model_.cleanupUndeletedObjects(); 256 }, this); 257 258 // Sort global and process memory dumps. 259 lastTask = lastTask.after(function() { 260 progressMeter.update('Sorting memory dumps...'); 261 this.model_.sortMemoryDumps(); 262 }, this); 263 264 // Calculate memory dump graph attributes. 265 lastTask = lastTask.after(function() { 266 progressMeter.update('Calculating memory dump graph attributes...'); 267 this.model_.calculateMemoryGraphAttributes(); 268 }, this); 269 270 // Run initializers. 271 lastTask = lastTask.after(function() { 272 progressMeter.update('Initializing objects (step 2/2)...'); 273 this.model_.initializeObjects(); 274 }, this); 275 276 // Build event indices mapping from an event id to all flow events. 277 lastTask = lastTask.after(function() { 278 progressMeter.update('Building flow event indices...'); 279 this.model_.buildEventIndices(); 280 }, this); 281 282 // Run audits. 283 lastTask = lastTask.after(function() { 284 progressMeter.update('Running auditors...'); 285 auditors.forEach(function(auditor) { 286 auditor.runAudit(); 287 }); 288 }, this); 289 290 lastTask = lastTask.after(function() { 291 progressMeter.update('Updating interaction records...'); 292 this.model_.sortInteractionRecords(); 293 }, this); 294 295 lastTask = lastTask.after(function() { 296 progressMeter.update('Updating alerts...'); 297 this.model_.sortAlerts(); 298 }, this); 299 300 lastTask = lastTask.after(function() { 301 progressMeter.update('Update bounds...'); 302 this.model_.updateBounds(); 303 }, this); 304 305 // Cleanup. 306 lastTask.after(function() { 307 this.importing_ = false; 308 }, this); 309 return importTask; 310 }, 311 312 createImporter_: function(eventData) { 313 var importerConstructor = tr.importer.Importer.findImporterFor(eventData); 314 if (!importerConstructor) { 315 throw new Error('Couldn\'t create an importer for the provided ' + 316 'eventData.'); 317 } 318 return new importerConstructor(this.model_, eventData); 319 }, 320 321 hasEventDataDecoder_: function(importers) { 322 if (importers.length === 0) 323 return false; 324 325 for (var i = 0; i < importers.length; ++i) { 326 if (!importers[i].isTraceDataContainer()) 327 return true; 328 } 329 return false; 330 } 331 }; 332 333 return { 334 ImportOptions: ImportOptions, 335 Import: Import 336 }; 337}); 338</script> 339