Maison >Opération et maintenance >exploitation et maintenance Linux >Partagez un exemple d'obtention d'informations sur la pile actuelle sous Linux et Windows

Partagez un exemple d'obtention d'informations sur la pile actuelle sous Linux et Windows

零下一度
零下一度original
2017-06-29 15:35:471943parcourir

L'éditeur suivant vous proposera un article sur la façon d'obtenir les informations actuelles sur la pile sous Linux et Windows. L'éditeur le trouve plutôt bon, je vais donc le partager avec vous maintenant et le donner comme référence pour tout le monde. Suivons l'éditeur et jetons un coup d'œil

Lors de l'écriture de services logiciels stables et fiables, il est souvent utilisé pour générer des informations sur la pile afin que les utilisateurs/développeurs puissent obtenir des informations d'exécution précises. Couramment utilisé dans la sortie de journaux, le rapport d'erreurs et la détection d'anomalies.

Il existe une fonction relativement simple pour obtenir des informations sur la pile sous 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.
}

La le code ci-dessus provient de Il est légèrement modifié par rapport à la pile de référenceoverflow. Le cœur est constitué des deux fonctions backtrace et backtrace_symbols.

Il est recommandé d'utiliser le code open source StackWalker sous Windows, qui prend en charge X86, AMD64 et IA64.

Si vous avez besoin du code le plus simple, voici le code que j'ai extrait, qui est évidemment plus compliqué que Linux. (De nombreuses fonctions de Win sont plus compliquées à mettre en œuvre, et bien sûr il existe de nombreuses fonctions qui sont beaucoup plus simples à mettre en œuvre que Linux.)

Je donnerai quelques explications plus tard.


#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;
}

Veuillez lier dbghelp.lib pour la compilation

Le noyau est StackWalk et SymGetSymFromAddr64, SymGetLineFromAddr64.

StackWalk est utilisé pour obtenir la couche suivante de la pile.

SymGetSymFromAddr64 est utilisé pour obtenir le nom de la fonction actuelle.

SymGetLineFromAddr64 est utilisé pour obtenir le fichier et le numéro de ligne de la fonction.

Pour que ces trois fonctions fonctionnent correctement, il est nécessaire d'initialiser les fonctions liées aux symboles (SymInitialize), d'obtenir la table de description du thread actuel (RtlCaptureContext), et de charger le fichier utilisé (SymLoadModule64).

Les deux fichiers d'en-tête f09cc4b7bed6d9cc68d3d0b1b9d93304 715cd25a5675b4112aa1e30746b0ebb2

Une fois le code ci-dessus exécuté, les informations sur la pile seront affichées sur la console.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn