[DynamoRIO-工具]调用链与服务内部函数调用跟踪

· · 个人记录

本文承接《对C++服务基于JIT模式的二进制无侵入式动态插桩研究》相关内容,在前者基础上重构完善了部分内容,修复了一些BUG

已知BUGdrwrap拓展对函数进行前后插桩时会出现回调延迟。如A顺序调用BCCpre hook回调可能会在Bpost hook回调前调用。这种情况暂时只在同一位置的第二次及之后调用中出现,初步猜测原因是code cache建立完成后的乱序执行。

代码解析

1. Trace,Span记录处理部分

概念说明:trace表示一次有意义的服务调用,span表示服务内部调用的需要追踪的函数。

这部分代码主要集中在前半部分含有tracespan字样的函数与类中,功能是通过栈模拟被插桩程序的函数栈,实现对函数调用关系的记录。

trace启动由trace_post_hook回调触发,其绑定在指定的服务调用触发函数上,当该函数触发时启动cur_trace

当插桩程序进入需要追踪的某函数前,function_call_pre_hook回调函数被触发,检测当前是否有cur_trace,存在时开启栈模拟。

需要追踪的某函数返回后,function_call_post_hook回调函数被触发,在有cur_trace追踪时,弹出栈中函数。同时当模拟栈空时,意味着一次追踪结束,function_call_post_hook负责调用print_trace打印cur_trace,并做清理。

2. instr辅助函数部分

// 设置instr的application pc
static instr_t*
set_app_instr(instr_t* &instr, app_pc pc);

// instr插入辅助函数
static void
insert_instr_before_where(IN instrlist_t* bb, IN instr_t* where, IN instr_t* instr);

// instr插入辅助函数
static void
insert_instr_after_where(IN instrlist_t* bb, IN instr_t* where, IN instr_t* instr);

// 在where前保存RBP与RSP,模拟函数调用时的push压栈与修改栈底
static void
save_stack_status(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc);

// 在where前平栈,模拟函数返回时的pop弹栈与修改栈底
static void
reset_stack_status(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc);

// 在where前使用push压栈,保存所有的X64通用寄存器(除RBP和RSP)。注意本函数不保存状态寄存器和浮点寄存器等
static void 
save_all_register(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc);

// 在where前使用pop弹出保存的所有X64通用寄存器(除RBP和RSP)
static void 
reset_all_register(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc);

// 删除where后所有instr(不包括where)
static void 
clear_instr_from(IN void *drcontext, IN instrlist_t *bb, IN instr_t *where);

// 获得__cdecl调用约定下第index个参数存放的寄存器
static reg_id_t 
get_pass_param_reg(IN int index);

// 函数调用前传递参数,本函数假定传递参数为void*字面立即值
static void
pass_param(IN void *drcontext, IN instrlist_t *bb, IN instr_t *where, IN app_pc pc, IN int index, IN void *param);

// 插入函数调用,本函数假定传递参数为void*字面立即值
static void
insert_application_function_call(IN void *drcontext, IN instrlist_t *bb, IN instr_t *where, IN app_pc instr_pc, IN app_pc insert_function, IN int param_count IN...);

3. basic_block_app2app_callback

dr_fragment_app_pc(tag)可以获取当前基本块的Application地址。

这里假定函数开头保存了原基本块的三条指令,分别是noppush RBPmov RBP RSP,即保存栈状态后插入函数调用。

where_pc表示插桩代码转换翻译的Application地址,需要保证在当前基本块范围内,且除非需要跳过后续指令,需要保证where_pc在插入位置的下一条指令前,即instr_get_app_pc(where) < where_pc <= instr_get_app_pc(instr_get_next(where))next_pc表示下一次基本块的开始地址,用来进行后续插桩位置标定。

这里采用状态机依次插入所需代码,where_pc每次自增保证不同基本块开头Application地址不同,避免函数调用返回位置无法区分不同基本块。

Client源码

