TRACE改进版(静态tls在dll中使用的问题)

TRACE改进版(静态tls在dll中使用的问题)

前面的文章里自己捣鼓了一个vc下的TraceX的实现。主要就是基于调用栈的共享。
http://www.cppblog.com/zwp/archive/2009/11/04/100134.html
先是调用sprintf格式化输出到一段静态线程局部存储空间,然后输出到OutputDebugString。从而可以被调试器捕获。
之所以选择静态tls而不选择全局变量,主要是防止多线程环境下可能出现的读写冲突。至于为何不使用栈变量(局部自动变量)是因为必须利用栈上现有的参数,且参数个数是不确定的,所以无法移动和拷贝这些参数。
然而,昨天在一个测试工程中动态加载了一个dll,在该dll里调用了Trace时却出错了。跟下来一看是在访问tls变量时出错。而静态加载该dll则不会出错。
后来从网上搜索才得知。静态tls在dll被动态加载时并不会被初始化。这也是静态tls最大的一个缺点了。缺点归缺点,不能因为不能用就放弃Trace啊,所以后来经过思考决定用动态tls来重新实现相关线程局部存储部分。
代码如下:
#ifndef __DEBUG_INFO_H__
#define  __DEBUG_INFO_H__

#ifndef NDEBUG
//  最多512个字符
VOID _cdecl TraceA( const   char   * msgFmt, );
VOID _cdecl TraceW(
const  wchar_t  * msgFmt, );

#define  TRACEA TraceA
#define  TRACEW TraceW

//  确保所有的TRACE都在这个范围之内。
bool  TraceStartUp();
void  TraceShutDown();

#define  TRACESTARTUP TraceStartUp
#define  TRACESHUTDOWN TraceShutDown

#else

#define  TRACEA ((void)0)
#define  TRACEW ((void)0)

#define  TRACESTARTUP ((void)0)
#define  TRACESHUTDOWN ((void)0)
#endif



#endif


#include 
" debuginfo.h "
#include 
< stdio.h >
#include 
< Windows.h >

#ifndef NDEBUG

const  UINT g_cInvalidTlsIdx  =   0xFFFFFFFF ;
UINT g_unTlsSlotIdx 
=  g_cInvalidTlsIdx;

bool  TraceStartUp()
{
    
if(g_cInvalidTlsIdx == g_unTlsSlotIdx)
    
{
        g_unTlsSlotIdx 
= TlsAlloc();
        
if (g_cInvalidTlsIdx == g_unTlsSlotIdx)
        
{
            
return false;
        }

    }

    
return true;
}


void  TraceShutDown()
{
    
if (g_cInvalidTlsIdx != g_unTlsSlotIdx)
    
{
        TlsFree(g_unTlsSlotIdx);
    }

    g_unTlsSlotIdx 
= g_cInvalidTlsIdx;
}


static   void  __stdcall SaveRetAddr(LPVOID pRet)
{
    TlsSetValue(g_unTlsSlotIdx, pRet);
}


static  LPVOID __stdcall GetRetAddr()
{
    
return TlsGetValue(g_unTlsSlotIdx);
}



const  UINT g_cBufSize  =   512 ;
static   char   *  __stdcall GetAddrOftszBuf()
{
    
return new char[g_cBufSize];
}


static  wchar_t  *  __stdcall GetAddrOftswzBuf()
{
    
return new wchar_t[g_cBufSize];
}


static   void  __stdcall DestroyStrBuf(LPVOID lpBuf)
{
    delete [] lpBuf;
}



_declspec(naked) VOID TraceA(
const   char   * msgFmt, )
{
    __asm 
{
        call SaveRetAddr;
        call GetAddrOftszBuf;
        push eax;
        call dword ptr [sprintf];
        pop eax;
        push eax;
        push eax;
        call dword ptr [OutputDebugStringA];
        call DestroyStrBuf;
        call GetRetAddr;
        push eax;
        ret;
    }

}


_declspec(naked) VOID TraceW(
const  wchar_t  * msgFmt, )
{
    __asm 
{
        call SaveRetAddr;
        call GetAddrOftszBuf;
        push eax;
        call dword ptr [_swprintf];
        pop eax;
        push eax;
        push eax;
        call dword ptr [OutputDebugStringW];
        call DestroyStrBuf;
        call GetRetAddr;
        push eax;
        ret;
    }

}


#endif

这里多增加了两个函数StartTrace和ShutdownTrace。用来从tls中获取一个可用索引和释放该索引。这就要求所有的Trace都必须在这两个调用之间工作,否则就会出问题。

你可能感兴趣的:(TRACE改进版(静态tls在dll中使用的问题))