提取Dump文件的Callstack,创建windbg的一个扩展应用

关键字:

Call Native dll in managed codes

Windbg extension, windbg sdk/api

Use Marshal in .NET

Get callstack from a dump file.

本文实现:

使用windbg提供的sdk自动化提取dump文件的callstack;

实现流程思路:

 ·   

Part1:Windbg sdk

Windbg 为扩展应用开发者提供了一套sdk, 安装Debugging Tools for Windows, 在%programfiles%\ Debugging Tools for Windows (x86)\sdk可以找到.

其中:

Sdk\inc: 头文件定义

1

Sdk\lib:静态链接库,dbghelp.lib,dbgeng.lib,engextcpp.lib,分别针对三个不同平台

2

Sdk\samples: samples(推荐从这里入门) 
3

Sdk\help:帮助文档 
4
另:若不想基于native/c++开发应用,可以考虑在managed codes通过dllimport的方式使用编译好的dbghelp.dll,dbgeng.dll,这两个文件在%programfiles%\ Debugging Tools for Windows (x86)下可以找到。

 

Part2:Create native dll to wrap windbg sdk

在VS里新建Wind32 动态链接库应用工程,

添加一个头文件dbgcallstack.h,内容如下:

#include <windows.h>

#include "dbgeng.h" // windbg header

// The following ifdef block is the standard way of creating macros which make exporting

// from a DLL simpler. All files within this DLL are compiled with the DBGCALLSTACK_EXPORTS

// symbol defined on the command line. this symbol should not be defined on any project

// that uses this DLL. This way any other project whose source files include this file see

// DBGCALLSTACK_API functions as being imported from a DLL, whereas this DLL sees symbols

// defined with this macro as being exported.

#ifdef DBGCALLSTACK_EXPORTS

#define DBGCALLSTACK_API __declspec(dllexport)

#else

#define DBGCALLSTACK_API __declspec(dllimport)

#endif

//define the max length of call stack

const UINT MAX_CALLSTACK_LINE_LENGTH = 51200;

//define the structure for call stack information

struct CallStackData

{

char stack_line[MAX_CALLSTACK_LINE_LENGTH];

};

extern "C" DBGCALLSTACK_API HRESULT GetCallStack(const char *dumpfile,const char *symbol, __out CallStackData* callstack);

extern "C" DBGCALLSTACK_API HRESULT GetCallStackEx(const char *dumpfile,const char *symbol, __out char* callstack);

class StdioOutputCallbacks : public IDebugOutputCallbacks

{

public:

// IUnknown.

STDMETHOD(QueryInterface)(

THIS_

IN REFIID InterfaceId,

OUT PVOID* Interface

);

STDMETHOD_(ULONG, AddRef)(

THIS

);

STDMETHOD_(ULONG, Release)(

THIS

);

// IDebugOutputCallbacks.

STDMETHOD(Output)(

THIS_

IN ULONG Mask,

IN PCSTR Text

);

};

extern StdioOutputCallbacks g_OutputCb;

 

添加对应的cpp实现dbgcallstack.cpp,代码如下:

#include <windows.h>

#include "dbgeng.h" // windbg header

#include "stdafx.h"

#include "dbgcallstack.h"

#include <stdlib.h>

#include <stdio.h>

#include <stdarg.h>

StdioOutputCallbacks g_OutputCb;

CallStackData* g_Callstack;

STDMETHODIMP

StdioOutputCallbacks::QueryInterface(

THIS_

IN REFIID InterfaceId,

OUT PVOID* Interface

)

{

*Interface = NULL;

if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||

IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))

{

*Interface = (IDebugOutputCallbacks *)this;

AddRef();

return S_OK;

}

else

{

return E_NOINTERFACE;

}

}

STDMETHODIMP_(ULONG)

StdioOutputCallbacks::AddRef(

THIS

)

{

// This class is designed to be static so

// there's no true refcount.

return 1;

}

STDMETHODIMP_(ULONG)

StdioOutputCallbacks::Release(

THIS

)

{

// This class is designed to be static so

// there's no true refcount.

return 0;

}

STDMETHODIMP

StdioOutputCallbacks::Output(

THIS_

IN ULONG Mask,

IN PCSTR Text

)

{

UNREFERENCED_PARAMETER(Mask);

fputs(Text, stdout);

strcat(g_Callstack->stack_line,Text);

strcat(g_Callstack->stack_line,"\r\n");

return S_OK;

}

 

extern "C" DBGCALLSTACK_API HRESULT GetCallStack(const char *dumpfile,const char *symbol, __out CallStackData* callstack)

{

HRESULT hr = S_OK;

IDebugClient *client = NULL;

IDebugControl *control = NULL;

IDebugSymbols *symbols = NULL;

g_Callstack=callstack;

// Create the base IDebugClient object

hr = DebugCreate(__uuidof(IDebugClient), (LPVOID*)&client);

if(FAILED(hr))

{

goto cleanup;

}

// from the base, create the Control and Symbols objects

hr = client->QueryInterface(__uuidof(IDebugControl), (LPVOID*)&control);

if(FAILED(hr))

{

goto cleanup;

}

hr = client->QueryInterface(__uuidof(IDebugSymbols), (LPVOID*)&symbols);

if(FAILED(hr))

{

goto cleanup;

}

if (symbol)

{

hr = symbols->SetSymbolPath(symbol);

if(FAILED(hr))

{

fputs("set symbols error\r\n", stdout);

goto cleanup;

}

}

hr = client->SetOutputCallbacks(&g_OutputCb);

hr = client->OpenDumpFile(dumpfile);

if(FAILED(hr))

{

fputs("open dumpfile error\r\n", stdout);

goto cleanup;

}

// wait for the engine to finish processing

control->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);

if(FAILED(hr))

{

goto cleanup;

}

PDEBUG_STACK_FRAME stackFrames =NULL; //new DEBUG_STACK_FRAME[512];

ULONG numFrames;

fputs("output call stack", stdout);

strcpy(g_Callstack->stack_line,"get information\r\n");

control->

OutputStackTrace(DEBUG_OUTCTL_ALL_CLIENTS, stackFrames,

512, DEBUG_STACK_SOURCE_LINE |

DEBUG_STACK_FRAME_ADDRESSES |

DEBUG_STACK_COLUMN_NAMES |

DEBUG_STACK_FRAME_NUMBERS);

cleanup:

// cleanup and destroy the objects

if(symbols)

{

symbols->Release();

symbols = NULL;

}

if(control)

{

control->Release();

control = NULL;

}

if (client)

{

//

// Request a simple end to any current session.

// This may or may not do anything but it isn't

// harmful to call it.

//

// We don't want to see any output from the shutdown.

client->SetOutputCallbacks(NULL);

client->EndSession(DEBUG_END_PASSIVE);

client->Release();

}

return S_OK;

}

 

extern "C" DBGCALLSTACK_API HRESULT GetCallStackEx(const char *dumpfile,const char *symbol, __out char* callstack)

{

CallStackData *data=new CallStackData();

HRESULT hr=GetCallStack(dumpfile,symbol,data);

strcpy(callstack,data->stack_line);

return hr;

}

编译输出dbgcallstack.dll.

注意,编译前需要指定调试引擎动态链接库dbgeng.lib位置,在工程属性Configuration Properties->Linker->Input设置

Part3:Use native dll above in C#

第一步,添加dbgcallstack.dll到新建的C#工程,设置其Build Action->Content, Copy to output directory->always

第二步,定义结构体CallStackData,

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]

public struct CallStackData

{

/// <summary>

/// Data

/// </summary>

[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 51200)]

public string data;

}

第三步,引入:

[DllImport("dbgcallstack.dll")]

public static extern int GetCallStack(string a, string b, outCallStackData cs);

[DllImport("dbgcallstack.dll")]

public static extern int GetCallStackEx(string a, string b,

[Out][MarshalAsAttribute(UnmanagedType.LPStr, SizeConst = 512)]StringBuilder cs);

第四步,调用:

CallStackData data = new CallStackData();

int hr = GetCallStackEx(dumpfile location, symbol location, out data);

Console.WriteLine(data.data);

References:

http://blogs.msdn.com/joshpoley/archive/2008/06/23/automating-crash-dump-analysis-some-final-thoughts.aspx (automating dump file analysis)

http://msdn.microsoft.com/en-us/library/cc265826.aspx (debugger engine api overview)

http://www.pcreview.co.uk/forums/thread-1878614.php (values type marshal problem)

http://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=clrinterop&ReleaseId=14120

(a tool to help the conversion between native codes and managed signatures)

posted @ 2012-05-04 18:52  Dance With Automation  Views(893)  Comments(0Edit  收藏  举报