#include "dr_api.h"
#include "drmgr.h"
#include "drsyms.h"
#include "drwrap.h"
#include "droption.h"
#include "drcallstack.h"
#include <cstdio>
#include <cstring>
#include <cstdarg>
#include <string>
#include <sstream>
#include <iostream>
#include <stack>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>

using std::string;
using std::vector;
using std::stack;
using std::unordered_map;
using std::unordered_set;

using dynamorio::droption::DROPTION_SCOPE_CLIENT;
using dynamorio::droption::droption_parser_t;
using dynamorio::droption::droption_t;

static droption_t<string> g_log_file(DROPTION_SCOPE_CLIENT, "f", R"(/DynamoRIO/log/client.log)", "log file location", "log file location");
static int g_log_file_fd;

using span_id_t = string;
using trace_id_t = string;

class trace;
class span;

class trace {
public :
  ~trace();

  thread_id_t thread_id_;
  trace_id_t trace_id_;
  span* enter_span_;
};

class span {
public:
  ~span();

  void push_new_callee(span* const callee);

  trace* parent_trace_;
  span* parent_span_;

  span_id_t span_id_;

  string caller_name_;
  vector<void*> arg_;
  void* ret_val_;

  vector<span*> callee_list_;
};

trace::~trace() {
  delete enter_span_;
}

span::~span() {
  for (auto callee : callee_list_) {
    delete callee;
  }
}

void span::push_new_callee(span* const callee) {
  callee_list_.push_back(callee);
  callee->parent_trace_ = parent_trace_;
  callee->parent_span_ = this;
}

struct traced_function_info {
  string name;
  app_pc pc;
  app_pc start;
  app_pc end;
};

struct trace_module_info {
  string name;
  app_pc start;
  unordered_map<string, traced_function_info*> traced_function;
};

// temp use
string instancelib = "Application.out";
string instancefunc = "A::getInstance";

string tracelib = "Application.out";
string tracefunc = "_ZNK1A10getTraceIDB5cxx11Ev";

string instrumentlib = "Application.out";
string enter_function = "enter_function";

unordered_set<string> whiteListModule { instancelib, tracelib, instrumentlib };

unordered_map<string, trace_module_info*> trace_module;

static bool
enumerate_symbols(drsym_info_t *info, drsym_error_t status, void *data) {
  if (info->name == nullptr) return true;

  string module_file_name((char*)data);
  app_pc module_start = trace_module[module_file_name]->start;

  string func_name(info->name);

  trace_module[module_file_name]-> traced_function[info->name] = 
    new traced_function_info{ info->name, module_start + info->start_offs, module_start + info->start_offs, module_start + info->end_offs };

  return true;
}

trace* cur_trace;
stack<span*> func_call_stack;

static void 
print_span(span* s, string pre_space) {
  dr_fprintf(g_log_file_fd, "%s[function]: %s\n", pre_space.c_str(), s->caller_name_.c_str());
  dr_fprintf(g_log_file_fd, "%s[span]: %s\n", pre_space.c_str(), s->span_id_.c_str());
  if (s->callee_list_.empty()) {
    return;
  }
  pre_space += " |  ";
  for (auto callee : s->callee_list_) {
    print_span(callee, pre_space);
  }
}

static void 
print_trace(trace* t) {
  if (t == nullptr) {
    return;
  }

  dr_fprintf(g_log_file_fd, "[thread]: %d\n", t->thread_id_);
  dr_fprintf(g_log_file_fd, "[trace]: %s\n", t->trace_id_.c_str());
  print_span(t->enter_span_, " |  ");
}

static string
generate_span() {
  string span;
  for (int i = 0; i < 10; ++i) {
    span.push_back(dr_get_random_value(10) + '0');
  }
  return span;
}

