[DynamoRIO-工具]调用链与服务内部函数调用跟踪
本文承接《对C++服务基于JIT模式的二进制无侵入式动态插桩研究》相关内容,在前者基础上重构完善了部分内容,修复了一些BUG。
- 效果图
已知BUG:drwrap拓展对函数进行前后插桩时会出现回调延迟。如A顺序调用B、C,C的pre hook回调可能会在B的post hook回调前调用。这种情况暂时只在同一位置的第二次及之后调用中出现,初步猜测原因是code cache建立完成后的乱序执行。
代码解析
1. Trace,Span记录处理部分
概念说明:
trace表示一次有意义的服务调用,span表示服务内部调用的需要追踪的函数。
这部分代码主要集中在前半部分含有trace,span字样的函数与类中,功能是通过栈模拟被插桩程序的函数栈,实现对函数调用关系的记录。
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地址。
这里假定函数开头保存了原基本块的三条指令,分别是nop,push RBP和mov 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;
}