diff --git a/src/clients/drcctlib_instr_analysis/CMakeLists.txt b/src/clients/drcctlib_instr_analysis/CMakeLists.txt new file mode 100644 index 0000000..677218f --- /dev/null +++ b/src/clients/drcctlib_instr_analysis/CMakeLists.txt @@ -0,0 +1,71 @@ +# Forked from the CMakeLists.txt +# In drcctlib_instr_statistics +cmake_minimum_required(VERSION 2.6) + +include(../../make/policies.cmake NO_POLICY_SCOPE) + +if (UNIX) + add_compile_options(-Wno-deprecated) + add_compile_options(-Wno-unused-result) + add_compile_options(-Wno-unused-variable) + add_compile_options(-Wno-unused-local-typedefs) + add_compile_options(-Wno-unused-function) + add_compile_options(-Werror=sign-compare) + add_compile_options(-Werror=narrowing) + add_compile_options(-std=c++11) + + if (DEBUG) + add_compile_options(-g3) + endif (DEBUG) +endif (UNIX) + +# add third-party libraries + +add_library(drcctlib_instr_analysis SHARED +drcctlib_instr_analysis.cpp + ) + +configure_DynamoRIO_client(drcctlib_instr_analysis) +use_DynamoRIO_extension(drcctlib_instr_analysis drcctlib) +place_shared_lib_in_lib_dir(drcctlib_instr_analysis) + +add_dependencies(drcctlib_instr_analysis api_headers) + +# Provide a hint for how to use the client +if (NOT DynamoRIO_INTERNAL OR NOT "${CMAKE_GENERATOR}" MATCHES "Ninja") + add_custom_command(TARGET drcctlib_instr_analysis + POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E echo "Usage: pass to drconfig or drrun: -t drcctlib_instr_analysis" + VERBATIM) +endif () + +install_target(drcctlib_instr_analysis ${INSTALL_CLIENTS_LIB}) + +set(INSTALL_CCTTEST_CONFIG ${INSTALL_CLIENTS_BASE}) + +function (write_config_file dst bindir libdir) + file(WRITE ${dst} "# drcctlib_instr_analysis tool config file\n") + file(APPEND ${dst} "CLIENT_REL=${libdir}/${LIB_PFX}drcctlib_instr_analysis${LIB_EXT}\n") +endfunction () + +if (X64) + set(CONFIG_INSTALL ${PROJECT_BINARY_DIR}/drcctlib_instr_analysis.drrun64) + set(CONFIG_BUILD ${PROJECT_BINARY_DIR}/tools/drcctlib_instr_analysis.drrun64) +else (X64) + set(CONFIG_INSTALL ${PROJECT_BINARY_DIR}/drcctlib_instr_analysis.drrun32) + set(CONFIG_BUILD ${PROJECT_BINARY_DIR}/tools/drcctlib_instr_analysis.drrun32) +endif (X64) + +set(BUILD_CLIENTS_BIN clients/${INSTALL_BIN}) +set(BUILD_CLIENTS_LIB clients/${INSTALL_LIB}) + +write_config_file(${CONFIG_INSTALL} ${INSTALL_CLIENTS_BIN} ${INSTALL_CLIENTS_LIB}) +write_config_file(${CONFIG_BUILD} ${BUILD_CLIENTS_BIN} ${BUILD_CLIENTS_LIB}) + +DR_install(FILES "${CONFIG_INSTALL}" DESTINATION ${INSTALL_CCTTEST_CONFIG}) +register_tool_file("drcctlib_instr_analysis") + +################################################## +# Documentation + diff --git a/src/clients/drcctlib_instr_analysis/drcctlib_instr_analysis.cpp b/src/clients/drcctlib_instr_analysis/drcctlib_instr_analysis.cpp new file mode 100644 index 0000000..9b81a53 --- /dev/null +++ b/src/clients/drcctlib_instr_analysis/drcctlib_instr_analysis.cpp @@ -0,0 +1,346 @@ +/** + * @file drcctlib_instr_analysis.cpp + * @author Jacob Salzberg (jssalzbe@ncsu.edu) + * DrCCTProf instruction analysis client. + * Analyzes every instruction, + * grouping them into five groups: + * memory load, memory store, conditional branch, + * unconditional branch, and other. + * Prints the statistics per context to a file. + * Forked from drcctlib_instr_statistics.cpp + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dr_api.h" +#include "drmgr.h" +#include "dr_ir_instr.h" +#include "dr_ir_instrlist.h" +#include "dr_ir_utils.h" +#include "drcctlib.h" + +using namespace std; + +#define DRCCTLIB_PRINTF(format, args...) \ + DRCCTLIB_PRINTF_TEMPLATE("instr_statistics", format, ##args) +#define DRCCTLIB_EXIT_PROCESS(format, args...) \ + DRCCTLIB_CLIENT_EXIT_PROCESS_TEMPLATE("instr_statistics", format, ##args) + +#define MAX_CLIENT_CCT_PRINT_DEPTH 10 +#define TOP_REACH_NUM_SHOW 200 + +/** + * Thread local storage index + */ +static int tls_idx; + +/** + * The log file. + */ +static file_t gTraceFile; + +/** + * Per thread storage for counts of instruction kinds + */ +typedef struct _per_thread_t { + /** + * Map from the context handle to the time it first appears + */ + map *calling_contexts; + + /** + * Map from the context handle to the number of times it is called + */ + map *calling_contexts_called; + + /** + * Counters for the number of memory loads, + * indexed by calling context index + */ + vector *memloads; + + /** + * Counters for the number of memory stores, + * indexed by calling context index + */ + vector *memstores; + + /** + * Counters for the number of conditional branches, + * indexed by calling context index + */ + vector *branches; + + /** + * Counters for the number of unconditional branches, + * indexed by calling context index + */ + vector *jumps; + + /** + * Counters for the number of "other" instructions + * that do not fit the above categories + * indexed by calling context index + */ + vector *others; +} per_thread_t; + +/** + * For every basic block + * Iterate over the instruction list and count the + * number of branches, memory stores, memory loads, + * unconditional jumps + * and other instructions. + * @param drcontext the dynamorio context + * @param ctxt_hndl the context handle + * @param slot_num number of instructions (unused) + * @param mem_ref_num number of memory references (unused) + * @param mem_ref_start where the memory references start (unused) + * @param data additional data passed to this function (unused) + */ +static inline void +InstrumentPerBBCache(void *drcontext, context_handle_t ctxt_hndl, int32_t slot_num, + int32_t mem_ref_num, mem_ref_msg_t *mem_ref_start, void **data) +{ + per_thread_t *pt; + if (*data != NULL) { + pt = (per_thread_t *)*data; + } else { + pt = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx); + *data = pt; + } + + int index; + if (pt->calling_contexts->find(ctxt_hndl) == pt->calling_contexts->end()) { + (*(pt->calling_contexts))[ctxt_hndl] = pt->calling_contexts->size(); + pt->memloads->push_back(0); + pt->memstores->push_back(0); + pt->branches->push_back(0); + pt->jumps->push_back(0); + pt->others->push_back(0); + } + index = (*(pt->calling_contexts))[ctxt_hndl]; + + if (pt->calling_contexts_called->find(ctxt_hndl) == + pt->calling_contexts_called->end()) { + (*(pt->calling_contexts_called))[ctxt_hndl] = 0; + } + (*(pt->calling_contexts_called))[ctxt_hndl] += 1; + + context_t *ctxt = drcctlib_get_full_cct(ctxt_hndl, 1); + byte *ip = ctxt->ip; + int memload_count = 0; + int memstore_count = 0; + int branch_count = 0; + int jump_count = 0; + int other_count = 0; + if (ip) { + instrlist_t *bb = decode_as_bb(drcontext, ip); + instr_t *next; + for (instr_t *instr = instrlist_first_app(bb); + instr != instrlist_last_app(bb) && instr != NULL; instr = next) { + if (instr_is_cbr(instr)) { + branch_count += 1; + } else if (instr_writes_memory(instr)) { + memstore_count += 1; + } else if (instr_reads_memory(instr)) { + memload_count += 1; + } else { + other_count += 1; + } + next = instr_get_next_app(instr); + } + + // Only check the last application for unconditional jumps. + // This is because unconditional jumps end a branch + if (instr_is_ubr(instrlist_last_app(bb))) { + jump_count += 1; + } + if (instr_is_cbr(instrlist_last_app(bb))) { + branch_count += 1; + } + instrlist_clear_and_destroy(drcontext, bb); + } + + pt->memloads->at(index) = memload_count; + pt->memstores->at(index) = memstore_count; + pt->branches->at(index) = branch_count; + pt->jumps->at(index) = jump_count; + pt->others->at(index) = other_count; +} + +/** + * Initialize the tool. + * @param argc The tool's argc + * @param argv The tool's argv + */ +static void +ClientInit(int argc, const char *argv[]) +{ +#ifdef ARM_CCTLIB + char name[MAXIMUM_PATH] = "arm.drcctlib_instr_statistics.out."; +#else + char name[MAXIMUM_PATH] = "x86.drcctlib_instr_statistics.out."; +#endif + char *envPath = getenv("DR_CCTLIB_CLIENT_OUTPUT_FILE"); + + if (envPath) { + // assumes max of MAXIMUM_PATH + strcpy(name, envPath); + } + + gethostname(name + strlen(name), MAXIMUM_PATH - strlen(name)); + pid_t pid = getpid(); + sprintf(name + strlen(name), "%d", pid); + cerr << "Creating log file at:" << name << endl; + + gTraceFile = dr_open_file(name, DR_FILE_WRITE_OVERWRITE | DR_FILE_ALLOW_LARGE); + + DR_ASSERT(gTraceFile != INVALID_FILE); + // print the arguments passed + dr_fprintf(gTraceFile, "\n"); + + for (int i = 0; i < argc; i++) { + dr_fprintf(gTraceFile, "%d %s ", i, argv[i]); + } + + dr_fprintf(gTraceFile, "\n"); +} + +/** + * Record the statistics to a file + */ +static void +ClientExit(void) +{ + drcctlib_exit(); + dr_close_file(gTraceFile); +} + +/** + * Record the statistics to a file + */ +static void +ClientThreadEnd(void *drcontext) +{ + per_thread_t *pt = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx); + for (std::pair element : *(pt->calling_contexts)) { + int i = element.second; + context_handle_t cct_hndl = element.first; + int no = i + 1; + int memload_count = pt->memloads->at(i); + int memstore_count = pt->memstores->at(i); + int branch_count = pt->branches->at(i); + int jump_count = pt->jumps->at(i); + int other_count = pt->others->at(i); + int times_called = (*(pt->calling_contexts_called))[cct_hndl]; + dr_fprintf(gTraceFile, + "NO. %d" + " loads (%02d)," + " store (%02d)," + " conditional branch (%02d)," + " unconditional branch (%02d)" + " other: (%02d)" + " ins call number %d" + " context handle %d" + "====", + no, memload_count, memstore_count, branch_count, jump_count, + other_count, times_called, cct_hndl); + drcctlib_print_ctxt_hndl_msg(gTraceFile, cct_hndl, false, false); + dr_fprintf(gTraceFile, + "===============================================" + "======================" + "===========\n"); + drcctlib_print_full_cct(gTraceFile, cct_hndl, true, false, + MAX_CLIENT_CCT_PRINT_DEPTH); + dr_fprintf(gTraceFile, + "===============================================" + "======================" + "===========\n\n\n"); + } + + delete pt->calling_contexts; + delete pt->calling_contexts_called; + delete pt->memloads; + delete pt->memstores; + delete pt->branches; + delete pt->jumps; + delete pt->others; + + dr_thread_free(drcontext, pt, sizeof(per_thread_t)); +} + +/** + * Initialize per thread storage + */ +static void +ClientThreadStart(void *drcontext) +{ + per_thread_t *pt = (per_thread_t *)dr_thread_alloc(drcontext, sizeof(per_thread_t)); + if (pt == NULL) { + DRCCTLIB_EXIT_PROCESS("pt == NULL"); + } + drmgr_set_tls_field(drcontext, tls_idx, (void *)pt); + + pt->calling_contexts = new map; + pt->calling_contexts_called = new map; + pt->memloads = new vector; + pt->memstores = new vector; + pt->branches = new vector; + pt->jumps = new vector; + pt->others = new vector; +} + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Main entry point for tool + * @param id the client id: + * @param argc the argument count + * @param argv the argument list + */ +DR_EXPORT void +dr_client_main(client_id_t id, int argc, const char *argv[]) +{ + dr_set_client_name("DynamoRIO Client 'drcctlib_instr_analysis'", + "http://dynamorio.org/issues"); + + ClientInit(argc, argv); + if (!drmgr_init()) { + DRCCTLIB_EXIT_PROCESS( + "ERROR: drcctlib_reuse_distance unable to initialize drmgr"); + } + drmgr_priority_t thread_init_pri = { sizeof(thread_init_pri), + "drcctlib_reuse-thread_init", NULL, NULL, + DRCCTLIB_THREAD_EVENT_PRI + 1 }; + drmgr_priority_t thread_exit_pri = { sizeof(thread_exit_pri), + "drcctlib_reuse-thread-exit", NULL, NULL, + DRCCTLIB_THREAD_EVENT_PRI + 1 }; + drmgr_register_thread_init_event_ex(ClientThreadStart, &thread_init_pri); + drmgr_register_thread_exit_event_ex(ClientThreadEnd, &thread_exit_pri); + + drcctlib_init_ex(DRCCTLIB_FILTER_ALL_INSTR, INVALID_FILE, NULL, NULL, + InstrumentPerBBCache, DRCCTLIB_CACHE_MODE); + dr_register_exit_event(ClientExit); + tls_idx = drmgr_register_tls_field(); + if (tls_idx == -1) { + DRCCTLIB_EXIT_PROCESS( + "ERROR: drcctlib_reuse_distance drmgr_register_tls_field fail"); + } +} + +#ifdef __cplusplus +} +#endif