static void 
function_call_pre_hook(void* wrapcxt, void** user_data) {
  // dr_fprintf(STDERR, "function pre hook: [%s]\n", *(char**)user_data);
  if (cur_trace != nullptr) {
    span* s = new span;
    s->span_id_ = generate_span();
    if (!func_call_stack.empty()) {
      s->parent_span_ = func_call_stack.top();
      s->parent_span_->push_new_callee(s);
    } else {
      s->parent_span_ = nullptr;
      cur_trace->enter_span_ = s;
    }
    s->caller_name_ = string(*(char**)user_data);
    s->parent_trace_ = cur_trace;
    func_call_stack.push(s);
  }
}

static void
function_call_post_hook(void* wrapcxt, void* user_data) {
  // dr_fprintf(STDERR, "function post hook: [%s]\n", (char*)user_data);
  if (cur_trace != nullptr) {
    if (func_call_stack.empty())
      return;
    func_call_stack.top()->ret_val_ = drwrap_get_retval(wrapcxt);
    func_call_stack.pop();
    if (func_call_stack.empty()) {
      print_trace(cur_trace);
      delete cur_trace;
      cur_trace = nullptr;
    }
  }
}

static void
trace_pre_hook(void* wrapcxt, void** user_data) { }

static void
trace_post_hook(void* wrapcxt, void* user_data) {
  string* x = (string*)drwrap_get_retval(wrapcxt);

  if (x == nullptr || x->empty()) {
    return;
  }

  if (cur_trace == nullptr) {
    cur_trace = new trace;
    cur_trace->thread_id_ = dr_get_thread_id(drwrap_get_drcontext(wrapcxt));
    cur_trace->trace_id_ = *x;
  }
}

static void
module_load(void* drcontext, const module_data_t* info, bool loaded) {
  if (info->names.file_name == nullptr) {
    return;
  }
  string module_file_name(info->names.file_name);
  if (whiteListModule.find(module_file_name) == whiteListModule.end()) {
    return;
  }

  trace_module[module_file_name] = new trace_module_info{module_file_name, info->start};
  drsym_enumerate_symbols_ex(info->full_path, enumerate_symbols, sizeof(drsym_info_t), (void*)info->names.file_name, DRSYM_DEMANGLE);

  if (module_file_name == instrumentlib) {
    for (const auto& trace_func : trace_module[module_file_name]->traced_function) {
      if (trace_func.first == enter_function || trace_func.first == tracefunc || trace_func.first == instancefunc) 
        continue;
      if (trace_func.first != "a" && trace_func.first != "b" && trace_func.first != "c" && trace_func.first != "d") 
        continue;
      drwrap_wrap_ex(trace_func.second->pc,
                    function_call_pre_hook,
                    function_call_post_hook,
                    (void*)trace_func.first.c_str(),
                    0);
    }
  }
  if (module_file_name == tracelib) {
    drwrap_wrap_ex(trace_module[module_file_name]->traced_function[tracefunc]->pc, 
                   trace_pre_hook,
                   trace_post_hook,
                   nullptr,
                   0);
  }
}

static void
print_instrlist(void* drcontext, instrlist_t* bb) {
  for (auto instr = instrlist_first(bb); instr != nullptr; instr = instr_get_next(instr)) {
    dr_print_instr(drcontext, STDERR, instr, "");
    // dr_print_instr(drcontext, g_log_file_fd, instr, "");
  }
  dr_fprintf(STDERR, "\n");
  // dr_fprintf(g_log_file_fd, "\n");
}

static instr_t*
set_app_instr(instr_t* &instr, app_pc pc) {
  instr_set_app(instr);
  instr_set_translation(instr, pc);
  return instr;
}

static void
insert_instr_before_where(IN instrlist_t* bb, IN instr_t* where, IN instr_t* instr) {
  if (where == nullptr) {
    instrlist_append(bb, instr);
  } else {
    instrlist_preinsert(bb, where, instr);
  }
}

static void
insert_instr_after_where(IN instrlist_t* bb, IN instr_t* where, IN instr_t* instr) {
  if (where == nullptr) {
    instrlist_prepend(bb, instr);
  } else {
    instrlist_postinsert(bb, where, instr);
  }
}

