提取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: 头文件定义
Sdk\lib:静态链接库,dbghelp.lib,dbgeng.lib,engextcpp.lib,分别针对三个不同平台
Sdk\help:帮助文档
另:若不想基于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)