zoukankan      html  css  js  c++  java
  • 提取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)

  • 相关阅读:
    ambry集群搭建(无SSL验证的方式)
    接口属性命名不规范的处理方式
    windows下搭建ElasticSearch
    MyBatis-Plus分页插件——PageHelper和IPage原理介绍
    饥荒服务器搭建加mod使用 阿里云服务器 Ubuntu18
    WSL2+Ubuntu20.04桌面功能配置
    设计模式学习总结(Java版)
    Java in 蓝桥杯
    Windows 批处理脚本学习
    Vue风格指南小结
  • 原文地址:https://www.cnblogs.com/dancewithautomation/p/2483138.html
Copyright © 2011-2022 走看看