在调试多线程Windows CE应用程序时查看所有线程的调用堆栈



因此,使用Visual Studio 2008为Windows CE 6.0平台开发本地c++代码。考虑以下多线程应用程序:

#include "stdafx.h"
void IncrementCounter(int& counter)
{
    if (++counter >= 1000)
    {
        counter = 0;
    }
}
unsigned long ThreadFunction(void* arguments)
{
    int threadCounter = 0;
    while (true)
    {
         Sleep(20);
         IncrementCounter(threadCounter);
    }
    return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
    CreateThread(
                    NULL,       
                    0,          
                    (LPTHREAD_START_ROUTINE)ThreadFunction, 
                    NULL,       
                    0,          
                    NULL        
                 );

    int mainCounter = 0;
    while (true)
    {
        Sleep(20);
        IncrementCounter(mainCounter);
    }
    return 0;
}

当我在我的Windows 7 dev. machine上构建它并从Visual Studio运行一个调试会话时,在counter = 0;语句上有一个断点,执行最终会中断,两个线程将在"threads"调试窗口中显示。我可以使用双击或右键单击->"switch to Thread"在两个线程之间来回切换,并查看两个线程的调用堆栈和浏览源代码并检查符号(用于应用程序代码中的调用堆栈帧)。然而,当我做同样的Windows CE连接通过。ActiveSync/WMDC(已经尝试了我们的自定义CE 6.0硬件与内部操作系统和SDK,以及一个旧的Windows mobile 5.0 PDA与股票MS SDK)我可以看到一个调用堆栈和浏览源代码的线程,其中中断已经发生(当前执行点是在我的应用程序代码),但是我没有得到任何有用的其他线程,这是目前被阻塞在内核空间等待它的睡眠超时。

有谁知道是否有可能在Windows CE上更好地工作?我猜这可能与调试器不知道在哪里找到WinCE内核元素的.pdb符号文件有关,或者我是否需要运行调试操作系统?

Windows CE 6远程调试。当暂停程序描述相同的问题时,没有调用堆栈,但没有真正提供解决方案

感谢理查德

可能是因为coredll.dll缺少pdb文件。如果您正在为您的设备创建映像,则可以访问此文件,否则恐怕它取决于平台。

你可以在这里发现这个问题被认为是由VS2005设计的,所以也许VS2008也是如此:

http://connect.microsoft.com/VisualStudio/feedback/details/190785/unable-to-debug-windows-mobile-application-that-is-in-a-system-call

在下面的链接中,你可以找到一些使用平台构建器为"未运行的线程"查找调用堆栈的说明

https://distrinet.cs.kuleuven.be/projects/SEESCOA/internal/workpackages/workpackage6/Task6dot2/ESCE/classes/331.pdf

由于我只使用vs2005,我无法确认它是否有任何帮助。

如果日志记录是不够的(正如你提供的SO链接中建议的那样),要找到一个线程的调用堆栈,就像你的例子一样,我建议使用GetThreadCallStack函数。下面是一步一步的步骤:

1 -添加以下代码到您的项目:

typedef struct _CallSnapshotEx {
  DWORD dwReturnAddr;
  DWORD dwFramePtr;
  DWORD dwCurProc;
  DWORD dwParams[4];
} CallSnapshotEx;
#define STACKSNAP_EXTENDED_INFO 2
DWORD dwGUIThread;
void DumpGUIThreadCallStack() {
  HINSTANCE hCore = LoadLibrary(_T("coredll.dll"));
  typedef ULONG (*GETTHREADCALLSTACK)(HANDLE hThrd, ULONG dwMaxFrames, LPVOID lpFrames[], DWORD dwFlags,DWORD dwSkip);
  GETTHREADCALLSTACK pGetThreadCallStack = (GETTHREADCALLSTACK)GetProcAddress(hCore, _T("GetThreadCallStack"));
  if ( !pGetThreadCallStack )
    return;
  #define MAX_FRAMES 40
  CallSnapshotEx  lpFrames[MAX_FRAMES];
  DWORD dwCnt = pGetThreadCallStack((HANDLE)dwGUIThread, MAX_FRAMES, (void**)lpFrames, STACKSNAP_EXTENDED_INFO, 0);
  TCHAR szBuff[64];
  for ( DWORD i = 0; i < dwCnt; ++i ) {
    wsprintf(szBuff, L"[%d] %pn", i, lpFrames[i].dwReturnAddr);
    OutputDebugString(szBuff);
  }
}

它将在输出窗口调用帧返回地址中转储(示例输出在第3点)。

在WinMain中初始化dwGUIThread:

dwGUIThread = GetCurrentThreadId();

3 -在ThreadFunction内部实际断点之前执行DumpGUIThreadCallStack();。它将向输出窗口写入类似于这样的文本:

[0] 8C04D2C4
[1] 8C04D34C
[2] 40026D48
[3] 000111F4 <--- 1
[4] 00011BAC <--- 2
[5] 4003C2DC

1和2是您感兴趣的返回地址,您希望找到最接近它们的符号。

4 -在调试器内部切换到拆卸模式(右键单击源文件并选择"Go to disassembly")。在这种模式下,在窗口的顶部,你会看到Address:线。您应该从输出窗口复制粘贴到它的地址,在我的情况下,000111F4将引导我到以下行:

      while (true)
      {
          Sleep(20);
000111F0  mov         r0, #0x14 
000111F4  bl          0001193C  // <--- 1
          IncrementCounter(mainCounter);

给出你的GUI线程实际在做什么。


Visual Studio Debugger允许从直接窗口执行函数,但我无法调用DumpGUIThreadCallStack,我总是得到'错误:不支持函数求值'。

为了找到最接近的帧返回地址符号,你也可以使用。map文件和。cod文件(/FAcs编译的源代码),在谷歌上有一些很好的教程。

上面的例子是在WCE6.0(最终用户)设备上使用VS 2005和标准SDK 5.0进行测试的。

最新更新