void print_stats()

in opt/instrument/BlockInstrument.cpp [1141:1405]


void print_stats(ScopedMetrics& sm,
                 const std::vector<MethodInfo>& instrumented_methods,
                 const size_t max_num_blocks) {
  MethodInfo total{};
  for (const auto& i : instrumented_methods) {
    total += i;
  }

  const size_t total_instrumented = instrumented_methods.size();
  const size_t only_method_instrumented = total.num_too_many_blocks;
  const size_t total_block_instrumented =
      total_instrumented - only_method_instrumented;

  auto print = [](size_t num, size_t total, size_t& accumulate) {
    std::stringstream ss;
    accumulate += num;
    ss << std::fixed << std::setprecision(3) << std::setw(6) << num << " ("
       << std::setw(6) << (num * 100. / total) << "%, " << std::setw(6)
       << (accumulate * 100. / total) << "%)";
    return ss.str();
  };

  auto divide = [](size_t a, size_t b) {
    if (b == 0) {
      return std::string("N/A");
    }
    std::stringstream ss;
    ss << std::fixed << std::setprecision(4) << double(a) / double(b);
    return ss.str();
  };

  // ----- Print summary
  {
    auto summary_scope = sm.scope("summary");
    TRACE(INSTRUMENT, 4, "Maximum blocks for block instrumentation: %zu",
          max_num_blocks);
    sm.set_metric("max_num_blocks", max_num_blocks);
    TRACE(INSTRUMENT, 4, "Total instrumented methods: %zu", total_instrumented);
    sm.set_metric("total_instrumented", total_instrumented);
    TRACE(INSTRUMENT, 4, "- Block + method instrumented: %zu",
          total_block_instrumented);
    sm.set_metric("block_and_method_instrumented", total_block_instrumented);
    TRACE(INSTRUMENT, 4, "- Only method instrumented: %zu",
          only_method_instrumented);
    sm.set_metric("method_instrumented_only", only_method_instrumented);
  }

  auto scope_total_avg = [&](const std::string& key, size_t num, size_t denom) {
    auto scope = sm.scope(key);
    sm.set_metric("total", num);
    if (denom != 0) {
      sm.set_metric("average100", 100 * num / denom);
    }
    return scope;
  };

  // ----- Bit-vector stats
  TRACE(INSTRUMENT, 4, "Bit-vector stats for block instrumented methods:");
  {
    size_t acc = 0;
    size_t total_bit_vectors = 0;
    std::map<int /*num_vectors*/, size_t /*num_methods*/> dist;
    for (const auto& i : instrumented_methods) {
      if (i.too_many_blocks) {
        ++dist[-1];
      } else {
        ++dist[i.num_vectors];
        total_bit_vectors += i.num_vectors;
      }
    }
    for (const auto& p : dist) {
      TRACE(INSTRUMENT, 4, " %3d vectors: %s", p.first,
            SHOW(print(p.second, total_instrumented, acc)));
    }
    TRACE(INSTRUMENT, 4, "Total/average bit vectors: %zu, %s",
          total_bit_vectors,
          SHOW(divide(total_bit_vectors, total_block_instrumented)));
    scope_total_avg("bit_vectors", total_bit_vectors, total_block_instrumented);
  }

  // ----- Instrumented block stats
  TRACE(INSTRUMENT, 4, "Instrumented / actual non-entry block stats:");

  {
    std::map<int, std::pair<size_t /*instrumented*/, size_t /*block*/>> dist;
    for (const auto& i : instrumented_methods) {
      if (i.too_many_blocks) {
        ++dist[-1].first;
      } else {
        ++dist[i.num_instrumented_blocks].first;
      }
      ++dist[i.num_non_entry_blocks].second;
    }
    std::array<size_t, 2> accs = {0, 0};
    for (const auto& p : dist) {
      TRACE(INSTRUMENT, 4, " %5d blocks: %s | %s", p.first,
            SHOW(print(p.second.first, total_instrumented, accs[0])),
            SHOW(print(p.second.second, total_instrumented, accs[1])));
    }
    TRACE(
        INSTRUMENT, 4, "Total/average instrumented blocks: %zu, %s",
        total.num_instrumented_blocks,
        SHOW(divide(total.num_instrumented_blocks, total_block_instrumented)));
    scope_total_avg("instrumented_blocks", total.num_instrumented_blocks,
                    total_block_instrumented);
    TRACE(INSTRUMENT, 4, "Total/average non-entry blocks: %zu, %s",
          total.num_non_entry_blocks,
          SHOW(divide(total.num_non_entry_blocks, total_instrumented)));
    scope_total_avg("non_entry_blocks", total.num_non_entry_blocks,
                    total_block_instrumented);
  }

  const size_t total_catches = std::accumulate(
      instrumented_methods.begin(), instrumented_methods.end(), size_t(0),
      [](int a, auto&& i) { return a + i.num_catches; });
  const size_t total_instrumented_catches = std::accumulate(
      instrumented_methods.begin(), instrumented_methods.end(), size_t(0),
      [](int a, auto&& i) { return a + i.num_instrumented_catches; });

  // ----- Instrumented/skipped block stats
  auto print_ratio = [&total](size_t num) {
    std::stringstream ss;
    ss << num << std::fixed << std::setprecision(2) << " ("
       << (num * 100. / total.num_non_entry_blocks) << "%)";
    return ss.str();
  };
  auto metric_ratio = [&sm, &total](const std::string& sub_key, size_t num) {
    if (total.num_non_entry_blocks == 0) {
      return;
    }
    sm.set_metric(sub_key, num);
    sm.set_metric(sub_key + ".ratio100.00",
                  10000 * num / total.num_non_entry_blocks);
  };

  {
    auto non_entry_scope = sm.scope("non_entry_blocks_stats");
    TRACE(INSTRUMENT, 4, "Total non-entry blocks: %zu",
          total.num_non_entry_blocks);
    sm.set_metric("total", total.num_non_entry_blocks);
    TRACE(INSTRUMENT, 4, "- Instrumented blocks: %s",
          SHOW(print_ratio(total.num_instrumented_blocks)));
    metric_ratio("total_instrumented_blocks", total.num_instrumented_blocks);
    TRACE(INSTRUMENT, 4, "- Merged blocks: %s",
          print_ratio(total.num_merged).c_str());
    sm.set_metric("merged", total.num_merged);
    TRACE(INSTRUMENT, 4, "- Merged blocks (into non-instrumentable): %s",
          print_ratio(total.num_merged_not_instrumented).c_str());
    sm.set_metric("merged_not_instrumentable",
                  total.num_merged_not_instrumented);
    TRACE(INSTRUMENT, 4, "- Skipped catch blocks: %s",
          SHOW(print_ratio(total_catches - total_instrumented_catches)));
    {
      auto skipped_scope = sm.scope("skipped");
      metric_ratio("catch_blocks", total_catches - total_instrumented_catches);
      auto no_sb = std::accumulate(
          instrumented_methods.begin(), instrumented_methods.end(), size_t(0),
          [](size_t a, auto&& i) { return a + i.num_no_source_blocks; });
      TRACE(INSTRUMENT, 4, "- Skipped due to no source block: %s",
            SHOW(print_ratio(no_sb)));
      metric_ratio("no_source_blocks", no_sb);
      auto too_large_methods = std::accumulate(
          instrumented_methods.begin(), instrumented_methods.end(), size_t(0),
          [](size_t a, auto&& i) { return a + i.num_blocks_too_large; });
      TRACE(INSTRUMENT, 4, "- Skipped due to too large methods: %s",
            SHOW(print_ratio(too_large_methods)));
      metric_ratio("too_large_methods", too_large_methods);
      auto empty_blocks = std::accumulate(
          instrumented_methods.begin(), instrumented_methods.end(), size_t(0),
          [](size_t a, auto&& i) { return a + i.num_empty_blocks; });
      TRACE(INSTRUMENT, 4, "- Skipped empty blocks: %s",
            SHOW(print_ratio(empty_blocks)));
      metric_ratio("empty_blocks", empty_blocks);
      auto useless_blocks = std::accumulate(
          instrumented_methods.begin(), instrumented_methods.end(), size_t(0),
          [](size_t a, auto&& i) { return a + i.num_useless_blocks; });
      TRACE(INSTRUMENT, 4, "- Skipped useless blocks: %s",
            SHOW(print_ratio(useless_blocks)));
      metric_ratio("useless_blocks", useless_blocks);
    }
  }

  // ----- Instrumented exit block stats
  TRACE(INSTRUMENT, 4, "Instrumented exit block stats:");
  {
    size_t acc = 0;
    size_t total_exits = 0;
    size_t no_exit = 0;
    std::map<int /*num_vectors*/, size_t /*num_methods*/> dist;
    TRACE(INSTRUMENT, 4, "No onMethodExit but 1+ non-entry blocks:");
    int k = 0;
    for (const auto& i : instrumented_methods) {
      if (!i.too_many_blocks && i.num_exit_calls == 0 &&
          i.num_non_entry_blocks != 0) {
        TRACE(INSTRUMENT, 4, "- %d: %zu, %s", ++k, i.num_non_entry_blocks,
              show_deobfuscated(i.method).c_str());
        ++no_exit;
      }
      ++dist[i.num_exit_calls];
      total_exits += i.num_exit_calls;
    }
    for (const auto& p : dist) {
      TRACE(INSTRUMENT, 4, " %4d exits: %s", p.first,
            SHOW(print(p.second, total_instrumented, acc)));
    }
    TRACE(INSTRUMENT, 4, "Total/average instrumented exits: %zu, %s",
          total_exits, SHOW(divide(total_exits, total_instrumented)));
    auto exit_scope =
        scope_total_avg("instrumented_exits", total_exits, total_instrumented);
    sm.set_metric("methods_without_exit_calls", no_exit);
  }

  // ----- Catch block stats
  TRACE(INSTRUMENT, 4, "Catch block stats:");
  {
    size_t acc = 0;
    std::map<int, size_t> dist;
    for (const auto& i : instrumented_methods) {
      ++dist[i.num_catches];
    }
    for (const auto& p : dist) {
      TRACE(INSTRUMENT, 4, " %4d catches: %s", p.first,
            SHOW(print(p.second, total_instrumented, acc)));
    }
    TRACE(INSTRUMENT, 4, "Total/average catch blocks: %zu, %s",
          total.num_catches,
          SHOW(divide(total.num_catches, total_instrumented)));
    scope_total_avg("catch_blocks", total.num_catches, total_instrumented);
  }

  auto print_two_dists = [&divide, &print, &instrumented_methods,
                          total_instrumented, total_block_instrumented](
                             const char* name1, const char* name2,
                             auto&& accessor1, auto&& accessor2) {
    std::map<int, std::pair<size_t, size_t>> dist;
    size_t total1 = 0;
    size_t total2 = 0;
    for (const auto& i : instrumented_methods) {
      if (i.too_many_blocks) {
        ++dist[-1].first;
        ++dist[-1].second;
      } else {
        ++dist[accessor1(i)].first;
        ++dist[accessor2(i)].second;
        total1 += accessor1(i);
        total2 += accessor2(i);
      }
    }
    std::array<size_t, 2> accs = {0, 0};
    for (const auto& p : dist) {
      TRACE(INSTRUMENT, 4, " %5d blocks: %s | %s", p.first,
            SHOW(print(p.second.first, total_instrumented, accs[0])),
            SHOW(print(p.second.second, total_instrumented, accs[1])));
    }
    TRACE(INSTRUMENT, 4, "Total/average %s blocks: %zu, %s", name1, total1,
          SHOW(divide(total1, total_block_instrumented)));
    TRACE(INSTRUMENT, 4, "Total/average %s blocks: %zu, %s", name2, total2,
          SHOW(divide(total2, total_block_instrumented)));
  };

  TRACE(INSTRUMENT, 4, "Empty / useless block stats:");
  print_two_dists(
      "empty", "useless", [](auto&& v) { return v.num_empty_blocks; },
      [](auto&& v) { return v.num_useless_blocks; });
}