大熊的口袋

 

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都必须在这两个调用之间工作,否则就会出问题。

posted on 2009-11-14 12:54 大熊的口袋 阅读(1982) 评论(5)  编辑 收藏 引用 所属分类: cppwin32

评论

# re: TRACE改进版(静态tls在dll中使用的问题) 2009-11-14 14:28 OwnWaterloo

别搞了兄弟,用vsprintf之类的函数就可以了。

  回复  更多评论   

# re: TRACE改进版(静态tls在dll中使用的问题) 2009-11-14 14:47 李佳

额... 大部分都用汇编做的... 支持一下吧   回复  更多评论   

# re: TRACE改进版(静态tls在dll中使用的问题) 2009-11-14 15:02 OwnWaterloo

@李佳
如果只是练习着玩玩,机器码写都没问题。

如果是实际应用上,这么实现这么简单的需求 —— 10行代码了事 ——用到汇编和线程局部存储,那……
有点过了……

  回复  更多评论   

# re: TRACE改进版(静态tls在dll中使用的问题)[未登录] 2009-11-14 15:27 q

这位仁兄,不错!精神可佳,支持一下.

  回复  更多评论   

# re: TRACE改进版(静态tls在dll中使用的问题) 2009-11-16 08:57 大熊的口袋

之前也听说过有v系列的函数,然后又看到过一些基于栈共享实现一些东东的思想。这次遇到这个问题,首先想到的就是栈共享。完全没有考虑到v系类函数,不想在栈共享实现过程中又遇到线程同步问题,用了静态tls后又出现dll被动态加载后无法正常使用的问题。遂出现现在的解决方案。虽然不是最简单的,但是确实学到一些东西:)  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

公告

常用链接

留言簿(2)

随笔分类

随笔档案

win32 & debug

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