libredex/InteractiveDebugging.cpp (152 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "Macros.h" #if !defined(NDEBUG) && !IS_WINDOWS #include "InteractiveDebugging.h" #include "ControlFlow.h" #include "DexClass.h" #include "IRCode.h" #include "Show.h" #include "Trace.h" #include <stdarg.h> #include <stdio.h> #include <string.h> #include "TraceContextAccess.h" namespace { const char* g_dump_file = "stdout"; const char* g_open_mode = "a"; void write(const char* format, ...) { FILE* dump_file_ptr = stdout; bool not_stdout = strcmp(g_dump_file, "stdout") != 0; if (not_stdout) { FILE* tmp = fopen(g_dump_file, g_open_mode); if (!tmp) { printf("%s does not exist, writing to stdout\n", g_dump_file); } else { dump_file_ptr = tmp; } } va_list args; va_start(args, format); vfprintf(dump_file_ptr, format, args); va_end(args); if (not_stdout) { fclose(dump_file_ptr); } } const DexMethod* get_current_dex_method() { auto* trace_context = TraceContextAccess::get_s_context(); if (!trace_context) { return nullptr; } auto* dex_method_ref = trace_context->get_dex_method_ref(); if (!dex_method_ref) { write("No DexMethodRef set in current TraceContext\n"); return nullptr; } auto dex_method = dex_method_ref->as_def(); if (!dex_method) { write("DexMethodRef (%s) in current TraceContext is not a DexMethod\n", dex_method_ref->c_str()); return nullptr; } return dex_method; } const IRCode* get_current_ir_code() { auto dex_method = get_current_dex_method(); if (!dex_method) { return nullptr; } auto code = dex_method->get_code(); if (!code) { write("DexMethod (%s) has no IRCode\n", dex_method->c_str()); return nullptr; } return code; } class CFGHolder { public: explicit CFGHolder(const IRCode* ir_code); const cfg::ControlFlowGraph& get(); ~CFGHolder(); private: IRCode* m_ir_code; bool m_cfg_was_built; }; CFGHolder::CFGHolder(const IRCode* ir_code) : m_ir_code(const_cast<IRCode*>(ir_code)) { m_cfg_was_built = m_ir_code->cfg_built(); if (!m_cfg_was_built) { m_ir_code->build_cfg(true); } } const cfg::ControlFlowGraph& CFGHolder::get() { return m_ir_code->cfg(); } CFGHolder::~CFGHolder() { if (!m_cfg_was_built) { m_ir_code->clear_cfg(); } } } // namespace void dumpcfg(const cfg::ControlFlowGraph& cfg) { write("\n%s\n", show(cfg).c_str()); } void dumpcfg() { const IRCode* ir_code = get_current_ir_code(); if (!ir_code) { return; } CFGHolder cfgHolder(ir_code); const auto& cfg = cfgHolder.get(); dumpcfg(cfg); } void dumpblock(const cfg::Block* block) { write("\n%s\n", show(block).c_str()); } void dumpblock(cfg::BlockId block_id) { const IRCode* ir_code = get_current_ir_code(); if (!ir_code) { return; } CFGHolder cfgHolder(ir_code); const auto& cfg = cfgHolder.get(); const auto& blocks = cfg.blocks(); auto it = std::find_if(blocks.begin(), blocks.end(), [block_id](const auto* b) { return b->id() == block_id; }); if (it == blocks.end()) { return; } const auto* block = *it; dumpblock(block); } void dumpir(const IRCode* ir_code) { if (!ir_code) { return; } write("\n%s\n", show(ir_code).c_str()); } void dumpir() { const IRCode* ir_code = get_current_ir_code(); dumpir(ir_code); } void setdumpfile(const char* file_name) { g_dump_file = strdup(file_name); printf("Set dump file to %s\n", g_dump_file); } void setdumpfilemode(const char* mode) { if (strcmp(mode, "append") == 0 || strcmp(mode, "a") == 0) { g_open_mode = "a"; } else if (strcmp(mode, "truncate") == 0 || strcmp(mode, "w") == 0) { g_open_mode = "w"; } else { printf("Error setting dump file mode: argument, \"%s\", unrecognized\n", mode); } } const char* methname() { auto dex_method = get_current_dex_method(); if (!dex_method) { return ""; } return strdup(show(dex_method).c_str()); } void dumpmethname() { write("%s\n", methname()); } #endif