打印调用堆栈

原文地址:http://blog.csdn.net/chief1985/article/details/4618492

java里面可以使用Throwable类来获取堆栈,示例代码如下:


package name.xu;
public class CallStack {
	public static void printCallStatck() {
		Throwable ex = new Throwable();
		StackTraceElement[] stackElements = ex.getStackTrace();
		if (stackElements != null) {
			for (int i = 0; i < stackElements.length; i++) {
				System.out.print(stackElements[i].getClassName()+"/t");
				System.out.print(stackElements[i].getFileName()+"/t");
				System.out.print(stackElements[i].getLineNumber()+"/t");
				System.out.println(stackElements[i].getMethodName());
				System.out.println("-----------------------------------");
			}
		}
	}
	
}

 

C#里面使用与java类似的方法,示例代码如下:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace TestProjectCSharp
{
    class CallStack
    {
        public static void printCallStack()
        {
            StackTrace ss = new StackTrace(true);
            String flName = ss.GetFrame(1).GetFileName();// GetMethod().DeclaringType; 
            int lineNo = ss.GetFrame(1).GetFileLineNumber();
            String methodName = ss.GetFrame(1).GetMethod().Name;
            Console.WriteLine(flName+"---"+lineNo+"---"+methodName);
        }
    }
}

 

c里面获取堆栈跟系统有关(主要是获取函数名称不一致,获取地址是一致的),
linux下使用backtrace和backtrace_symbols函数,示例代码如下:(编译方法:gcc -o funstack -rdynamic -ldl funstack.c)


//funstack.c
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#if defined(REG_RIP)
# define SIGSEGV_STACK_IA64
# define REGFORMAT "%016lx"
#elif defined(REG_EIP)
# define SIGSEGV_STACK_X86
# define REGFORMAT "%08x"
#else
# define SIGSEGV_STACK_GENERIC
# define REGFORMAT "%x"
#endif
static void signal_segv(int signum, siginfo_t* info, void*ptr) {
        static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};
        size_t i;
        ucontext_t *ucontext = (ucontext_t*)ptr;
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
        int f = 0;
        Dl_info dlinfo;
        void **bp = 0;
        void *ip = 0;
#else
        void *bt[20];
        char **strings;
        size_t sz;
#endif
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
# if defined(SIGSEGV_STACK_IA64)
        ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
        bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
# elif defined(SIGSEGV_STACK_X86)
        ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
        bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
# endif
        fprintf(stderr, "Stack trace:/n");
        while(bp && ip) {
                if(!dladdr(ip, &dlinfo))
                        break;
                const char *symname = dlinfo.dli_sname;
                fprintf(stderr, "% 2d: %p %s+%u (%s)/n",
                                ++f,
                                ip,
                                symname,
                                (unsigned)(ip - dlinfo.dli_saddr),
                                dlinfo.dli_fname);
                if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main"))
                        break;
                ip = bp[1];
                bp = (void**)bp[0];
        }
#else
        fprintf(stderr, "Stack trace (non-dedicated):/n");
        sz = backtrace(bt, 20);
        strings = backtrace_symbols(bt, sz);
        for(i = 0; i < sz; ++i)
                fprintf(stderr, "%s/n", strings[i]);
#endif
        fprintf(stderr, "End of stack trace/n");
        return;
}
int setup_sigsegv() {
        struct sigaction action;
        memset(&action, 0, sizeof(action));
        action.sa_sigaction = signal_segv;
        action.sa_flags = SA_SIGINFO;
        if(sigaction(SIGUSR1, &action, NULL) < 0) {
                perror("sigaction");
                return 0;
        }
        return 1;
}

void func1()
{
        raise(SIGUSR1);
        return ;
}
void func2()
{
        raise(SIGUSR1);
        return ;
}
void entry()
{
        func1();
        func2();
        return;
}
int main()
{
        setup_sigsegv();
        entry();
}

 

