参考:http://msdn.microsoft.com/en-us/library/windows/desktop/ms679291(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms680181(v=vs.85).aspx
http://sleepy.sourceforge.net/
http://www.codersnotes.com/sleepy
Very Sleepy是一个比较好用的Profiler工具(是对Sleepy的改进版本),开源,代码也很简洁。
那么Sleepy的原理是什么呢?
Sleepy程序作为主进程,启动子进程,或者Attach到目标进程上,从而获取到目标进程的第一个线程。
Sleepy每隔很短的一段时间就调用一次sample取样操作
void CALLBACK sprof_callback(UINT wTimerID, UINT msg,
DWORD dwUser, DWORD dw1, DWORD dw2)
{
ProfilerThread* thread = (ProfilerThread*)dwUser;
thread->sample();
}
然后将目标线程暂停,并且查看目标线程正在执行的eip地址
void Profiler::sampleTarget()
{
// An open question is whther or not this routine can be called
// reentrantly by the multi-media timer support.
HRESULT result = SuspendThread(target_thread);
if(result == 0xffffffff)
throw ProfilerExcep("SuspendThread failed.");
CONTEXT threadcontext;
threadcontext.ContextFlags = CONTEXT_i386 | CONTEXT_CONTROL;
result = GetThreadContext(target_thread, &threadcontext);
if(!result)
throw ProfilerExcep("GetThreadContext failed.");
PROFILER_ADDR addr = threadcontext.Eip;
//std::cout << "addr: " << addr << std::endl;
result = ResumeThread(target_thread);
if(!result)
throw ProfilerExcep("ResumeThread failed.");
//NOTE: this has to go after ResumeThread. Otherwise mem allocation needed by std::map
//may hit a lock held by the suspended thread.
counts[addr]++;
}
并且将维护一张map,记录各个被取样到的ip地址被调用到的次数。
最后再通过DbgHlp的Sym功能,将ip地址映射成具体的函数名称。
Very Sleepy加入了Call Graph的feature,可以统计Include/Exclude方式下的某个函数被调用的计数。
估计是使用了DbgHelp的StackWalk64函数。
基于对pin的学习,这个功能很容易地用pintool重新改写。
但是,pintool的好处在哪呢?