msvc_dbg.c raw

   1  /*
   2   * Copyright (c) 2004-2005 Andrei Polushin
   3   *
   4   * Permission is hereby granted, free of charge, to any person obtaining a copy
   5   * of this software and associated documentation files (the "Software"), to
   6   * deal in the Software without restriction, including without limitation the
   7   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   8   * sell copies of the Software, and to permit persons to whom the Software is
   9   * furnished to do so, subject to the following conditions:
  10   *
  11   * The above copyright notice and this permission notice shall be included in
  12   * all copies or substantial portions of the Software.
  13   *
  14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20   * IN THE SOFTWARE.
  21   */
  22  
  23  #if !defined(_M_ARM) && !defined(_M_ARM64) && !defined(_M_X64) \
  24      && defined(_MSC_VER)
  25  
  26  /* TODO: arm[64], x86_64 currently miss some machine-dependent code below. */
  27  
  28  /* See also `GC_HAVE_BUILTIN_BACKTRACE` in `gc_config_macros.h` file. */
  29  
  30  #  include <stdio.h>
  31  #  include <stdlib.h>
  32  
  33  #  define GC_BUILD
  34  #  include "gc/gc.h"
  35  
  36  #  ifndef WIN32_LEAN_AND_MEAN
  37  #    define WIN32_LEAN_AND_MEAN 1
  38  #  endif
  39  #  define NOSERVICE
  40  #  include <windows.h>
  41  
  42  #  pragma pack(push, 8)
  43  #  include <imagehlp.h>
  44  #  pragma pack(pop)
  45  
  46  #  ifdef __cplusplus
  47  extern "C" {
  48  #  endif
  49  
  50  /* Compatibility with the platform `execinfo.h` file. */
  51  int backtrace(void *addresses[], int count);
  52  char **backtrace_symbols(void *const addresses[], int count);
  53  
  54  #  ifdef __cplusplus
  55  } /* extern "C" */
  56  #  endif
  57  
  58  #  pragma comment(lib, "dbghelp.lib")
  59  #  pragma optimize("gy", off)
  60  
  61  /*
  62   * Disable a warning that `/GS` cannot protect parameters and local
  63   * variables from local buffer overrun because optimizations are off.
  64   */
  65  #  pragma warning(disable : 4748)
  66  
  67  typedef GC_word word;
  68  #  define GC_ULONG_PTR word
  69  
  70  #  ifdef _WIN64
  71  typedef GC_ULONG_PTR ULONG_ADDR;
  72  #  else
  73  typedef ULONG ULONG_ADDR;
  74  #  endif
  75  
  76  #  ifndef MAX_SYM_NAME
  77  #    define MAX_SYM_NAME 2000
  78  #  endif
  79  
  80  static HANDLE
  81  GetSymHandle(void)
  82  {
  83    static HANDLE symHandle = NULL;
  84    if (!symHandle) {
  85      BOOL bRet = SymInitialize(symHandle = GetCurrentProcess(), NULL, FALSE);
  86      if (bRet) {
  87        DWORD dwOptions = SymGetOptions();
  88        SymSetOptions((dwOptions & ~(DWORD)SYMOPT_UNDNAME) | SYMOPT_LOAD_LINES);
  89      }
  90    }
  91    return symHandle;
  92  }
  93  
  94  static void *CALLBACK
  95  FunctionTableAccess(HANDLE hProcess, ULONG_ADDR dwAddrBase)
  96  {
  97    return SymFunctionTableAccess(hProcess, dwAddrBase);
  98  }
  99  
 100  static ULONG_ADDR CALLBACK
 101  GetModuleBase(HANDLE hProcess, ULONG_ADDR dwAddress)
 102  {
 103    MEMORY_BASIC_INFORMATION memoryInfo;
 104    ULONG_ADDR dwAddrBase = SymGetModuleBase(hProcess, dwAddress);
 105    if (dwAddrBase != 0) {
 106      return dwAddrBase;
 107    }
 108    if (VirtualQueryEx(hProcess, (void *)(GC_ULONG_PTR)dwAddress, &memoryInfo,
 109                       sizeof(memoryInfo))) {
 110      char filePath[_MAX_PATH];
 111      char curDir[_MAX_PATH];
 112      char exePath[_MAX_PATH];
 113      DWORD size = GetModuleFileNameA((HINSTANCE)memoryInfo.AllocationBase,
 114                                      filePath, sizeof(filePath));
 115  
 116      /*
 117       * Save and restore current directory around `SymLoadModule` (see
 118       * the KB article Q189780).
 119       */
 120      GetCurrentDirectoryA(sizeof(curDir), curDir);
 121      GetModuleFileNameA(NULL, exePath, sizeof(exePath));
 122  #  if _MSC_VER > 1200
 123      strcat_s(exePath, sizeof(exePath), "\\..");
 124  #  else /* VC 6 or earlier */
 125      strcat(exePath, "\\..");
 126  #  endif
 127      SetCurrentDirectoryA(exePath);
 128  #  ifdef _DEBUG
 129      GetCurrentDirectoryA(sizeof(exePath), exePath);
 130  #  endif
 131      SymLoadModule(hProcess, NULL, size ? filePath : NULL, NULL,
 132                    (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase, 0);
 133      SetCurrentDirectoryA(curDir);
 134    }
 135    return (ULONG_ADDR)(GC_ULONG_PTR)memoryInfo.AllocationBase;
 136  }
 137  
 138  static ULONG_ADDR
 139  CheckAddress(void *address)
 140  {
 141    ULONG_ADDR dwAddress = (ULONG_ADDR)(GC_ULONG_PTR)address;
 142    GetModuleBase(GetSymHandle(), dwAddress);
 143    return dwAddress;
 144  }
 145  
 146  static size_t GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread,
 147                                          CONTEXT *context, size_t skip,
 148                                          void *frames[], size_t maxFrames);
 149  
 150  static size_t
 151  GetStackFrames(size_t skip, void *frames[], size_t maxFrames)
 152  {
 153    HANDLE hProcess = GetSymHandle();
 154    HANDLE hThread = GetCurrentThread();
 155    CONTEXT context;
 156    context.ContextFlags = CONTEXT_FULL;
 157    if (!GetThreadContext(hThread, &context)) {
 158      return 0;
 159    }
 160    /* `GetThreadContext` might return invalid context for the current thread. */
 161  #  if defined(_M_IX86)
 162    __asm mov context.Ebp, ebp
 163  #  endif
 164        return GetStackFramesFromContext(hProcess, hThread, &context, skip + 1,
 165                                         frames, maxFrames);
 166  }
 167  
 168  static size_t
 169  GetStackFramesFromContext(HANDLE hProcess, HANDLE hThread, CONTEXT *context,
 170                            size_t skip, void *frames[], size_t maxFrames)
 171  {
 172    size_t frameIndex;
 173    DWORD machineType;
 174    STACKFRAME stackFrame = { 0 };
 175    stackFrame.AddrPC.Mode = AddrModeFlat;
 176  #  if defined(_M_IX86)
 177    machineType = IMAGE_FILE_MACHINE_I386;
 178    stackFrame.AddrPC.Offset = context->Eip;
 179    stackFrame.AddrStack.Mode = AddrModeFlat;
 180    stackFrame.AddrStack.Offset = context->Esp;
 181    stackFrame.AddrFrame.Mode = AddrModeFlat;
 182    stackFrame.AddrFrame.Offset = context->Ebp;
 183  #  elif defined(_M_MRX000)
 184    machineType = IMAGE_FILE_MACHINE_R4000;
 185    stackFrame.AddrPC.Offset = context->Fir;
 186  #  elif defined(_M_ALPHA)
 187    machineType = IMAGE_FILE_MACHINE_ALPHA;
 188    stackFrame.AddrPC.Offset = (unsigned long)context->Fir;
 189  #  elif defined(_M_PPC)
 190    machineType = IMAGE_FILE_MACHINE_POWERPC;
 191    stackFrame.AddrPC.Offset = context->Iar;
 192  #  elif defined(_M_IA64)
 193    machineType = IMAGE_FILE_MACHINE_IA64;
 194    stackFrame.AddrPC.Offset = context->StIIP;
 195  #  elif defined(_M_ALPHA64)
 196    machineType = IMAGE_FILE_MACHINE_ALPHA64;
 197    stackFrame.AddrPC.Offset = context->Fir;
 198  #  elif !defined(CPPCHECK)
 199  #    error Unknown CPU
 200  #  endif
 201    for (frameIndex = 0; frameIndex < maxFrames;) {
 202      BOOL bRet
 203          = StackWalk(machineType, hProcess, hThread, &stackFrame, &context,
 204                      NULL, FunctionTableAccess, GetModuleBase, NULL);
 205      if (!bRet) {
 206        break;
 207      }
 208      if (skip) {
 209        skip--;
 210      } else {
 211        frames[frameIndex++] = (void *)(GC_ULONG_PTR)stackFrame.AddrPC.Offset;
 212      }
 213    }
 214    return frameIndex;
 215  }
 216  
 217  static size_t
 218  GetModuleNameFromAddress(void *address, char *moduleName, size_t size)
 219  {
 220    const char *sourceName;
 221    IMAGEHLP_MODULE moduleInfo = { sizeof(moduleInfo) };
 222  
 223    if (size)
 224      *moduleName = 0;
 225    if (!SymGetModuleInfo(GetSymHandle(), CheckAddress(address), &moduleInfo)) {
 226      return 0;
 227    }
 228    sourceName = strrchr(moduleInfo.ImageName, '\\');
 229    if (sourceName) {
 230      sourceName++;
 231    } else {
 232      sourceName = moduleInfo.ImageName;
 233    }
 234    if (size) {
 235      strncpy(moduleName, sourceName, size)[size - 1] = 0;
 236    }
 237    return strlen(sourceName);
 238  }
 239  
 240  union sym_namebuf_u {
 241    IMAGEHLP_SYMBOL sym;
 242    char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME];
 243  };
 244  
 245  static size_t
 246  GetSymbolNameFromAddress(void *address, char *symbolName, size_t size,
 247                           size_t *offsetBytes)
 248  {
 249    if (size)
 250      *symbolName = 0;
 251    if (offsetBytes)
 252      *offsetBytes = 0;
 253    __try {
 254      ULONG_ADDR dwOffset = 0;
 255      union sym_namebuf_u u;
 256  
 257      u.sym.SizeOfStruct = sizeof(u.sym);
 258      u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym);
 259  
 260      if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset,
 261                             &u.sym)) {
 262        return 0;
 263      } else {
 264        const char *sourceName = u.sym.Name;
 265        char undName[1024];
 266        if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName),
 267                                 UNDNAME_NO_MS_KEYWORDS
 268                                     | UNDNAME_NO_ACCESS_SPECIFIERS)) {
 269          sourceName = undName;
 270        } else if (SymUnDName(&u.sym, undName, sizeof(undName))) {
 271          sourceName = undName;
 272        }
 273        if (offsetBytes) {
 274          *offsetBytes = dwOffset;
 275        }
 276        if (size) {
 277          strncpy(symbolName, sourceName, size)[size - 1] = 0;
 278        }
 279        return strlen(sourceName);
 280      }
 281    } __except (EXCEPTION_EXECUTE_HANDLER) {
 282      SetLastError(GetExceptionCode());
 283    }
 284    return 0;
 285  }
 286  
 287  static size_t
 288  GetFileLineFromAddress(void *address, char *fileName, size_t size,
 289                         size_t *lineNumber, size_t *offsetBytes)
 290  {
 291    const char *sourceName;
 292    IMAGEHLP_LINE line = { sizeof(line) };
 293    GC_ULONG_PTR dwOffset = 0;
 294  
 295    if (size)
 296      *fileName = 0;
 297    if (lineNumber)
 298      *lineNumber = 0;
 299    if (offsetBytes)
 300      *offsetBytes = 0;
 301    if (!SymGetLineFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset,
 302                            &line)) {
 303      return 0;
 304    }
 305    if (lineNumber) {
 306      *lineNumber = line.LineNumber;
 307    }
 308    if (offsetBytes) {
 309      *offsetBytes = dwOffset;
 310    }
 311    sourceName = line.FileName;
 312    /*
 313     * TODO: Resolve relative filenames, found in "source directories"
 314     * registered with MSVC IDE.
 315     */
 316    if (size) {
 317      strncpy(fileName, sourceName, size)[size - 1] = 0;
 318    }
 319    return strlen(sourceName);
 320  }
 321  
 322  #  define GC_SNPRINTF _snprintf
 323  
 324  static size_t
 325  GetDescriptionFromAddress(void *address, const char *format, char *buffer,
 326                            size_t size)
 327  {
 328    char *const begin = buffer;
 329    char *const end = buffer + size;
 330    size_t line_number = 0;
 331  
 332    (void)format;
 333    if (size) {
 334      *buffer = 0;
 335    }
 336    buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL);
 337    size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
 338  
 339    if (line_number) {
 340      char str[20];
 341  
 342      (void)GC_SNPRINTF(str, sizeof(str), "(%d) : ", (int)line_number);
 343      str[sizeof(str) - 1] = '\0';
 344      if (size) {
 345        strncpy(buffer, str, size)[size - 1] = 0;
 346      }
 347      buffer += strlen(str);
 348      size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
 349    }
 350  
 351    if (size) {
 352      strncpy(buffer, "at ", size)[size - 1] = 0;
 353    }
 354    buffer += sizeof("at ") - 1;
 355    size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
 356  
 357    buffer += GetSymbolNameFromAddress(address, buffer, size, NULL);
 358    size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
 359  
 360    if (size) {
 361      strncpy(buffer, " in ", size)[size - 1] = 0;
 362    }
 363    buffer += sizeof(" in ") - 1;
 364    size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
 365  
 366    buffer += GetModuleNameFromAddress(address, buffer, size);
 367    return buffer - begin;
 368  }
 369  
 370  static size_t
 371  GetDescriptionFromStack(void *const frames[], size_t count, const char *format,
 372                          char *description[], size_t size)
 373  {
 374    const GC_ULONG_PTR begin = (GC_ULONG_PTR)description;
 375    const GC_ULONG_PTR end = begin + size;
 376    GC_ULONG_PTR buffer = begin + (count + 1) * sizeof(char *);
 377    size_t i;
 378  
 379    for (i = 0; i < count; ++i) {
 380      if (description)
 381        description[i] = (char *)buffer;
 382      buffer += 1
 383                + GetDescriptionFromAddress(frames[i], format, (char *)buffer,
 384                                            end < buffer ? 0 : end - buffer);
 385    }
 386    if (description)
 387      description[count] = NULL;
 388    return buffer - begin;
 389  }
 390  
 391  /* The below is for compatibility with the platform `execinfo.h` file. */
 392  
 393  int
 394  backtrace(void *addresses[], int count)
 395  {
 396    return GetStackFrames(1, addresses, count);
 397  }
 398  
 399  char **
 400  backtrace_symbols(void *const addresses[], int count)
 401  {
 402    size_t size = GetDescriptionFromStack(addresses, count, NULL, NULL, 0);
 403    char **symbols = (char **)malloc(size);
 404    if (symbols != NULL)
 405      GetDescriptionFromStack(addresses, count, NULL, symbols, size);
 406    return symbols;
 407  }
 408  
 409  #else
 410  
 411  /* ANSI C does not allow translation units to be empty. */
 412  extern int GC_quiet;
 413  
 414  #endif
 415