static void
save_stack_status(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc) {
  instr_t* instr = nullptr;
#ifdef X86
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RBP));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = XINST_CREATE_move(drcontext, opnd_create_reg(DR_REG_RBP), opnd_create_reg(DR_REG_RSP));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
#elif defined(AARCHXX)
#endif
}

static void
reset_stack_status(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc) {
  instr_t* instr = nullptr;
#ifdef X86
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RBP));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
#elif defined(AARCHXX)
#endif
}

static void 
save_all_register(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc) {
  instr_t* instr = nullptr;
#ifdef X64
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RAX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RCX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RDX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RBX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RSI));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_RDI));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R8));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R9));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R10));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R11));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R12));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R13));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R14));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_push(drcontext, opnd_create_reg(DR_REG_R15));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
#elif defined(X86)
#elif defined(AARCHXX)
#endif
}

static void 
reset_all_register(IN void* drcontext, IN instrlist_t* bb, IN instr_t* where, IN app_pc pc) {
  instr_t* instr = nullptr;
#ifdef X64
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R15));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R14));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R13));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R12));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R11));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R10));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R9));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_R8));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RDI));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RSI));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RBX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RDX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RCX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
  instr = INSTR_CREATE_pop(drcontext, opnd_create_reg(DR_REG_RAX));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
#elif defined(X86)
#elif defined(AARCHXX)
#endif
}

static void 
clear_instr_from(IN void *drcontext, IN instrlist_t *bb, IN instr_t *where) {
  if (where == nullptr) {
    instrlist_clear(drcontext, bb);
  } else {
    for (auto del = instr_get_next(where); del != nullptr; del = instr_get_next(where)) {
      instrlist_remove(bb, del);
      instr_destroy(drcontext, del);
    }
  }
}

static reg_id_t 
get_pass_param_reg(IN int index) {
  switch (index) {
  case 1:
    return DR_REG_RDI;
  case 2:
    return DR_REG_RSI;
  case 3:
    return DR_REG_RDX;
  case 4:
    return DR_REG_RCX;
  case 5:
    return DR_REG_R8;
  case 6:
    return DR_REG_R9;
  default:
    return DR_REG_INVALID;
  }
}

static void
pass_param(IN void *drcontext, IN instrlist_t *bb, IN instr_t *where, IN app_pc pc, IN int index, IN void *param) {
# ifdef X86
  instr_t* instr;
# ifdef X64
  if (index <= 6) {
    instr = INSTR_CREATE_mov_imm(drcontext, opnd_create_reg(get_pass_param_reg(index)), OPND_CREATE_INTPTR(param));
    insert_instr_before_where(bb, where, set_app_instr(instr, pc));
    return;
  }
# endif
  instr = INSTR_CREATE_push_imm(drcontext, OPND_CREATE_INT32(param));
  insert_instr_before_where(bb, where, set_app_instr(instr, pc));
# endif
}

static void
insert_application_function_call(IN void *drcontext, IN instrlist_t *bb, IN instr_t *where, IN app_pc instr_pc, IN app_pc insert_function, IN int param_count IN...) {
  // save_stack_status(drcontext, bb, where, instr_pc);
  save_all_register(drcontext, bb, where, instr_pc);
  va_list args;
  va_start(args, param_count);
  for (int i = 1; i <= param_count; ++i) {
    void* arg = va_arg(args, void*);
    pass_param(drcontext, bb, where, instr_pc, i, arg);
  }
  va_end(args);
  instr_t* instr = XINST_CREATE_call(drcontext, opnd_create_pc(insert_function));
  insert_instr_before_where(bb, where, set_app_instr(instr, instr_pc));
}