windows下使用GetThreadContext ,StackWalk,SymGetOptions ,SymFunctionTableAccess,示例代码如下:


#include "SimpleSymbolEngine.h"
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment( lib, "dbghelp" )
static char const szRCSID[] = "$Id: SimpleSymbolEngine.cpp,v 1.4 2005/05/04 21:52:05 Eleanor Exp $";
//////////////////////////////////////////////////////////////////////////////////////
// Singleton for the engine (SymInitialize doesn't support multiple calls)
SimpleSymbolEngine& SimpleSymbolEngine::instance()
{
static SimpleSymbolEngine theEngine;
    return theEngine;
}
/////////////////////////////////////////////////////////////////////////////////////
SimpleSymbolEngine::SimpleSymbolEngine()
{
    hProcess = GetCurrentProcess();
    DWORD dwOpts = SymGetOptions();
    dwOpts |= SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS;
    SymSetOptions ( dwOpts );
    ::SymInitialize( hProcess, 0, true );
}
/////////////////////////////////////////////////////////////////////////////////////
SimpleSymbolEngine::~SimpleSymbolEngine()
{
    ::SymCleanup( hProcess );
}
/////////////////////////////////////////////////////////////////////////////////////
std::string SimpleSymbolEngine::addressToString( PVOID address )
{
    std::ostringstream oss;
    // First the raw address
    oss << "0x" <
MaxNameLength = sizeof( SymInfo ) – offsetof( tagSymInfo, symInfo.Name ); DWORD dwDisplacement; if ( SymGetSymFromAddr( hProcess, (DWORD)address, &dwDisplacement, pSym) ) { oss << ” ” <Name; if ( dwDisplacement != 0 ) oss << “+0x” << std::hex << dwDisplacement << std::dec; } // Finally any file/line number IMAGEHLP_LINE lineInfo = { sizeof( IMAGEHLP_LINE ) }; if ( SymGetLineFromAddr( hProcess, (DWORD)address, &dwDisplacement, &lineInfo ) ) { char const *pDelim = strrchr( lineInfo.FileName, ‘//’ ); oss << ” at ” << ( pDelim ? pDelim + 1 : lineInfo.FileName ) << “(” << lineInfo.LineNumber << “)”; } return oss.str(); } ///////////////////////////////////////////////////////////////////////////////////// // StackTrace: try to trace the stack to the given output void SimpleSymbolEngine::StackTrace( PCONTEXT pContext, std::ostream & os ) { os <Eip; stackFrame.AddrPC.Mode = AddrModeFlat; stackFrame.AddrFrame.Offset = pContext->Ebp; stackFrame.AddrFrame.Mode = AddrModeFlat; stackFrame.AddrStack.Offset = pContext->Esp; stackFrame.AddrStack.Mode = AddrModeFlat; while ( ::StackWalk( IMAGE_FILE_MACHINE_I386, hProcess, GetCurrentThread(), // this value doesn’t matter much if previous one is a real handle &stackFrame, pContext, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL ) ) { os << ” 0x” << (PVOID) stackFrame.AddrFrame.Offset << ” ” << addressToString( (PVOID)stackFrame.AddrPC.Offset ) << “/n”; } os.flush(); }

 

完整的代码到http://www.howzatt.demon.co.uk/articles/SimpleSymbolEngine.zip下载。http://www.codeproject.com/KB/threads/StackWalker.aspx里面也有例子。

参考:

http://www.diybl.com/course/3_program/java/javashl/2008119/96739.html

http://topic.csdn.net/u/20090618/11/7c19832a-975e-4be6-987b-e61d789b31b5.html

http://bbs3.chinaunix.net/viewthread.php?tid=950357&extra=&page=2

http://www.codeproject.com/KB/threads/StackWalker.aspx

http://topic.csdn.net/u/20070515/21/fc3ebc11-b871-4761-90ae-3c6ddc7b4248.html

http://www.diybl.com/course/3_program/c++/cppjs/20090403/163752_2.html

http://accu.org/index.php/journals/276

http://blog.thepimp.net/archives/how-to-generate-backtraces-on-windows-without-compiler.html

———-淡定的分割线———-

codeproject的例子可以编译通过,但是运行报错。又找到第二个更简洁的可行例子。

原文地址:http://blog.csdn.net/jfyy/article/details/6759243


BOOL WINAPI StackWalk64(
    __in      DWORD MachineType,
    __in      HANDLE hProcess,
    __in      HANDLE hThread,
    __inout   LPSTACKFRAME64 StackFrame,
    __inout   PVOID ContextRecord,
    __in_opt  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
    __in_opt  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
    __in_opt  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
    __in_opt  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
    );

而后将原文中的的方法简单做了修改和封装。

调用方法: vector CallStack::GetCallStack();

#include 
#include 
#include 
#include 

#define MAX_ADDRESS_LENGTH 32
#define MAX_NAME_LENGTH 1024
using namespace std;

struct CrashInfo
{
    char ErrorCode[MAX_ADDRESS_LENGTH];
    char Address[MAX_ADDRESS_LENGTH];
    char Flags[MAX_ADDRESS_LENGTH];
};

// CallStack信息
//
struct CallStackInfo
{
    char ModuleName[MAX_NAME_LENGTH];
    char MethodName[MAX_NAME_LENGTH];
    char FileName[MAX_NAME_LENGTH];
    char LineNumber[MAX_NAME_LENGTH];
};


class CallStack
{
public:
    CallStack(void);
    ~CallStack(void);
    static vector GetCallStack();

protected:
    static void SafeStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc);
    static vector GetCallStack(const CONTEXT *pContext);
    static CrashInfo GetCrashInfo(const EXCEPTION_RECORD *pRecord);
};


