anchor.diff:      // ignore errors.
     }
   }
  -
  -/// Report that metric [name] took [time] micro-seconds to process
  -/// [scanTotalChars] characters.
  -void report(String name, int time) {
  -  var sb = new StringBuffer();
  -  sb.write('$name: $time us, ${time ~/ 1000} ms');
  -  sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms');
  -  print('$sb');
  -}
  -
  -Future<Program> generateKernel(Uri entryUri,
  -    {bool useSdkSummary: false, bool compileSdk: true}) async {
  -  var dartkTimer = new Stopwatch()..start();
  -  // TODO(sigmund): add a constructor with named args to compiler options.
  -  var options = new CompilerOptions()
  -    ..strongMode = false
  -    ..compileSdk = compileSdk
  -    ..packagesFilePath = '.packages'
  -    ..onError = ((e) => print('${e.message}'));
  -  if (useSdkSummary) {
  -    // TODO(sigmund): adjust path based on the benchmark runner architecture.
  -    // Possibly let the runner make the file available at an architecture
  -    // independent location.
  -    options.sdkSummary = 'out/ReleaseX64/dart-sdk/lib/_internal/spec.sum';
  -  } else {
  -    options.sdkPath = 'sdk';
  -  }
  -  Program program = await kernelForProgram(entryUri, options);
  -  dartkTimer.stop();
  -  var suffix = useSdkSummary ? "_sum" : "";
  -  report("kernel_gen_e2e${suffix}", dartkTimer.elapsedMicroseconds);
  -  return program;
  -}
  
positive.code: library front_end.tool.perf;
  
  import 'dart:async';
  import 'dart:io' show exit, stderr;
  
  import 'package:analyzer/dart/ast/ast.dart';
  import 'package:analyzer/error/listener.dart';
  
negative.code:   return token;
  }
  
  UnlinkedUnitBuilder unlinkedSummarize(Source source) {
    var unit = parseFull(source);
    unlinkedSummarizeTimer.start();
    var unlinkedUnit = serializeAstUnlinked(unit);
    unlinkedSummarizeTimer.stop();
    return unlinkedUnit;
  }
  
  /// Produces unlinked summaries for every file in [files] and reports the time
  /// spent doing so.
  void unlinkedSummarizeFiles(Set<Source> files) {
    // The code below will record again how many chars are scanned and how long it
    // takes to scan them, even though we already did so in [scanReachableFiles].
    // Recording and reporting this twice is unnecessary, but we do so for now to
    // validate that the results are consistent.
    scanTimer = new Stopwatch();
    var old = scanTotalChars;
    scanTotalChars = 0;
    parseTimer = new Stopwatch();
    unlinkedSummarizeTimer = new Stopwatch();
    generateUnlinkedSummaries(files);
  
    if (old != scanTotalChars) print('input size changed? ${old} chars');
    report("scan", scanTimer.elapsedMicroseconds);
    report("parse", parseTimer.elapsedMicroseconds);
    report('unlinked summarize', unlinkedSummarizeTimer.elapsedMicroseconds);
    report(
        'unlinked summarize + parse',
        unlinkedSummarizeTimer.elapsedMicroseconds +
            parseTimer.elapsedMicroseconds);
  }
  
  /// Simple container for a mapping from URI string to an unlinked summary.
  class UnlinkedSummaries {
    final summariesByUri = <String, UnlinkedUnit>{};
  
    /// Get the unlinked summary for the given URI, and report a warning if it
    /// can't be found.
    UnlinkedUnit getUnit(String uri) {
      var result = summariesByUri[uri];
      if (result == null) {
        print('Warning: no summary found for: $uri');
      }
      return result;
    }
  }
  
  class _Scanner extends Scanner {
    _Scanner(String contents) : super(new CharSequenceReader(contents)) {
      preserveComments = false;
  