void PassManager::run_passes()

in libredex/PassManager.cpp [1087:1312]


void PassManager::run_passes(DexStoresVector& stores, ConfigFiles& conf) {
  auto profiler_info = ScopedCommandProfiling::maybe_info_from_env("");
  const Pass* profiler_info_pass = nullptr;
  if (profiler_info) {
    profiler_info_pass = get_profiled_pass(*this);
  }
  auto profiler_all_info =
      ScopedCommandProfiling::maybe_info_from_env("ALL_PASSES_");

  if (conf.force_single_dex()) {
    // Squash the dexes into one, so that the passes all see only one dex and
    // all the cross-dex reference checking are accurate.
    squash_into_one_dex(stores);
  }

  DexStoreClassesIterator it(stores);
  Scope scope = build_class_scope(it);

  // Clear stale data. Make sure we start fresh.
  m_preserved_analysis_passes.clear();

  {
    Timer t("API Level Checker");
    api::LevelChecker::init(m_redex_options.min_sdk, scope);
  }

  maybe_write_env_seeds_file(conf, scope);
  maybe_print_seeds_incoming(conf, scope, m_pg_config);
  maybe_write_hashes_incoming(conf, scope);

  maybe_enable_opt_data(conf);

  // Load configurations regarding the scope.
  conf.load(scope);

  sanitizers::lsan_do_recoverable_leak_check();

  eval_passes(stores, conf);

  // Retrieve the hasher's settings.
  bool run_hasher_after_each_pass =
      is_run_hasher_after_each_pass(conf, get_redex_options());

  // Retrieve the assessor's settings.
  m_assessor_config = ::get_assessor_config(conf, get_redex_options());
  const auto& assessor_config = this->get_assessor_config();

  // Retrieve the type checker's settings.
  CheckerConfig checker_conf{conf};
  checker_conf.on_input(scope);

  // Pull on method-profiles, so that they get initialized, and are matched
  // against the *initial* methods
  conf.get_method_profiles();

  if (run_hasher_after_each_pass) {
    m_initial_hash = run_hasher(nullptr, scope);
  }

  CheckUniqueDeobfuscatedNames check_unique_deobfuscated{conf};
  check_unique_deobfuscated.run_initially(scope);

  VisualizerHelper graph_visualizer(conf);

  sanitizers::lsan_do_recoverable_leak_check();

  const bool hwm_pass_stats =
      traceEnabled(STATS, 1) || conf.get_json_config().get("mem_stats", true);
  const bool hwm_per_pass =
      conf.get_json_config().get("mem_stats_per_pass", true);

  size_t min_pass_idx_for_dex_ref_check =
      checker_conf.min_pass_idx_for_dex_ref_check(m_activated_passes);

  // Abort if the analysis pass dependencies are not satisfied.
  AnalysisUsage::check_dependencies(m_activated_passes);

  AfterPassSizes after_pass_size(this, conf);

  // For core loop legibility, have a lambda here.

  auto pre_pass_verifiers = [&](Pass* pass, size_t i) {
    if (i == 0 && assessor_config.run_initially) {
      ::run_assessor(*this, scope, /* initially */ true);
    }
  };

  auto post_pass_verifiers = [&](Pass* pass, size_t i, size_t size) {
    ConcurrentSet<const DexMethodRef*> all_code_referenced_methods;
    walk::parallel::code(build_class_scope(stores), [&](DexMethod* m,
                                                        IRCode& code) {
      // Ensure that pass authors deconstructed the editable CFG at the end of
      // their pass. Currently, passes assume the incoming code will be in
      // IRCode form
      always_assert_log(!code.editable_cfg_built(), "%s has a cfg!", SHOW(m));
      if (slow_invariants_debug) {
        std::vector<DexMethodRef*> methods;
        methods.reserve(1000);
        methods.push_back(m);
        code.gather_methods(methods);
        for (auto* mref : methods) {
          always_assert_log(
              DexMethod::get_method(mref->get_class(), mref->get_name(),
                                    mref->get_proto()) != nullptr,
              "Did not find %s in the context, referenced from %s!", SHOW(mref),
              SHOW(m));
          all_code_referenced_methods.insert(mref);
        }
      }
    });
    if (slow_invariants_debug) {
      ScopedMetrics sm(*this);
      sm.set_metric("num_code_referenced_methods",
                    all_code_referenced_methods.size());
    }

    bool run_hasher = run_hasher_after_each_pass;
    bool run_assessor = assessor_config.run_after_each_pass ||
                        (assessor_config.run_finally && i == size - 1);
    bool run_type_checker = checker_conf.run_after_pass(pass);

    if (run_hasher || run_assessor || run_type_checker ||
        check_unique_deobfuscated.m_after_each_pass) {
      scope = build_class_scope(it);

      if (run_hasher) {
        m_current_pass_info->hash = boost::optional<hashing::DexHash>(
            this->run_hasher(pass->name().c_str(), scope));
      }
      if (run_assessor) {
        ::run_assessor(*this, scope);
        ScopedMetrics sm(*this);
        source_blocks::track_source_block_coverage(sm, stores);
      }
      if (run_type_checker) {
        // It's OK to overwrite the `this` register if we are not yet at the
        // output phase -- the register allocator can fix it up later.
        checker_conf.check_no_overwrite_this(false)
            .validate_access(false)
            .run_verifier(scope);
      }
      auto timer = m_check_unique_deobfuscateds_timer.scope();
      check_unique_deobfuscated.run_after_pass(pass, scope);
    }
    if (i >= min_pass_idx_for_dex_ref_check) {
      CheckerConfig::ref_validation(stores, pass->name());
    }
  };

  JNINativeContextHelper jni_native_context_helper(
      scope, m_redex_options.jni_summary_path);

  std::unordered_map<const Pass*, size_t> runs;

  /////////////////////
  // MAIN PASS LOOP. //
  /////////////////////
  for (size_t i = 0; i < m_activated_passes.size(); ++i) {
    Pass* pass = m_activated_passes[i];
    const size_t pass_run = ++runs[pass];
    AnalysisUsageHelper analysis_usage_helper{m_preserved_analysis_passes};
    analysis_usage_helper.pre_pass(pass);

    TRACE(PM, 1, "Running %s...", pass->name().c_str());
    ScopedVmHWM vm_hwm{hwm_pass_stats, hwm_per_pass};
    Timer t(pass->name() + " " + std::to_string(pass_run) + " (run)");
    m_current_pass_info = &m_pass_info[i];

    pre_pass_verifiers(pass, i);

    {
      auto scoped_command_prof = profiler_info_pass == pass
                                     ? ScopedCommandProfiling::maybe_from_info(
                                           profiler_info, &pass->name())
                                     : boost::none;
      auto scoped_command_all_prof = ScopedCommandProfiling::maybe_from_info(
          profiler_all_info, &pass->name());
      jemalloc_util::ScopedProfiling malloc_prof(m_malloc_profile_pass == pass);
      pass->run_pass(stores, conf, *this);
      trace_cls.dump(pass->name());
    }

    vm_hwm.trace_log(this, pass);

    sanitizers::lsan_do_recoverable_leak_check();

    graph_visualizer.add_pass(pass, i);

    post_pass_verifiers(pass, i, m_activated_passes.size());

    analysis_usage_helper.post_pass(pass);

    process_method_profiles(*this, conf);

    if (after_pass_size.handle(m_current_pass_info, &stores, &conf)) {
      // Measuring child. Return to write things out.
      break;
    }

    m_current_pass_info = nullptr;
  }

  after_pass_size.wait();

  // Always run the type checker before generating the optimized dex code.
  scope = build_class_scope(it);
  checker_conf.check_no_overwrite_this(get_redex_options().no_overwrite_this())
      .validate_access(true)
      .run_verifier(scope);

  jni_native_context_helper.post_passes(scope, conf);

  check_unique_deobfuscated.run_finally(scope);

  graph_visualizer.finalize();

  maybe_print_seeds_outgoing(conf, it);
  maybe_write_hashes_outgoing(conf, scope);

  sanitizers::lsan_do_recoverable_leak_check();

  Timer::add_timer("PassManager.Hashers", m_hashers_timer.get_seconds());
  Timer::add_timer("PassManager.CheckUniqueDeobfuscateds",
                   m_check_unique_deobfuscateds_timer.get_seconds());
  Timer::add_timer("CFGMutation", cfg::CFGMutation::get_seconds());
}