#include "StdAfx.h"
#include "CallStackInfo.h"


// 添加对dbghelp.lib的编译依赖   
//   
#pragma comment(lib, "dbghelp.lib")
#pragma once


CallStack::CallStack(void)
{
}

CallStack::~CallStack(void)
{
}

// 安全拷贝字符串函数
//   
void CallStack::SafeStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
{
    if (nMaxDestSize <= 0)
        return;
    if (strlen(szSrc) ExceptionAddress);
    sprintf_s(crashinfo.ErrorCode, "%08X", pRecord->ExceptionCode);
    sprintf_s(crashinfo.Flags, "%08X", pRecord->ExceptionFlags);

    return crashinfo;
}


vector CallStack::GetCallStack()
{
    CONTEXT context;
    HANDLE hThread = GetCurrentThread();

    // get context
    context.ContextFlags = (CONTEXT_FULL);
    if (GetThreadContext(hThread, &context))
    {
        return GetCallStack(&context);
    }
    else{
        vector arrCallStackInfo;
        return arrCallStackInfo;
    }

}

// 得到CallStack信息
//   
vector CallStack::GetCallStack(const CONTEXT *pContext)
{
    HANDLE hProcess = GetCurrentProcess();

    SymInitialize(hProcess, NULL, TRUE);

    vector arrCallStackInfo;

    CONTEXT c = *pContext;

    STACKFRAME64 sf;
    memset(&sf, 0, sizeof(STACKFRAME64));
    DWORD dwImageType = IMAGE_FILE_MACHINE_I386;

    // 不同的CPU类型,具体信息可查询MSDN
    //
#ifdef _M_IX86   
    sf.AddrPC.Offset = c.Eip;
    sf.AddrPC.Mode = AddrModeFlat;
    sf.AddrStack.Offset = c.Esp;
    sf.AddrStack.Mode = AddrModeFlat;
    sf.AddrFrame.Offset = c.Ebp;
    sf.AddrFrame.Mode = AddrModeFlat;
#elif _M_X64   
    dwImageType = IMAGE_FILE_MACHINE_AMD64;
    sf.AddrPC.Offset = c.Rip;
    sf.AddrPC.Mode = AddrModeFlat;
    sf.AddrFrame.Offset = c.Rsp;
    sf.AddrFrame.Mode = AddrModeFlat;
    sf.AddrStack.Offset = c.Rsp;
    sf.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64   
    dwImageType = IMAGE_FILE_MACHINE_IA64;
    sf.AddrPC.Offset = c.StIIP;
    sf.AddrPC.Mode = AddrModeFlat;
    sf.AddrFrame.Offset = c.IntSp;
    sf.AddrFrame.Mode = AddrModeFlat;
    sf.AddrBStore.Offset = c.RsBSP;
    sf.AddrBStore.Mode = AddrModeFlat;
    sf.AddrStack.Offset = c.IntSp;
    sf.AddrStack.Mode = AddrModeFlat;
#else   
#error "Platform not supported!"
#endif   

    HANDLE hThread = GetCurrentThread();

    while (true)
    {
        // 该函数是实现这个功能的最重要的一个函数
        // 函数的用法以及参数和返回值的具体解释可以查询MSDN
        //   
        if (!StackWalk64(dwImageType, hProcess, hThread, &sf, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
        {
            break;
        }

        if (sf.AddrFrame.Offset == 0)
        {
            break;
        }

        CallStackInfo callstackinfo;
        SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, "N/A");
        SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, "N/A");
        SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, "N/A");
        SafeStrCpy(callstackinfo.LineNumber, MAX_NAME_LENGTH, "N/A");

        BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL64)+MAX_NAME_LENGTH];
        IMAGEHLP_SYMBOL64 *pSymbol = (IMAGEHLP_SYMBOL64*)symbolBuffer;
        memset(pSymbol, 0, sizeof(IMAGEHLP_SYMBOL64)+MAX_NAME_LENGTH);

        pSymbol->SizeOfStruct = sizeof(symbolBuffer);
        pSymbol->MaxNameLength = MAX_NAME_LENGTH;

        DWORD symDisplacement = 0;

        BOOL bGetFileName = FALSE;
        // 得到函数名
        //   
        if (SymGetSymFromAddr64(hProcess, sf.AddrPC.Offset, NULL, pSymbol))
        {
            SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, pSymbol->Name);
        }

        IMAGEHLP_LINE64 lineInfo;
        memset(&lineInfo, 0, sizeof(IMAGEHLP_LINE64));

        lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

        DWORD dwLineDisplacement;

        // 得到文件名和所在的代码行
        //
        if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
        {
            //该类本身不需要记载
            if (strcmp(lineInfo.FileName, __FILE__))
                bGetFileName = TRUE;

            SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, lineInfo.FileName);
            sprintf_s(callstackinfo.LineNumber, "%d", lineInfo.LineNumber);//后文中有改动——亦忧
        }

        IMAGEHLP_MODULE64 moduleInfo;
        memset(&moduleInfo, 0, sizeof(IMAGEHLP_MODULE64));

        moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);

        // 得到模块名
        //
        if (SymGetModuleInfo64(hProcess, sf.AddrPC.Offset, &moduleInfo))
        {
            SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, moduleInfo.ModuleName);
        }

        if (bGetFileName)
            arrCallStackInfo.push_back(callstackinfo);
    }

    SymCleanup(hProcess);

    return arrCallStackInfo;
}

 

