generateHotspotReport method

String generateHotspotReport(
  1. CpuSamples samples, {
  2. int topN = 10,
})

Implementation

String generateHotspotReport(CpuSamples samples, {int topN = 10}) {
  final total = (samples.sampleCount ?? 0) < 1 ? 1 : samples.sampleCount!;
  final functions = samples.functions ?? [];

  final sorted = [...functions]
    ..sort(
        (a, b) => (b.exclusiveTicks ?? 0).compareTo(a.exclusiveTicks ?? 0));

  // Filter: keep only Dart user code
  // Dart source files have resolvedUrl like "package:foo/bar.dart" or "file:///...dart"
  // Native frames on Android have resolvedUrl like "/data/app/.../libflutter.so+0x..."
  // Native frames on iOS have empty resolvedUrl or native lib paths
  final user = sorted.where((f) {
    final url = f.resolvedUrl ?? '';
    final name = _formatName(f);
    // Bracket notation = VM internal/native regardless of URL
    if (name.startsWith('[')) return false;
    // Must have a Dart source URL
    if (url.isEmpty) return false;
    if (!url.endsWith('.dart')) return false; // .so, .dylib, empty = not Dart
    // Skip SDK internals
    if (url.startsWith('dart:')) return false;
    if (url.startsWith('org-dartlang-sdk:')) return false; // AOT compiled SDK
    if (url.contains('org-dartlang-sdk')) return false;
    // Skip Flutter framework (keep app code)
    if (url.contains('packages/flutter/')) return false;
    if (url.contains('pub.dartlang.org')) return false;
    // Only keep app package code
    if (!url.contains('package:') && !url.startsWith('file:')) return false;
    return true;
  }).take(topN).toList();

  final windowSec =
      ((samples.timeExtentMicros ?? 0) / 1e6).toStringAsFixed(1);
  final sb = StringBuffer();
  sb.writeln('CPU Hotspots (${windowSec}s window, $total samples)');
  sb.writeln('━' * 60);
  sb.writeln('${'Rank'.padRight(6)}${'Self%'.padRight(8)}${'Total%'.padRight(9)}Function');

  for (int i = 0; i < user.length; i++) {
    final f = user[i];
    final selfPct =
        ((f.exclusiveTicks ?? 0) / total * 100).toStringAsFixed(1).padLeft(5);
    final totalPct =
        ((f.inclusiveTicks ?? 0) / total * 100).toStringAsFixed(1).padLeft(6);
    final name = _formatName(f);
    sb.writeln('  ${(i + 1).toString().padLeft(2)}.  $selfPct%  $totalPct%  $name');
  }

  sb.writeln('');
  sb.writeln(_advice(user, total));
  return sb.toString();
}