static bool 
need_instrument(app_pc pc) {
  if (trace_module.find(instrumentlib) == trace_module.end() || 
      trace_module[instancelib]->traced_function.find(enter_function) == trace_module[instancelib]->traced_function.end()) {
    return false;
  }
  return pc == trace_module[instrumentlib]->traced_function[enter_function]->pc;
  // for (const auto& func : trace_module[instrumentlib]->traced_function) {
  //   if (pc == func.second->pc) {
  //     return true;
  //   }
  // }
  // return true;

  // // old version, may be usefully
  // for (const auto& module : trace_module) {
  //   if (module.first != instrumentlib) {
  //     continue;
  //   }
  //   for (const auto& func : module.second->traced_function) {
  //     if (pc == func.second->pc) {
  //       return true;
  //     }
  //   }
  // }
  // return false;
}

static dr_emit_flags_t
basic_block_app2app_callback(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) {
  static app_pc where_pc = nullptr;
  static app_pc next_pc = nullptr;
  static unsigned status = 0;

  static string AinstanceString("http");
  AinstanceString = "http";

  instr_t *call = nullptr;

  if (next_pc == nullptr && need_instrument(dr_fragment_app_pc(tag))) {

    // dr_fprintf(STDERR, "translating: %d\n", translating);
    // dr_fprintf(STDERR, "original bb:\n");
    // print_instrlist(drcontext, bb);

    instr_t *first = instrlist_first(bb);
    instr_t *where = instr_get_next(instr_get_next(first));
    where_pc = instr_get_app_pc(where) + 1;
    next_pc = instr_get_app_pc(instr_get_next(where));

    clear_instr_from(drcontext, bb, where);

    insert_application_function_call(drcontext, bb, nullptr, where_pc, trace_module[instancelib]->traced_function[instancefunc]->pc,
      1, &AinstanceString);

    // dr_fprintf(STDERR, "status 0:\n");
    // print_instrlist(drcontext, bb);

    return DR_EMIT_STORE_TRANSLATIONS;
  } else if (dr_fragment_app_pc(tag) == next_pc) {

    // dr_fprintf(STDERR, "translating: %d\n", translating);
    // dr_fprintf(STDERR, "original bb:\n");
    // print_instrlist(drcontext, bb);

    switch (status)
    {
    case 0:
      ++where_pc;
      clear_instr_from(drcontext, bb, nullptr);
      call = XINST_CREATE_sub(drcontext, opnd_create_reg(DR_REG_RSP), OPND_CREATE_INT32(64));
      instrlist_append(bb, set_app_instr(call, where_pc));
      call = XINST_CREATE_move(drcontext, opnd_create_reg(DR_REG_RSI), opnd_create_reg(DR_REG_RAX));
      instrlist_append(bb, set_app_instr(call, where_pc));
      call = INSTR_CREATE_lea(drcontext, opnd_create_reg(DR_REG_RDI), OPND_CREATE_MEM_lea(DR_REG_RSP, DR_REG_NULL, 0, 10));
      instrlist_append(bb, set_app_instr(call, where_pc));
      call = INSTR_CREATE_call(drcontext, opnd_create_pc(trace_module[tracelib]->traced_function[tracefunc]->pc));
      instrlist_append(bb, set_app_instr(call, where_pc));
      ++status;
      break;
    case 1:
      ++where_pc;
      {
        instr_t* first_instr = instrlist_first(bb);
        call = XINST_CREATE_add(drcontext, opnd_create_reg(DR_REG_RSP), OPND_CREATE_INT32(64));
        instrlist_prepend(bb, set_app_instr(call, where_pc));
        reset_all_register(drcontext, bb, first_instr, next_pc);
        // reset_stack_status(drcontext, bb, first_instr, next_pc);
      }
      next_pc = nullptr;
      status = 0;
    }

    // dr_fprintf(STDERR, "status %d:\n", status == 0 ? 2 : 1);
    // print_instrlist(drcontext, bb);

    return DR_EMIT_STORE_TRANSLATIONS;
  }

  return DR_EMIT_DEFAULT;
}