———-淡定的分割线———-

运行发现除了调用堆栈的行号除了第一句外,其他都不准确。http://www.codeproject.com/KB/threads/StackWalker.aspx的例子讨论中有解决方法。


This is solvable as follows:
 
Add this to the StackWalkerInternal

//pSGLFA != NULL )
    { // yes, we have SymGetLineFromAddr64()
        if (this->pSGLFA(hProcess, dwAddr, pdwDisplacement, pLine) != FALSE)
        {
            csEntry.lineNumber = pLine->LineNumber;
            // TODO: Mache dies sicher...!
            strcpy_s(csEntry.lineFileName, sizeof(csEntry.lineFileName), pLine->FileName);
            success = TRUE;
        }
        else
        {
            this->m_parent->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), dwAddr);
        }
    } // yes, we have SymGetLineFromAddr64()
    return success;
}
//pSGLFA != NULL )
    { // yes, we have SymGetLineFromAddr64()
        if (this->pSGLFA(hProcess, dwAddr, pdwDisplacement, pLine) != FALSE)
        {
            csEntry.lineNumber = pLine->LineNumber;
            // TODO: Mache dies sicher...!
            strcpy_s(csEntry.lineFileName, sizeof(csEntry.lineFileName), pLine->FileName);
            success = TRUE;
        }
        else
        {
            this->m_parent->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), dwAddr);
        }
    } // yes, we have SymGetLineFromAddr64()

