void PassImpl::run_pass(DexStoresVector& stores, ConfigFiles& config, PassManager& mgr) { if (m_config.create_runtime_asserts) { m_config.runtime_assert = RuntimeAssertTransform::Config(config.get_proguard_map()); } auto scope = build_class_scope(stores); run(scope); mgr.incr_metric("branches_removed", m_transform_stats.branches_removed); mgr.incr_metric("materialized_consts", m_transform_stats.materialized_consts); mgr.incr_metric("constant_fields", m_stats.constant_fields); mgr.incr_metric("constant_methods", m_stats.constant_methods); }
void StaticSinkPass::run_pass(DexClassesVector& dexen, ConfigFiles& cfg) { auto method_list = cfg.get_coldstart_methods(); auto methods = strings_to_dexmethods(method_list); TRACE(SINK, 1, "methods used in coldstart: %lu\n", methods.size()); auto coldstart_classes = get_coldstart_classes(dexen, cfg); count_coldstart_statics(coldstart_classes); auto statics = get_noncoldstart_statics(coldstart_classes, methods); TRACE(SINK, 1, "statics not used in coldstart: %lu\n", statics.size()); remove_primary_dex_refs(dexen[0], statics); TRACE(SINK, 1, "statics after removing primary dex: %lu\n", statics.size()); auto sink_map = get_sink_map(dexen, coldstart_classes, statics); TRACE(SINK, 1, "statics with sinkable callsite: %lu\n", sink_map.size()); auto holder = move_statics_out(statics, sink_map); TRACE(SINK, 1, "methods in static holder: %lu\n", holder->get_dmethods().size()); DexClasses dc(1); dc.insert_at(holder, 0); dexen.emplace_back(std::move(dc)); }
/* * Format: array of "Lpkg/Class;" */ std::unordered_set<DexType*> keep_class_member_annos( const std::vector<std::string>& annos, ConfigFiles& cfg ) { std::unordered_set<DexType*> keep; for (const auto& anno : cfg.get_no_optimizations_annos()) { keep.emplace(anno); } try { for (auto const& keep_anno : annos) { auto type = DexType::get_type(DexString::get_string(keep_anno.c_str())); if (type != nullptr) { keep.emplace(type); } } } catch (...) { // Swallow exception if the field isn't there. } return keep; }
void AddRedexTxtToApkPass::run_pass(DexStoresVector& /* unused */, ConfigFiles& conf, PassManager& /* unused */) { std::string apk_dir; conf.get_json_config().get("apk_dir", "", apk_dir); if (!apk_dir.size()) { TRACE(ADD_REDEX_TXT, 2, "apk_dir not set, so not writing redex.txt\n"); return; } std::string out_file_name = apk_dir + "/redex.txt"; std::ofstream redex_txt(out_file_name); if (redex_txt.is_open()) { redex_txt << "Optimized by Redex" << std::endl << "http://fbredex.com/" << std::endl; redex_txt.close(); } else { opt_warn(CANT_WRITE_FILE, "Unable to write file %s\n", out_file_name.c_str()); } }
void PassManager::run_passes(DexClassesVector& dexen, ConfigFiles& cfg) { init_reachable_classes(build_class_scope(dexen), m_config, m_proguard_rules, cfg.get_no_optimizations_annos()); Scope scope = build_class_scope(dexen); // reportReachableClasses(scope, "reachable"); for (auto pass : m_activated_passes) { using namespace std::chrono; TRACE(PM, 1, "Running %s...\n", pass->name().c_str()); m_current_pass_metrics = &m_pass_metrics[pass->name()]; auto start = high_resolution_clock::now(); if (pass->assumes_sync()) { MethodTransform::sync_all(); } pass->run_pass(dexen, cfg, *this); auto end = high_resolution_clock::now(); TRACE(PM, 1, "Pass %s completed in %.1lf seconds\n", pass->name().c_str(), duration<double>(end - start).count()); m_current_pass_metrics = nullptr; } MethodTransform::sync_all(); }
void RenameClassesPass::run_pass(DexStoresVector& stores, ConfigFiles& cfg, PassManager& mgr) { const JsonWrapper& json_cfg = cfg.get_json_config(); if (json_cfg.get("emit_name_based_locators", false)) { // TODO: Purge the old RenameClassesPass entirely everywhere. fprintf(stderr, "[RenameClassesPass] error: Configuration option " "emit_locator_strings is not compatible with RenameClassesPass. " "Upgrade to RenameClassesPassV2 instead.\n"); exit(EXIT_FAILURE); } auto scope = build_class_scope(stores); ClassHierarchy ch = build_type_hierarchy(scope); std::unordered_set<const DexType*> untouchables; for (const auto& base : m_untouchable_hierarchies) { auto base_type = DexType::get_type(base.c_str()); if (base_type != nullptr) { untouchables.insert(base_type); TypeSet children; get_all_children(ch, base_type, children); untouchables.insert(children.begin(), children.end()); } } mgr.incr_metric(METRIC_CLASSES_IN_SCOPE, scope.size()); rename_classes( scope, m_pre_filter_whitelist, m_post_filter_whitelist, untouchables, m_rename_annotations, mgr); TRACE(RENAME, 1, "renamed classes: %d anon classes, %d from single char patterns, " "%d from multi char patterns\n", match_inner, match_short, match_long); TRACE(RENAME, 1, "String savings, at least %d bytes \n", base_strings_size - ren_strings_size); }
void RenameClassesPass::run_pass(DexClassesVector& dexen, ConfigFiles& cfg) { auto scope = build_class_scope(dexen); std::unordered_set<const DexType*> untouchables; for (const auto& base : m_untouchable_hierarchies) { auto base_type = DexType::get_type(base.c_str()); if (base_type != nullptr) { untouchables.insert(base_type); TypeVector children; get_all_children(base_type, children); untouchables.insert(children.begin(), children.end()); } } rename_classes( scope, m_pre_filter_whitelist, m_post_filter_whitelist, m_path, untouchables, cfg.get_proguard_map(), m_rename_annotations); TRACE(RENAME, 1, "renamed classes: %d anon classes, %d from single char patterns, " "%d from multi char patterns\n", match_inner, match_short, match_long); TRACE(RENAME, 1, "String savings, at least %d bytes \n", base_strings_size - ren_strings_size); }
void RemoveUnreachablePass::run_pass(DexStoresVector& stores, ConfigFiles& conf, PassManager& pm) { // Store names of removed classes and methods ConcurrentSet<std::string> removed_symbols; if (pm.no_proguard_rules()) { TRACE(RMU, 1, "RemoveUnreachablePass not run because no " "ProGuard configuration was provided."); return; } bool output_unreachable_symbols = pm.get_current_pass_info()->repeat == 0 && !m_unreachable_symbols_file_name.empty(); int num_ignore_check_strings = 0; auto reachables = reachability::compute_reachable_objects( stores, m_ignore_sets, &num_ignore_check_strings); reachability::ObjectCounts before = reachability::count_objects(stores); TRACE(RMU, 1, "before: %lu classes, %lu fields, %lu methods\n", before.num_classes, before.num_fields, before.num_methods); reachability::sweep(stores, *reachables, output_unreachable_symbols ? &removed_symbols : nullptr); reachability::ObjectCounts after = reachability::count_objects(stores); TRACE(RMU, 1, "after: %lu classes, %lu fields, %lu methods\n", after.num_classes, after.num_fields, after.num_methods); pm.incr_metric("num_ignore_check_strings", num_ignore_check_strings); pm.incr_metric("classes_removed", before.num_classes - after.num_classes); pm.incr_metric("fields_removed", before.num_fields - after.num_fields); pm.incr_metric("methods_removed", before.num_methods - after.num_methods); if (output_unreachable_symbols) { std::string filepath = conf.metafile(m_unreachable_symbols_file_name); write_out_removed_symbols(filepath, removed_symbols); } }
void PassManager::run_passes(DexStoresVector& stores, ConfigFiles& cfg) { DexStoreClassesIterator it(stores); Scope scope = build_class_scope(it); { Timer t("API Level Checker"); api::LevelChecker::init(m_redex_options.min_sdk, scope); } char* seeds_output_file = std::getenv("REDEX_SEEDS_FILE"); if (seeds_output_file) { std::string seed_filename = seeds_output_file; Timer t("Writing seeds file " + seed_filename); std::ofstream seeds_file(seed_filename); redex::print_seeds(seeds_file, cfg.get_proguard_map(), scope, false, false); } if (!cfg.get_printseeds().empty()) { Timer t("Writing seeds to file " + cfg.get_printseeds()); std::ofstream seeds_file(cfg.get_printseeds()); redex::print_seeds(seeds_file, cfg.get_proguard_map(), scope); std::ofstream config_file(cfg.get_printseeds() + ".pro"); redex::show_configuration(config_file, scope, *m_pg_config); std::ofstream incoming(cfg.get_printseeds() + ".incoming"); redex::print_classes(incoming, cfg.get_proguard_map(), scope); std::ofstream shrinking_file(cfg.get_printseeds() + ".allowshrinking"); redex::print_seeds(shrinking_file, cfg.get_proguard_map(), scope, true, false); std::ofstream obfuscation_file(cfg.get_printseeds() + ".allowobfuscation"); redex::print_seeds(obfuscation_file, cfg.get_proguard_map(), scope, false, true); } // Enable opt decision logging if specified in config. const Json::Value& opt_decisions_args = cfg.get_json_config()["opt_decisions"]; if (opt_decisions_args.get("enable_logs", false).asBool()) { opt_metadata::OptDataMapper::get_instance().enable_logs(); } // TODO(fengliu) : Remove Pass::eval_pass API for (size_t i = 0; i < m_activated_passes.size(); ++i) { Pass* pass = m_activated_passes[i]; TRACE(PM, 1, "Evaluating %s...\n", pass->name().c_str()); Timer t(pass->name() + " (eval)"); m_current_pass_info = &m_pass_info[i]; pass->eval_pass(stores, cfg, *this); m_current_pass_info = nullptr; } // Retrieve the type checker's settings. const Json::Value& type_checker_args = cfg.get_json_config()["ir_type_checker"]; bool run_after_each_pass = type_checker_args.get("run_after_each_pass", false).asBool(); // When verify_none is enabled, it's OK to have polymorphic constants. bool polymorphic_constants = type_checker_args.get("polymorphic_constants", false).asBool() || get_redex_options().verify_none_enabled; bool verify_moves = type_checker_args.get("verify_moves", false).asBool(); bool check_no_overwrite_this = type_checker_args.get("check_no_overwrite_this", false).asBool(); std::unordered_set<std::string> trigger_passes; for (auto& trigger_pass : type_checker_args["run_after_passes"]) { trigger_passes.insert(trigger_pass.asString()); } for (size_t i = 0; i < m_activated_passes.size(); ++i) { Pass* pass = m_activated_passes[i]; TRACE(PM, 1, "Running %s...\n", pass->name().c_str()); Timer t(pass->name() + " (run)"); m_current_pass_info = &m_pass_info[i]; { ScopedCommandProfiling cmd_prof( m_profiler_info && m_profiler_info->pass == pass ? boost::make_optional(m_profiler_info->command) : boost::none); jemalloc_util::ScopedProfiling malloc_prof(m_malloc_profile_pass == pass); pass->run_pass(stores, cfg, *this); } if (run_after_each_pass || trigger_passes.count(pass->name()) > 0) { scope = build_class_scope(it); // 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. run_type_checker(scope, polymorphic_constants, verify_moves, /* check_no_overwrite_this */ false); } m_current_pass_info = nullptr; } // Always run the type checker before generating the optimized dex code. scope = build_class_scope(it); run_type_checker(scope, polymorphic_constants, verify_moves, check_no_overwrite_this); if (!cfg.get_printseeds().empty()) { Timer t("Writing outgoing classes to file " + cfg.get_printseeds() + ".outgoing"); // Recompute the scope. scope = build_class_scope(it); std::ofstream outgoing(cfg.get_printseeds() + ".outgoing"); redex::print_classes(outgoing, cfg.get_proguard_map(), scope); } }
void ShortenSrcStringsPass::run_pass(DexStoresVector& stores, ConfigFiles& conf, PassManager& mgr) { m_filename_mappings = conf.metafile(m_filename_mappings); strip_src_strings(stores, m_filename_mappings.c_str(), mgr); }