Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 import "dart:async"; | 5 import "dart:async"; |
| 6 import "dart:convert"; | 6 import "dart:convert"; |
| 7 import "dart:io"; | 7 import "dart:io"; |
| 8 import "dart:isolate"; | 8 import "dart:isolate"; |
| 9 import "dart:mirrors"; | |
| 10 | 9 |
| 11 import "package:args/args.dart"; | 10 import "package:args/args.dart"; |
| 12 import "package:path/path.dart"; | 11 import "package:path/path.dart"; |
| 13 | 12 |
| 14 /// [Environment] stores gathered arguments information. | 13 /// [Environment] stores gathered arguments information. |
| 15 class Environment { | 14 class Environment { |
| 16 String sdkRoot; | 15 String sdkRoot; |
| 17 String pkgRoot; | 16 String pkgRoot; |
| 18 var input; | 17 var input; |
| 19 var output; | 18 var output; |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 182 Map createHitmap(String rawJson, Resolver resolver) { | 181 Map createHitmap(String rawJson, Resolver resolver) { |
| 183 Map<String, Map<int,int>> hitMap = {}; | 182 Map<String, Map<int,int>> hitMap = {}; |
| 184 | 183 |
| 185 addToMap(source, line, count) { | 184 addToMap(source, line, count) { |
| 186 if (!hitMap[source].containsKey(line)) { | 185 if (!hitMap[source].containsKey(line)) { |
| 187 hitMap[source][line] = 0; | 186 hitMap[source][line] = 0; |
| 188 } | 187 } |
| 189 hitMap[source][line] += count; | 188 hitMap[source][line] += count; |
| 190 } | 189 } |
| 191 | 190 |
| 192 JSON.decode(rawJson).forEach((Map e) { | 191 JSON.decode(rawJson)['coverage'].forEach((Map e) { |
| 193 String source = resolver.resolve(e["source"]); | 192 String source = resolver.resolve(e["source"]); |
| 194 if (source == null) { | 193 if (source == null) { |
| 195 // Couldnt resolve import, so skip this entry. | 194 // Couldnt resolve import, so skip this entry. |
| 196 return; | 195 return; |
| 197 } | 196 } |
| 198 if (!hitMap.containsKey(source)) { | 197 if (!hitMap.containsKey(source)) { |
| 199 hitMap[source] = {}; | 198 hitMap[source] = {}; |
| 200 } | 199 } |
| 201 var hits = e["hits"]; | 200 var hits = e["hits"]; |
| 202 // hits is a flat array of the following format: | 201 // hits is a flat array of the following format: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 if (FileSystemEntity.isDirectorySync(absPath)) { | 247 if (FileSystemEntity.isDirectorySync(absPath)) { |
| 249 return new Directory(absPath).listSync(recursive: true) | 248 return new Directory(absPath).listSync(recursive: true) |
| 250 .where((entity) => entity is File && | 249 .where((entity) => entity is File && |
| 251 filePattern.hasMatch(basename(entity.path))) | 250 filePattern.hasMatch(basename(entity.path))) |
| 252 .toList(); | 251 .toList(); |
| 253 } | 252 } |
| 254 | 253 |
| 255 return [new File(absPath)]; | 254 return [new File(absPath)]; |
| 256 } | 255 } |
| 257 | 256 |
| 258 worker() { | 257 worker(WorkMessage msg) { |
| 259 final start = new DateTime.now().millisecondsSinceEpoch; | 258 final start = new DateTime.now().millisecondsSinceEpoch; |
| 260 String me = currentMirrorSystem().isolate.debugName; | |
| 261 | 259 |
| 262 port.receive((Message message, reply) { | 260 var env = msg.environment; |
| 263 if (message.type == Message.SHUTDOWN) { | 261 List files = msg.files; |
| 264 port.close(); | 262 Resolver resolver = new Resolver(env); |
| 263 var workerHitmap = {}; | |
| 264 files.forEach((File fileEntry) { | |
| 265 // Read file sync, as it only contains 1 object. | |
| 266 String contents = fileEntry.readAsStringSync(); | |
| 267 if (contents.length > 0) { | |
| 268 mergeHitmaps(createHitmap(contents, resolver), workerHitmap); | |
| 265 } | 269 } |
| 270 }); | |
| 266 | 271 |
| 267 if (message.type == Message.WORK) { | 272 if (env["verbose"]) { |
| 268 var env = message.payload[0]; | 273 final end = new DateTime.now().millisecondsSinceEpoch; |
| 269 List files = message.payload[1]; | 274 print("${msg.workerName}: Finished processing files. " |
| 270 Resolver resolver = new Resolver(env); | 275 "Took ${end - start} ms."); |
| 271 var workerHitmap = {}; | 276 } |
| 272 files.forEach((File fileEntry) { | |
| 273 // Read file sync, as it only contains 1 object. | |
| 274 String contents = fileEntry.readAsStringSync(); | |
| 275 if (contents.length > 0) { | |
| 276 mergeHitmaps(createHitmap(contents, resolver), workerHitmap); | |
| 277 } | |
| 278 }); | |
| 279 if (env["verbose"]) { | |
| 280 final end = new DateTime.now().millisecondsSinceEpoch; | |
| 281 print("worker[${me}]: Finished processing files. " | |
| 282 "Took ${end - start} ms."); | |
| 283 } | |
| 284 reply.send(new Message(Message.RESULT, [workerHitmap, resolver.failed])); | |
| 285 } | |
| 286 | 277 |
| 287 }); | 278 msg.replyPort.send(new ResultMessage(workerHitmap, resolver.failed)); |
| 288 } | 279 } |
| 289 | 280 |
| 290 class Message { | 281 class WorkMessage { |
| 291 static const int SHUTDOWN = 1; | 282 final String workerName; |
| 292 static const int RESULT = 2; | 283 final Environment environment; |
| 293 static const int WORK = 3; | 284 final List files; |
| 285 final SendPort replyPort; | |
| 286 WorkMessage(this.workerName, this.environment, this.files, this.replyPort); | |
| 287 } | |
| 294 | 288 |
| 295 final int type; | 289 class ResultMessage { |
| 296 final payload; | 290 final hitmap; |
| 297 | 291 final failedResolves; |
| 298 Message(this.type, this.payload); | 292 ResultMessage(this.hitmap, this.failedResolves); |
| 299 } | 293 } |
| 300 | 294 |
| 301 final env = new Environment(); | 295 final env = new Environment(); |
| 302 | 296 |
| 297 List<List> split(List list, int nBuckets) { | |
|
Ivan Posva
2014/02/03 04:32:02
Please at least add a TODO to come up with a more
rmacnak
2014/02/03 18:31:56
Done.
| |
| 298 // Leftover goes in the last bucket. | |
| 299 var buckets = new List(nBuckets); | |
| 300 var bucketSize = list.length ~/ nBuckets; | |
| 301 var taken = 0; | |
| 302 for (int i = 0; i < nBuckets; i++) { | |
| 303 bool lastBucket = i + 1 == nBuckets; | |
| 304 var start = i * bucketSize; | |
| 305 var end = lastBucket ? list.length : start + bucketSize ; | |
| 306 buckets[i] = list.sublist(start, end); | |
| 307 taken += buckets[i].length; | |
| 308 } | |
| 309 if (taken != list.length) throw "Error splitting"; | |
| 310 return buckets; | |
| 311 } | |
| 312 | |
| 313 Future<ResultMessage> spawnWorker(name, environment, files) { | |
| 314 ReceivePort port = new ReceivePort(); | |
| 315 var completer = new Completer(); | |
| 316 port.listen((WorkMessage msg) { | |
|
Ivan Posva
2014/02/03 04:32:02
I am not certain the type here is correct. Don't y
rmacnak
2014/02/03 18:31:56
Fixed, plus one other failure in checked mode.
| |
| 317 completer.complete(msg); | |
| 318 port.close(); | |
| 319 }); | |
| 320 var msg = new WorkMessage(name, environment, files, port.sendPort); | |
| 321 Isolate.spawn(worker, msg); | |
| 322 return completer.future; | |
| 323 } | |
| 324 | |
| 303 main(List<String> arguments) { | 325 main(List<String> arguments) { |
| 304 parseArgs(arguments); | 326 parseArgs(arguments); |
| 305 | 327 |
| 306 List files = filesToProcess(env.input); | 328 List files = filesToProcess(env.input); |
| 307 int filesPerWorker = files.length ~/ env.workers; | |
| 308 List workerPorts = []; | |
| 309 int doneCnt = 0; | |
| 310 | 329 |
| 311 List failedResolves = []; | 330 List failedResolves = []; |
| 312 List failedLoads = []; | 331 List failedLoads = []; |
| 313 Map globalHitmap = {}; | 332 Map globalHitmap = {}; |
| 314 int start = new DateTime.now().millisecondsSinceEpoch; | 333 int start = new DateTime.now().millisecondsSinceEpoch; |
| 315 | 334 |
| 316 if (env.verbose) { | 335 if (env.verbose) { |
| 317 print("Environment:"); | 336 print("Environment:"); |
| 318 print(" # files: ${files.length}"); | 337 print(" # files: ${files.length}"); |
| 319 print(" # workers: ${env.workers}"); | 338 print(" # workers: ${env.workers}"); |
| 320 print(" sdk-root: ${env.sdkRoot}"); | 339 print(" sdk-root: ${env.sdkRoot}"); |
| 321 print(" package-root: ${env.pkgRoot}"); | 340 print(" package-root: ${env.pkgRoot}"); |
| 322 } | 341 } |
| 323 | 342 |
| 324 port.receive((Message message, reply) { | |
| 325 if (message.type == Message.RESULT) { | |
| 326 mergeHitmaps(message.payload[0], globalHitmap); | |
| 327 failedResolves.addAll(message.payload[1]); | |
| 328 doneCnt++; | |
| 329 } | |
| 330 | |
| 331 // All workers are done. Process the data. | |
| 332 if (doneCnt == env.workers) { | |
| 333 workerPorts.forEach((p) => p.send(new Message(Message.SHUTDOWN, null))); | |
| 334 if (env.verbose) { | |
| 335 final end = new DateTime.now().millisecondsSinceEpoch; | |
| 336 print("Done creating a global hitmap. Took ${end - start} ms."); | |
| 337 } | |
| 338 | |
| 339 Future out; | |
| 340 if (env.prettyPrint) { | |
| 341 out = prettyPrint(globalHitmap, failedLoads); | |
| 342 } | |
| 343 if (env.lcov) { | |
| 344 out = lcov(globalHitmap); | |
| 345 } | |
| 346 | |
| 347 out.then((_) { | |
| 348 env.output.close().then((_) { | |
| 349 if (env.verbose) { | |
| 350 final end = new DateTime.now().millisecondsSinceEpoch; | |
| 351 print("Done flushing output. Took ${end - start} ms."); | |
| 352 } | |
| 353 }); | |
| 354 port.close(); | |
| 355 | |
| 356 if (env.verbose) { | |
| 357 if (failedResolves.length > 0) { | |
| 358 print("Failed to resolve:"); | |
| 359 failedResolves.toSet().forEach((e) { | |
| 360 print(" ${e}"); | |
| 361 }); | |
| 362 } | |
| 363 if (failedLoads.length > 0) { | |
| 364 print("Failed to load:"); | |
| 365 failedLoads.toSet().forEach((e) { | |
| 366 print(" ${e}"); | |
| 367 }); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 }); | |
| 372 } | |
| 373 }); | |
| 374 | |
| 375 Map sharedEnv = { | 343 Map sharedEnv = { |
| 376 "sdkRoot": env.sdkRoot, | 344 "sdkRoot": env.sdkRoot, |
| 377 "pkgRoot": env.pkgRoot, | 345 "pkgRoot": env.pkgRoot, |
| 378 "verbose": env.verbose, | 346 "verbose": env.verbose, |
| 379 }; | 347 }; |
| 380 | 348 |
| 381 // Create workers. | 349 // Create workers. |
| 382 for (var i = 1; i < env.workers; i++) { | 350 int workerId = 0; |
| 383 var p = spawnFunction(worker); | 351 var results = split(files, env.workers).map((workerFiles) { |
| 384 workerPorts.add(p); | 352 var result = spawnWorker("Worker ${workerId++}", sharedEnv, workerFiles); |
| 385 var start = files.length - filesPerWorker; | 353 return result.then((ResultMessage message) { |
| 386 var end = files.length; | 354 mergeHitmaps(message.hitmap, globalHitmap); |
| 387 var workerFiles = files.getRange(start, end).toList(); | 355 failedResolves.addAll(message.failedResolves); |
| 388 files.removeRange(start, end); | 356 }); |
| 389 p.send(new Message(Message.WORK, [sharedEnv, workerFiles]), port); | 357 }); |
| 390 } | |
| 391 // Let the last worker deal with the rest of the files (which should be only | |
| 392 // off by at max (#workers - 1). | |
| 393 var p = spawnFunction(worker); | |
| 394 workerPorts.add(p); | |
| 395 p.send(new Message(Message.WORK, [sharedEnv, files]), port.toSendPort()); | |
| 396 | 358 |
| 397 return 0; | 359 Future.wait(results).then((ignore) { |
| 360 // All workers are done. Process the data. | |
| 361 if (env.verbose) { | |
| 362 final end = new DateTime.now().millisecondsSinceEpoch; | |
| 363 print("Done creating a global hitmap. Took ${end - start} ms."); | |
| 364 } | |
| 365 | |
| 366 Future out; | |
| 367 if (env.prettyPrint) { | |
| 368 out = prettyPrint(globalHitmap, failedLoads); | |
| 369 } | |
| 370 if (env.lcov) { | |
| 371 out = lcov(globalHitmap); | |
| 372 } | |
| 373 | |
| 374 out.then((_) { | |
| 375 env.output.close().then((_) { | |
| 376 if (env.verbose) { | |
| 377 final end = new DateTime.now().millisecondsSinceEpoch; | |
| 378 print("Done flushing output. Took ${end - start} ms."); | |
| 379 } | |
| 380 }); | |
| 381 | |
| 382 if (env.verbose) { | |
| 383 if (failedResolves.length > 0) { | |
| 384 print("Failed to resolve:"); | |
| 385 failedResolves.toSet().forEach((e) { | |
| 386 print(" ${e}"); | |
| 387 }); | |
| 388 } | |
| 389 if (failedLoads.length > 0) { | |
| 390 print("Failed to load:"); | |
| 391 failedLoads.toSet().forEach((e) { | |
| 392 print(" ${e}"); | |
| 393 }); | |
| 394 } | |
| 395 } | |
| 396 }); | |
| 397 }); | |
| 398 } | 398 } |
| 399 | 399 |
| 400 /// Checks the validity of the provided arguments. Does not initialize actual | 400 /// Checks the validity of the provided arguments. Does not initialize actual |
| 401 /// processing. | 401 /// processing. |
| 402 parseArgs(List<String> arguments) { | 402 parseArgs(List<String> arguments) { |
| 403 var parser = new ArgParser(); | 403 var parser = new ArgParser(); |
| 404 | 404 |
| 405 parser.addOption("sdk-root", abbr: "s", | 405 parser.addOption("sdk-root", abbr: "s", |
| 406 help: "path to the SDK root"); | 406 help: "path to the SDK root"); |
| 407 parser.addOption("package-root", abbr: "p", | 407 parser.addOption("package-root", abbr: "p", |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 493 } | 493 } |
| 494 | 494 |
| 495 try { | 495 try { |
| 496 env.workers = int.parse("${args["workers"]}"); | 496 env.workers = int.parse("${args["workers"]}"); |
| 497 } catch (e) { | 497 } catch (e) { |
| 498 fail("Invalid worker count: $e"); | 498 fail("Invalid worker count: $e"); |
| 499 } | 499 } |
| 500 | 500 |
| 501 env.verbose = args["verbose"]; | 501 env.verbose = args["verbose"]; |
| 502 } | 502 } |
| OLD | NEW |