NEW:

    if (this->m_sw->FindLine(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line, csEntry))
    {
        this->m_sw->FindPreviousLine(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line, csEntry);	// ignore return status
    }

 

再次测试发现,应该除去第一次的情况。即调用堆栈第一句不适合FindPreviousLine。结合几家所言,终于完美了。

第二个例子获得文件名和行号的部分改为以下:


        if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
        {
            SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, lineInfo.FileName);

            // Get correct line number including return status
            if (arrCallStackInfo.size() > 0)
            {
                DWORD64 qwAddr = sf.AddrPC.Offset;
                DWORD pdwDisplacement = dwLineDisplacement;
                IMAGEHLP_LINE64 Line64 = lineInfo;
                DWORD nextLine = lineInfo.LineNumber;
                BOOL bOk = FALSE;
                do
                {
                    --qwAddr;
                    bOk = SymGetLineFromAddr64(hProcess, qwAddr, &pdwDisplacement, &lineInfo);
                } while (bOk && (nextLine == lineInfo.LineNumber));
                if (!bOk)
                {
                    dwLineDisplacement = pdwDisplacement;
                    lineInfo = Line64;
                }
            }

            sprintf_s(callstackinfo.LineNumber, "%d", lineInfo.LineNumber);
        }

 

Advertisements

用C#做爬虫和反防盗链

足迹网友推荐了了一个IT电子书网站。近三千本英文版IT类最新版的电子书直接下载!哇,流口水了,虽然一辈子也看不完。我点,我点,,我点,,,我累啊。。。赶快祭起爬虫包好一起卷走。 爬虫是用C#傻瓜型编程语言编写的一个网页抓取软件。通过针对特定格式的网页做定制,抓取网页部分或者全部信息,或者抓取网页中所包含的资源网址,下载到本地磁盘或数据库内。爬虫1.0曾用来抓新华字典和龙维基(即汉典的数据源),效果相当好。就是抓下来数据后,没有时间继续开发了。由于这次是抓网页内的资源再下载,涉及到两次抓取和分析,只得把爬虫1.0升级到了2.0。经过反复测试,确定10个线程抓取和分析网页,10个线程抓取电子书,再加上带锁的先入先出队列,基本就搞定了。 抓了两千来本书后,遇到了一个问题。爬虫发现有一部分书没有保存在原网站上,而是放到了另一个免费服务器filepi.com上。于是,修改定制规则,从filepi.com抓取。又发现,抓下来的是验证网页。令我百思不得其解的是,为什么同一个filepi.com的资源连接,从电子书网站上点就直接下载,从本地点就会转到验证网页呢?这天,“防盗链”三个字从脑海中一闪而过。百度一下发现了其中的奥妙在HTTP包头中的referer。经过三秒钟搜索后,在谷歌的第一个链接里得到两件Firefox插件神器live http headers和ref control。live http headers可以抓取HTTP包头。ref control则可以设定从Firefox发出的referer。 牛刀小试,把合适fiewpi.com的referer找了出来。果然,fiewpi.com立即乖乖献上电子书。 于是,在爬虫里加上一句。 AHttpWebRequest.Referer = “xxx”; 一句顶一万句。世界清净了。