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());
}