static void
exit_callback() {
  if (cur_trace != nullptr) {
    print_trace(cur_trace);
    delete cur_trace;
  }
  if (g_log_file_fd != INVALID_FILE) {
    dr_close_file(g_log_file_fd);
  }
}

DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[]) {
#ifndef X64
  static_assert(false, "only support x64");
#endif
  drsym_init(0);
  drmgr_init();
  drwrap_init();

  std::string parse_err;
  int last_index;
  if (!droption_parser_t::parse_argv(DROPTION_SCOPE_CLIENT, argc, argv, &parse_err, &last_index)) {
    dr_fprintf(STDERR, "Usage error: %s", parse_err.c_str());
    dr_abort();
  }

  g_log_file_fd = dr_open_file(g_log_file.get_value().c_str(), DR_FILE_WRITE_OVERWRITE);
  if (g_log_file_fd == INVALID_FILE) {
    dr_fprintf(STDERR, "file %s open failed", g_log_file.get_value().c_str());
    dr_abort();
  }

  drmgr_register_bb_app2app_event(basic_block_app2app_callback, nullptr);

  drmgr_register_module_load_event(module_load);

  dr_register_exit_event(exit_callback);
}

Application源码

#include <cstdio>
#include <string>
#include <iostream>
#include <random>
#include <ctime>

using namespace std;

std::uniform_int_distribution u(0,4);
std::default_random_engine e(time(0));

class A {
public:
  A(string name)
    : name_(name) { }

  static A* getInstance(string name) {
    printf("instance called, [name]: %s\n", name.c_str());
    // cout << "instance called, [name]: " << name << endl;
    static A* instance = new A(name);
    return instance;
  }

  string getTraceID() const {
    string trace;
    for (int i = 0; i < 10; ++i) {
      trace.push_back(u(e) + '0');
    }
    printf("getTraceID called, [trace]: %s\n", trace.c_str());
    // cout << "getTraceID called, [trace]: " << trace << endl;
    return trace;
  }

private:
  string name_;
};

int a(int ret);
int b(int ret);
int c(int ret);
int d(int ret);

int a(int ret) {
  if (u(e) < 5) {
    printf("a call b\n");
    // cout << "a call b" << endl;
    ++ret;
    b(ret);
  }
  if (u(e) < 5) {
    printf("a call c\n");
    // cout << "a call c" << endl;
    ++ret;
    c(ret);
  }
  if (u(e) < 5) {
    printf("a call d\n");
    // cout << "a call d" << endl;
    ++ret;
    d(ret);
  }
  return 0;
}

int b(int ret) {
  if (u(e) < 5) {
    printf("b call c\n");
    // cout << "b call c" << endl;
    ++ret;
    c(ret);
  }
  if (u(e) < 5) {
    printf("b call d\n");
    // cout << "b call d" << endl;
    ++ret;
    d(ret);
  }
  return 0;
}

int c(int ret) {
  if (u(e) < 5) {
    printf("c call d\n");
    // cout << "c call d" << endl;
    ++ret;
    d(ret);
  }
  return ret;
}

int d(int ret) {
  return ret;
}

int enter_function() {
  int ret = 0;
  if (u(e) < 5) {
    printf("enter call a\n");
    // cout << "enter call a" << endl;
    ++ret;
    a(ret);
  }
  if (u(e) < 5) {
    printf("enter call b\n");
    // cout << "enter call b" << endl;
    ++ret;
    b(ret);
  }
  if (u(e) < 5) {
    printf("enter call c\n");
    // cout << "enter call c" << endl;
    ++ret;
    c(ret);
  }
  if (u(e) < 5) {
    printf("enter call d\n");
    // cout << "enter call d" << endl;
    ++ret;
    d(ret);
  }
  if (u(e) > 10) {
    A::getInstance("")->getTraceID();
  }
  return ret;
}

int main() {
  for (int i = 0; i < 5; ++i) {
    enter_function();
  }
  return 0;
}