Home  >  Article  >  Operation and Maintenance  >  Share an example of obtaining current stack information on Linux and Windows

Share an example of obtaining current stack information on Linux and Windows

零下一度
零下一度Original
2017-06-29 15:35:471938browse

The following editor will bring you an article on how to obtain the current stack information on Linux and Windows. The editor thinks it is quite good, so I will share it with you now and give it as a reference for everyone. Let’s follow the editor and take a look.

When writing stable and reliable software services, output stack information is often used so that users/developers can obtain accurate running information. Commonly used in log output, error reporting, and anomaly detection.

There is a relatively simple function to obtain stack information in Linux:


#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>


void handler(int sig) {
 void *array[5];
 size_t size;

 // get void*&#39;s for all entries on the stack
 size = backtrace(array, 5);

 // print out all the frames to stderr
 fprintf(stderr, "Error: signal %d:\n", sig);
 char** msgs = backtrace_symbols(array, size);
 for(int i=1;i<size && msgs[i];++i)
 printf("[%d] %s\n", i, msgs[i]);
 exit(1);
}

void baz() {
 int *foo = (int*)-1; // make a bad pointer
 printf("%d\n", *foo);  // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }


int main(int argc, char **argv) {
 signal(SIGSEGV, handler); // install our handler
 foo(); // this will call foo, bar, and baz. baz segfaults.
}

The above code is from the reference Slightly modified from stackoverflow. The core are the two functions backtrace and backtrace_symbols.

It is recommended to use the open source code StackWalker under Windows, which supports X86, AMD64, and IA64.

If you need the simplest code, then the following is the code I extracted, which is obviously more complicated than Linux. (Many functions of Win are more complicated to implement, and of course there are many functions that are much simpler to implement than Linux.)

I will give some explanations later.


#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <DbgHelp.h>
#include <TlHelp32.h>

using namespace std;

HANDLE ph;

void baz()
{
 int* v = 0;
 *v = 0;
}
void bar()
{
 baz();
}

void foo(){
 try {
  bar();
 }
 except(EXCEPTION_EXECUTE_HANDLER) {
  auto sire = SymInitialize(ph, 0, FALSE);
  sire = SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);
  CONTEXT ctx = { 0 };
  ctx.ContextFlags = CONTEXT_FULL;
  RtlCaptureContext(&ctx);
  STACKFRAME64 sf = { 0 };
 #ifdef _M_IX86 // ignore IA64
  auto imageType = IMAGE_FILE_MACHINE_I386;
  sf.AddrPC.Offset = ctx.Eip;
  sf.AddrPC.Mode = AddrModeFlat;
  sf.AddrFrame.Offset = ctx.Ebp;
  sf.AddrFrame.Mode = AddrModeFlat;
  sf.AddrStack.Offset = ctx.Esp;
  sf.AddrStack.Mode = AddrModeFlat;
 #elif _M_X64
  auto imageType = IMAGE_FILE_MACHINE_AMD64;
  sf.AddrPC.Offset = ctx.Rip;
  sf.AddrPC.Mode = AddrModeFlat;
  sf.AddrFrame.Offset = ctx.Rsp;
  sf.AddrFrame.Mode = AddrModeFlat;
  sf.AddrStack.Offset = ctx.Rsp;
  sf.AddrStack.Mode = AddrModeFlat;
 #endif

  MODULEENTRY32 me;
  auto snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
  auto info = Module32First(snap, &me);
  while (info) {
   auto dw = SymLoadModule64(ph, 0, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, me.modBaseSize);
   if (!Module32Next(snap, &me))break;
  }
  CloseHandle(snap);
  auto thread = GetCurrentThread();

  PIMAGEHLP_SYMBOL64 sym = (IMAGEHLP_SYMBOL64 *)malloc(sizeof(IMAGEHLP_SYMBOL64) + 100);
  if (!sym)
   return;
  memset(sym, 0, sizeof(IMAGEHLP_SYMBOL64) + 100);
  sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
  sym->MaxNameLength = 100;

  IMAGEHLP_LINE64 line = { 0 };
  line.SizeOfStruct = sizeof(line);
  for (;;) {
   auto result = StackWalk(imageType, ph, thread, &sf, &ctx, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0);
   if (result) {
    DWORD64 offset = 0;
    DWORD offset_for_line = 0;
    CHAR und_fullname[100];

    if (sf.AddrPC.Offset != 0) {
     if (SymGetSymFromAddr64(ph, sf.AddrPC.Offset, &offset, sym)) {
      UnDecorateSymbolName(sym->Name, und_fullname, 100, UNDNAME_COMPLETE);
      cout << und_fullname;
     }

     if (SymGetLineFromAddr64(ph, sf.AddrPC.Offset, &offset_for_line, &line)) {
      cout << " " << line.FileName << "(" << line.LineNumber << ")";
     }
     cout << endl;
    }
   }
   else
    break;
  }
  SymCleanup(ph);
 }
}
int main()
{
 ph = GetCurrentProcess();
 foo();
 return 0;
}

Please link dbghelp.lib for compilation

The core is StackWalk and SymGetSymFromAddr64, SymGetLineFromAddr64.

StackWalk is used to get the next layer of stack.

SymGetSymFromAddr64 is used to get the current function name.

SymGetLineFromAddr64 is used to obtain the file and line number of the function.

In order for these three functions to work properly, it is necessary to initialize the symbol-related functions (SymInitialize), obtain the current thread description table (RtlCaptureContext), and load the used module (SymLoadModule64).

The two header files f09cc4b7bed6d9cc68d3d0b1b9d93304 715cd25a5675b4112aa1e30746b0ebb2 are used.

After the above code is executed, stack information will be output on the console.

The above is the detailed content of Share an example of obtaining current stack information on Linux and Windows. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn