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

时间:2014-07-16 00:24:12

标签: multithreading 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开发上运行时。使用counter = 0;语句上的断点从Visual Studio运行并运行调试会话,执行最终会中断,并且“Threads”调试窗口中将显示两个线程。我可以使用双击或右键单击>“切换到线程”在两个线程之间来回切换,然后查看调用堆栈并浏览源代码并检查符号(对于我的应用程序代码中的调用堆栈帧)两个线程。但是,当我在Windows CE连接通过时也这样做。 ActiveSync / WMDC(已经尝试了我们的自定义CE 6.0硬件与内部操作系统和SDK,以及旧的Windows移动5.0 PDA与库存MS SDK)我可以看到一个调用堆栈和浏览源的线程,其中已经发生了中断(当前执行点在我的应用程序代码中),但是我没有对其他线程有任何用处,其他线程当前在内核空间中被阻塞,等待它的睡眠超时。

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

Windows CE 6 remote debugging. No call stack when pause program描述了同样的问题,但并没有真正提供解决方案

感谢

理查德

1 个答案:

答案 0 :(得分:0)

可能是因为缺少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

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

如果日志记录不充分(如您提供的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] %p\n", i, lpFrames[i].dwReturnAddr);
    OutputDebugString(szBuff);
  }
}

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

2 - 在WinMain中初始化dwGUIThread:

dwGUIThread = GetCurrentThreadId();

3 - 在DumpGUIThreadCallStack();内执行断点之前执行ThreadFunction。它将写入输出窗口文本,类似于:

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

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

4 - 在调试器内部切换到反汇编模式(右键单击源文件并选择&#39;转到反汇编&#39;)。在窗口顶部的此模式下,您将看到Address:行。你应该从输出窗口复制粘贴到它的地址,在我的情况下,000111F4将引导我到以下行:

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

          IncrementCounter(mainCounter);

它为您提供了GUI线程实际执行的操作。


Visual Studio Debugger允许从即时窗口执行功能,但我无法调用DumpGUIThreadCallStack,我总是得到错误:不支持功能评估&#39;。

要查找帧返回地址的最近符号,您还可以将.map文件与.cod文件(/ FAcs编译源)一起使用,谷歌上有一些很好的教程。

以上示例在WCE6.0(最终用户)设备上使用VS 2005和Standard SDK 5.0进行了测试。