You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
655 lines
21 KiB
655 lines
21 KiB
/*
|
|
* Copyright 2002-2019 Intel Corporation.
|
|
*
|
|
* This software is provided to you as Sample Source Code as defined in the accompanying
|
|
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
|
|
* section 1.L.
|
|
*
|
|
* This software and the related documents are provided as is, with no express or implied
|
|
* warranties, other than those that are expressly stated in the License.
|
|
*/
|
|
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <string.h>
|
|
#include "call-stack.H"
|
|
using std::map;
|
|
using std::set;
|
|
using std::vector;
|
|
using std::list;
|
|
using std::string;
|
|
using std::hex;
|
|
using std::list;
|
|
using std::ostringstream;
|
|
using std::setw;
|
|
using std::vector;
|
|
using std::right;
|
|
using std::dec;
|
|
using std::setfill;
|
|
using std::endl;
|
|
using std::left;
|
|
|
|
#if defined(TARGET_WINDOWS)
|
|
#define strdup _strdup
|
|
#endif
|
|
|
|
using namespace CALLSTACK;
|
|
static REG vreg;
|
|
KNOB_COMMENT _comment("pintool:call-stack", "Call Stack knobs");
|
|
KNOB<BOOL> _knob_source_location(KNOB_MODE_WRITEONCE,
|
|
"pintool:call-stack",
|
|
"callstack:source_locaion",
|
|
"1",
|
|
"Emit source location (file,line,column) ");
|
|
|
|
/// @cond INTERNAL_DOXYGEN
|
|
|
|
///////////////////////// Analysis Functions //////////////////////////////////
|
|
static void a_process_call(ADDRINT target,
|
|
ADDRINT sp, CallStack* call_stack)
|
|
{
|
|
ASSERTX(call_stack);
|
|
call_stack->process_call(sp, target);
|
|
}
|
|
|
|
|
|
static void a_process_return(ADDRINT sp, ADDRINT ip, CallStack* call_stack)
|
|
{
|
|
ASSERTX(call_stack);
|
|
call_stack->process_return(sp, ip);
|
|
}
|
|
|
|
static void a_on_call(ADDRINT target,
|
|
CallStackManager* mngr, THREADID tid, CONTEXT* ctxt)
|
|
{
|
|
ASSERTX(mngr);
|
|
mngr->on_call(tid, ctxt, target);
|
|
}
|
|
|
|
static ADDRINT a_target_interesting(ADDRINT target, CallStackManager* mngr)
|
|
{
|
|
ASSERTX(mngr);
|
|
return mngr->TargetInteresting(target);
|
|
}
|
|
|
|
static ADDRINT a_on_ret_should_fire(THREADID tid, CallStackManager* mngr)
|
|
{
|
|
ASSERTX(mngr);
|
|
return mngr->on_ret_should_fire(tid);
|
|
}
|
|
|
|
static void a_on_ret_fire(THREADID tid, CONTEXT* ctxt, ADDRINT ip, CallStackManager* mngr)
|
|
{
|
|
ASSERTX(mngr);
|
|
mngr->on_ret_fire(tid, ctxt, ip);
|
|
}
|
|
/////////////////////// End Analysis Functions ////////////////////////////////
|
|
|
|
static void
|
|
i_trace(TRACE trace, void *v)
|
|
{
|
|
CallStackManager* mngr = CallStackManager::get_instance();
|
|
ASSERTX(mngr);
|
|
IARGLIST args = IARGLIST_Alloc();
|
|
if (mngr->NeedContext()){
|
|
IARGLIST_AddArguments(args, IARG_CONTEXT, IARG_END);
|
|
}
|
|
else{
|
|
// pass a null as context
|
|
IARGLIST_AddArguments(args, IARG_ADDRINT, static_cast<ADDRINT>(0), IARG_END);
|
|
}
|
|
|
|
for(BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
|
|
|
|
INS tail = BBL_InsTail(bbl);
|
|
|
|
//in the original implementation there were special handling for
|
|
//push
|
|
//ret
|
|
//on ia-32 windows
|
|
//if this ever become an issue we should add this handling
|
|
|
|
// We need a special check for RTM instructions cause they are
|
|
// defined as branch as well
|
|
#if defined(SUPPORT_RTM)
|
|
xed_decoded_inst_t const* const xedd = INS_XedDec(tail);
|
|
xed_iclass_enum_t iclass = xed_decoded_inst_get_iclass(xedd);
|
|
if (iclass == XED_ICLASS_XBEGIN || iclass == XED_ICLASS_XEND ||
|
|
iclass == XED_ICLASS_XABORT) {
|
|
continue;
|
|
}
|
|
#endif
|
|
if( INS_IsDirectControlFlow(tail) ) {
|
|
//check if direct or indirect call and take the target accordingly
|
|
ADDRINT target = INS_DirectControlFlowTargetAddress(tail);
|
|
INS_InsertCall(tail, IPOINT_BEFORE,
|
|
(AFUNPTR)a_process_call,
|
|
IARG_ADDRINT, target,
|
|
IARG_REG_VALUE, REG_STACK_PTR,
|
|
IARG_REG_VALUE, vreg,
|
|
IARG_END);
|
|
if (mngr->TargetInteresting(target)){
|
|
INS_InsertCall(tail, IPOINT_TAKEN_BRANCH,
|
|
(AFUNPTR)a_on_call,
|
|
IARG_ADDRINT, target,
|
|
IARG_PTR, mngr,
|
|
IARG_THREAD_ID,
|
|
IARG_IARGLIST, args, //context or null
|
|
IARG_END);
|
|
}
|
|
}
|
|
if( INS_IsIndirectControlFlow(tail) && !INS_IsRet(tail) ) {
|
|
INS_InsertCall(tail, IPOINT_TAKEN_BRANCH,
|
|
(AFUNPTR)a_process_call,
|
|
IARG_BRANCH_TARGET_ADDR,
|
|
IARG_REG_VALUE, REG_STACK_PTR,
|
|
IARG_REG_VALUE, vreg,
|
|
IARG_END);
|
|
|
|
INS_InsertIfCall(tail, IPOINT_TAKEN_BRANCH,
|
|
(AFUNPTR)a_target_interesting,
|
|
IARG_BRANCH_TARGET_ADDR,
|
|
IARG_PTR, mngr,
|
|
IARG_END);
|
|
INS_InsertThenCall(tail, IPOINT_TAKEN_BRANCH,
|
|
(AFUNPTR)a_on_call,
|
|
IARG_BRANCH_TARGET_ADDR,
|
|
IARG_PTR, mngr,
|
|
IARG_THREAD_ID,
|
|
IARG_IARGLIST, args, //context or null
|
|
IARG_END);
|
|
|
|
}
|
|
|
|
if( INS_IsRet(tail) ) {
|
|
INS_InsertCall(tail, IPOINT_BEFORE,
|
|
(AFUNPTR)a_process_return,
|
|
IARG_REG_VALUE, REG_STACK_PTR,
|
|
IARG_INST_PTR,
|
|
IARG_REG_VALUE, vreg,
|
|
IARG_END);
|
|
|
|
INS_InsertIfCall(tail, IPOINT_TAKEN_BRANCH,
|
|
(AFUNPTR)a_on_ret_should_fire,
|
|
IARG_THREAD_ID,
|
|
IARG_PTR, mngr,
|
|
IARG_END);
|
|
INS_InsertThenCall(tail, IPOINT_TAKEN_BRANCH,
|
|
(AFUNPTR)a_on_ret_fire,
|
|
IARG_THREAD_ID,
|
|
IARG_IARGLIST, args, //context or null
|
|
IARG_INST_PTR,
|
|
IARG_PTR, mngr,
|
|
IARG_END);
|
|
|
|
}
|
|
}
|
|
IARGLIST_Free(args);
|
|
}
|
|
|
|
static string RemoveNamespace(const string& name){
|
|
size_t pos = name.rfind(':');
|
|
if (pos == string::npos){
|
|
return name;
|
|
}
|
|
string s = name.substr(pos+1, string::npos);
|
|
return s;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
CallStack::create_entry( ADDRINT current_sp,
|
|
ADDRINT target)
|
|
{
|
|
// push entry -- note this is sp at the callsite
|
|
CallEntry entry(current_sp, target);
|
|
_call_vec.push_back(entry);
|
|
}
|
|
|
|
|
|
|
|
// roll back stack if we got here from a longjmp
|
|
// Note stack grows down and register stack grows up.
|
|
void CallStack::adjust_stack( ADDRINT current_sp )
|
|
{
|
|
if( _call_vec.size() == 0 ) return;
|
|
|
|
//original comment:
|
|
//TIPP: I changed this from > to >= ...not sure it's right, but works better
|
|
while( current_sp >= _call_vec.back().sp() ) {
|
|
_call_vec.pop_back();
|
|
if( _call_vec.size() == 0 ){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// standard call
|
|
void CallStack::process_call(ADDRINT current_sp,
|
|
ADDRINT target)
|
|
{
|
|
// check if we got here from a longjmp.
|
|
adjust_stack(current_sp);
|
|
create_entry(current_sp, target);
|
|
}
|
|
|
|
// standard return
|
|
void
|
|
CallStack::process_return( ADDRINT current_sp, ADDRINT ip )
|
|
{
|
|
// original implementation had this:
|
|
// on ia-32 windows to identify
|
|
// push
|
|
// ret
|
|
// and ignore it, in order to process callstack correctly
|
|
// add this back if needed
|
|
|
|
// check if we got here from a longjmp.
|
|
adjust_stack(current_sp);
|
|
|
|
if (_call_vec.size() > 0){
|
|
//on windows we do not start the instrumentation at the beginning code.
|
|
//this my lead to this scenario:
|
|
// call ...
|
|
// ret ...
|
|
// ret ...
|
|
//so if the stack size is 0 we are ignoring this.
|
|
|
|
_call_vec.pop_back();
|
|
}
|
|
}
|
|
|
|
void CallStack::push_head(ADDRINT current_sp, ADDRINT target){
|
|
create_entry(current_sp,target);
|
|
}
|
|
|
|
void CallStack::save_all_ips_info(){
|
|
|
|
CallStackInfo info;
|
|
CallStackManager* mngr = CallStackManager::get_instance();
|
|
ASSERTX(mngr);
|
|
for (UINT32 i = 0; i< depth(); i++){
|
|
ADDRINT ip = _call_vec[i].target();
|
|
mngr->get_ip_info(ip, info);
|
|
}
|
|
}
|
|
|
|
ADDRINT CallStack::top_target(){
|
|
return _call_vec.back().target();
|
|
}
|
|
|
|
// Get target of specific call stack depth
|
|
ADDRINT CallStack::depth_target(UINT32 depth) {
|
|
ASSERTX(_call_vec.size() > depth);
|
|
return _call_vec[depth].target();
|
|
}
|
|
|
|
UINT32 CallStack::depth(){
|
|
return _call_vec.size();
|
|
}
|
|
|
|
void CallStack::emit_stack( UINT32 depth,
|
|
vector<string>& out){
|
|
|
|
CallVec::reverse_iterator iter;
|
|
string last;
|
|
string pc;
|
|
INT32 level;
|
|
INT32 id;
|
|
ostringstream o;
|
|
CallStackInfo info;
|
|
|
|
UINT32 width = sizeof(ADDRINT) * 2; //bytes => nibbles
|
|
BOOL _source_location = _knob_source_location;
|
|
|
|
//emit the header
|
|
o << "# " << setw(width) << "IP";
|
|
o << setw(20) << right << " FUNCTION ";
|
|
o << setw(50) << " IMAGE NAME ";
|
|
|
|
if (_source_location){
|
|
o << setw(120) << " FILE NAME:LINE:COLUMN";
|
|
}
|
|
o << "\n";
|
|
out.push_back(o.str());
|
|
|
|
//number of entires to print
|
|
level = ( depth > _call_vec.size() ) ? _call_vec.size() -1 : depth -1;
|
|
id = 0;
|
|
o.str("");
|
|
//emit the call stack
|
|
for(iter = _call_vec.rbegin(); iter != _call_vec.rend(); iter++) {
|
|
CallStackManager* mngr = CallStackManager::get_instance();
|
|
mngr->get_ip_info(iter->target(), info);
|
|
|
|
o << right << dec << setw(2) << id << "# ";
|
|
o << "0x" << hex << setw(width) << setfill('0') << iter->target() << " ";
|
|
o << setw(20) << setfill(' ') << left << info.func_name;
|
|
o << setw(20) << info.image_name;
|
|
if (_source_location && info.file_name) {
|
|
o << " at " << info.file_name << ":" << dec << info.line;
|
|
if (info.column){
|
|
o << ":" << info.column;
|
|
}
|
|
}
|
|
|
|
o << endl;
|
|
out.push_back(o.str());
|
|
o.str("");
|
|
|
|
level--;
|
|
id++;
|
|
if (level < 0){
|
|
break;
|
|
}
|
|
}
|
|
out.push_back("\n");
|
|
}
|
|
|
|
void CallStack::get_targets(list<ADDRINT>& out)
|
|
{
|
|
CallVec::reverse_iterator iter;
|
|
for(iter = _call_vec.rbegin(); iter != _call_vec.rend(); iter++) {
|
|
out.push_back(iter->target());
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
CallStackManager* CallStackManager::_instance = 0;
|
|
|
|
// Handle new thread
|
|
void CallStackManager::thread_begin(THREADID tid, CONTEXT* ctxt,
|
|
INT32 flags, void* v){
|
|
CallStack* call_stack = new CallStack();
|
|
ASSERTX(call_stack);
|
|
ASSERTX(v);
|
|
ASSERTX(ctxt);
|
|
CallStackManager* call_stack_manager = reinterpret_cast<CallStackManager*>(v);
|
|
ASSERTX(call_stack_manager);
|
|
call_stack_manager->add_stack(tid, call_stack);
|
|
|
|
PIN_SetContextReg(ctxt, vreg, (ADDRINT)call_stack);
|
|
}
|
|
|
|
// Add the call stack of a new thread
|
|
void CallStackManager::add_stack(THREADID tid, CallStack* call_stack){
|
|
ASSERTX(call_stack);
|
|
PIN_GetLock(&_map_lock,tid+1);
|
|
_call_stack_map[tid] = call_stack;
|
|
PIN_ReleaseLock(&_map_lock);
|
|
}
|
|
|
|
// Activate call stack manager if needed
|
|
void CallStackManager::activate(){
|
|
if (_activated){
|
|
return;
|
|
}
|
|
_activated = true;
|
|
|
|
// Get virtual register and insturmentation routines
|
|
vreg = PIN_ClaimToolRegister();
|
|
PIN_AddThreadStartFunction(thread_begin, this);
|
|
TRACE_AddInstrumentFunction(i_trace, this);
|
|
IMG_AddInstrumentFunction(Img, this);
|
|
}
|
|
|
|
// Get call stack manager instance and create it if needed
|
|
CallStackManager* CallStackManager::get_instance(){
|
|
if (_instance != 0){
|
|
return _instance;
|
|
}
|
|
|
|
// Create new instance
|
|
_instance = new CallStackManager();
|
|
ASSERTX(_instance);
|
|
return _instance;
|
|
}
|
|
|
|
// Get call stack of a specific IP
|
|
CallStack CallStackManager::get_stack(THREADID tid){
|
|
PIN_GetLock(&_map_lock,tid+1);
|
|
CallStack *call_stack = _call_stack_map[tid];
|
|
PIN_ReleaseLock(&_map_lock);
|
|
ASSERTX(call_stack);
|
|
return *call_stack; //copy const.
|
|
}
|
|
|
|
// Get call stack and source information for a specific IP
|
|
void CallStackManager::get_ip_info(
|
|
ADDRINT ip,
|
|
CallStackInfo& info)
|
|
{
|
|
PIN_GetLock(&_lock,0);
|
|
|
|
// If we already have information for this IP then just return it
|
|
CallStackInfoMap::iterator it = _call_stack_info.find(ip);
|
|
|
|
if ( it != _call_stack_info.end() ) {
|
|
PIN_ReleaseLock(&_lock);
|
|
info = it->second;
|
|
return;
|
|
}
|
|
|
|
// We got here for new IP
|
|
CallStackInfo curr_info;
|
|
string curr_file_name;
|
|
|
|
// Get routine and image information
|
|
PIN_LockClient();
|
|
curr_info.rtn_id = RTN_Id(RTN_FindByAddress(ip));
|
|
curr_info.func_name = strdup(RTN_FindNameByAddress(ip).c_str());
|
|
IMG img = IMG_FindByAddress(ip);
|
|
|
|
// Get source location if neeed
|
|
if (_knob_source_location){
|
|
PIN_GetSourceLocation(ip, &curr_info.column, &curr_info.line, &curr_file_name);
|
|
if (curr_file_name.length() > 0)
|
|
curr_info.file_name = strdup(curr_file_name.c_str());
|
|
}
|
|
|
|
PIN_UnlockClient();
|
|
|
|
// Analyze image information
|
|
string curr_image_name;
|
|
if (IMG_Valid(img)) {
|
|
curr_image_name = IMG_Name(img);
|
|
ADDRINT img_addr = IMG_LowAddress(img);
|
|
// The string contains image name and the offset of
|
|
// the instruction
|
|
curr_image_name+= ":" + hexstr(ip-img_addr);
|
|
curr_info.image_name = strdup(curr_image_name.c_str());
|
|
}
|
|
else {
|
|
curr_info.image_name = (char*)("UNKNOWN IMAGE");
|
|
}
|
|
|
|
// Add new information to our database
|
|
info = curr_info;
|
|
_call_stack_info[ip] = curr_info;
|
|
|
|
PIN_ReleaseLock(&_lock);
|
|
}
|
|
|
|
BOOL CallStackManager::NeedContext(){
|
|
return _use_ctxt;
|
|
}
|
|
|
|
BOOL CallStackManager::TargetInteresting(ADDRINT ip){
|
|
if (_enter_func_handlers_map.find(ip) != _enter_func_handlers_map.end()){
|
|
return TRUE;
|
|
}
|
|
if (_exit_func_handlers_map.find(ip) != _exit_func_handlers_map.end()){
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CallStackManager::on_function_enter(CALL_STACK_HANDLER handler, const string& func_name, void* v, BOOL use_ctxt){
|
|
CallStackHandlerParams params(handler,func_name,v);
|
|
_enter_func_handlers.push_back(params);
|
|
if (use_ctxt) _use_ctxt = true;
|
|
}
|
|
|
|
void CallStackManager::on_function_exit(CALL_STACK_HANDLER handler, const string& func_name, void* v, BOOL use_ctxt){
|
|
CallStackHandlerParams params(handler,func_name,v);
|
|
_exit_func_handlers.push_back(params);
|
|
if (use_ctxt) _use_ctxt = true;
|
|
}
|
|
|
|
void CallStackManager::on_function_ip_enter(CALL_STACK_HANDLER handler, ADDRINT func_ip, void* v, BOOL use_ctxt){
|
|
CallStackHandlerParams *params = new CallStackHandlerParams(handler,"",v,func_ip,FALSE);
|
|
_enter_func_handlers_map[func_ip].push_back(params);
|
|
if (use_ctxt) _use_ctxt = true;
|
|
}
|
|
|
|
void CallStackManager::on_function_ip_exit(CALL_STACK_HANDLER handler, ADDRINT func_ip, void* v, BOOL use_ctxt){
|
|
CallStackHandlerParams *params = new CallStackHandlerParams(handler,"",v,func_ip,FALSE);
|
|
_exit_func_handlers_map[func_ip].push_back(params);
|
|
if (use_ctxt) _use_ctxt = true;
|
|
}
|
|
|
|
// iterate each function in the loaded image and check the following:
|
|
// 1. whether the function was registered in on_function_enter.
|
|
// if so store the ip of the interesting function with the relevant
|
|
// vector of handlers.
|
|
// 2. whether the function was registered in on_function_exit.
|
|
// if so store the ip of the interesting function with the relevant
|
|
// vector of handlers.
|
|
void CallStackManager::Img(IMG img, void* v)
|
|
{
|
|
CallStackManager* mngr = static_cast<CallStackManager*>(v);
|
|
|
|
for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec)){
|
|
for (RTN rtn=SEC_RtnHead(sec); RTN_Valid(rtn); rtn=RTN_Next(rtn)){
|
|
|
|
string name = RTN_Name(rtn);
|
|
//get undecorated function name(with namespace)
|
|
name = PIN_UndecorateSymbolName(name, UNDECORATION_NAME_ONLY);
|
|
name = RemoveNamespace(name);
|
|
ADDRINT ip = RTN_Address(rtn);
|
|
//check if the function has a callback registered for enter_function
|
|
for (UINT32 i = 0; i < mngr->_enter_func_handlers.size(); i++){
|
|
if (mngr->_enter_func_handlers[i]._function_name == name &&
|
|
mngr->_enter_func_handlers[i]._name_handler){
|
|
mngr->_enter_func_handlers[i]._first_ip = ip;
|
|
mngr->_enter_func_handlers_map[ip].push_back(&mngr->_enter_func_handlers[i]);
|
|
}
|
|
}
|
|
|
|
//check if the function has a callback registered for exit_function
|
|
for (UINT32 i = 0; i < mngr->_exit_func_handlers.size(); i++){
|
|
if (mngr->_exit_func_handlers[i]._function_name == name &&
|
|
mngr->_exit_func_handlers[i]._name_handler){
|
|
mngr->_exit_func_handlers[i]._first_ip = ip;
|
|
mngr->_exit_func_handlers_map[ip].push_back(&mngr->_exit_func_handlers[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// called after the execution of call/direct/indirect jump
|
|
//
|
|
// 1. check whether the target ip is present in the map of ip->handlers for enter to fuction
|
|
// if so - call all the handlers.
|
|
// 2. check whether the target ip is present in the map of ip->handlers for exit to fuction
|
|
// if so - record the depth of the stack at the entry point to the function
|
|
// so later, when we roll back the call-stack beyond the recorded depth we will call the
|
|
// registered handlers for on_function_exit
|
|
void CallStackManager::on_call(THREADID tid, CONTEXT* ctxt, ADDRINT ip){
|
|
|
|
//call all enter_function handlers
|
|
//iter holds a tuple of (ip, list of handlers to be called)
|
|
IpFuncHnadlersMap::iterator iter;
|
|
iter = _enter_func_handlers_map.find(ip);
|
|
if (iter != _enter_func_handlers_map.end()){
|
|
for (UINT32 i = 0 ; i < iter->second.size(); i++){
|
|
CallStackHandlerParams *params = iter->second[i];
|
|
ASSERTX(params);
|
|
params->_handler(ctxt, ip, tid, params->_args);
|
|
}
|
|
}
|
|
|
|
// if we already seen this function down the stack so we we exit only on the
|
|
// top most caller
|
|
// e.g. A*->A->A
|
|
// will stop on the first A
|
|
if(_marked_ip_for_exit.find(ip) != _marked_ip_for_exit.end()){
|
|
return;
|
|
}
|
|
|
|
//recored the stack depth if this is a requested exit functioniter = _exit_func_handlers_map.find(ip);
|
|
iter = _exit_func_handlers_map.find(ip);
|
|
if (iter != _exit_func_handlers_map.end()){
|
|
UINT32 depth = get_stack(tid).depth();
|
|
DepthFuncHandlersMap& m = _depth_func_handlers_tid_vec[tid];
|
|
m[depth] = iter->second; //a vector of handlers
|
|
_marked_ip_for_exit.insert(ip);
|
|
}
|
|
}
|
|
|
|
// If instrumentation called after the execution of ret instruction,
|
|
// after the call-stack was roll back.
|
|
// iterate over all the recorded call-stack depth to check if we have rolled back
|
|
// the call-stack beyond the recorded depth.
|
|
// if so, we return 1 so the Then instrumentation will be called
|
|
BOOL CallStackManager::on_ret_should_fire(THREADID tid){
|
|
|
|
BOOL was_found = FALSE;
|
|
UINT32 depth = get_stack(tid).depth();
|
|
DepthFuncHandlersMap::iterator iter;
|
|
DepthFuncHandlersMap& m = _depth_func_handlers_tid_vec[tid];
|
|
|
|
iter = m.begin();
|
|
//find all handlers that should be called based on the stack depth
|
|
for ( ; iter != m.end(); iter++){
|
|
if (iter->first > depth){
|
|
was_found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return was_found;
|
|
}
|
|
|
|
|
|
// Then analysis
|
|
// iterate over all the recorded call-stack depth to check if we have rolled back
|
|
// the call-stack beyond the recorded depth.
|
|
// if so:
|
|
// 1. we call all the registered handlers
|
|
// 2. remove the 'depth' entry so it will not be call again later.
|
|
void CallStackManager::on_ret_fire(THREADID tid, CONTEXT* ctxt, ADDRINT ip){
|
|
|
|
UINT32 depth = get_stack(tid).depth();
|
|
DepthFuncHandlersMap::iterator iter;
|
|
DepthFuncHandlersMap::iterator earase_iter;
|
|
DepthFuncHandlersMap& m = _depth_func_handlers_tid_vec[tid];
|
|
|
|
iter = m.begin();
|
|
//find all handlers that should be called based on the stack depth
|
|
while (iter != m.end()){
|
|
//we have rolled back the call-stack beyond the recorded depth
|
|
if (iter->first > depth){
|
|
for (UINT32 i = 0 ; i < iter->second.size(); i++){
|
|
CallStackHandlerParams *params = iter->second[i];
|
|
params->_handler(ctxt, ip, tid, params->_args);
|
|
_marked_ip_for_exit.erase(params->_first_ip);
|
|
}
|
|
earase_iter = iter;
|
|
iter++;
|
|
m.erase(earase_iter);
|
|
|
|
|
|
}
|
|
else{
|
|
iter++;
|
|
}
|
|
}
|
|
}
|
|
/// @endcond
|