summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core/shared-libraries-win32.cc
blob: e2db2579b88ef2e8433636f0d562598720a7c086 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <windows.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#include <sstream>

#include "shared-libraries.h"
#include "nsWindowsHelpers.h"

#define CV_SIGNATURE 0x53445352 // 'SDSR'

struct CodeViewRecord70
{
  uint32_t signature;
  GUID pdbSignature;
  uint32_t pdbAge;
  char pdbFileName[1];
};

static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge, char** aPdbName)
{
  if (!aStart) {
    return false;
  }

  PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(aStart);
  if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
    return false;
  }

  PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
      aStart + dosHeader->e_lfanew);
  if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
    return false;
  }

  uint32_t relativeVirtualAddress =
    ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
  if (!relativeVirtualAddress) {
    return false;
  }

  PIMAGE_DEBUG_DIRECTORY debugDirectory =
    reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(aStart + relativeVirtualAddress);
  if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
    return false;
  }

  CodeViewRecord70 *debugInfo = reinterpret_cast<CodeViewRecord70 *>(
      aStart + debugDirectory->AddressOfRawData);
  if (!debugInfo || debugInfo->signature != CV_SIGNATURE) {
    return false;
  }

  aAge = debugInfo->pdbAge;
  GUID& pdbSignature = debugInfo->pdbSignature;
  aSignature.m0 = pdbSignature.Data1;
  aSignature.m1 = pdbSignature.Data2;
  aSignature.m2 = pdbSignature.Data3;
  memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4));

  // The PDB file name could be different from module filename, so report both
  // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
  char * leafName = strrchr(debugInfo->pdbFileName, '\\');
  if (leafName) {
    // Only report the file portion of the path
    *aPdbName = leafName + 1;
  } else {
    *aPdbName = debugInfo->pdbFileName;
  }

  return true;
}

static bool IsDashOrBraces(char c)
{
  return c == '-' || c == '{' || c == '}';
}

SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
{
  SharedLibraryInfo sharedLibraryInfo;

  nsAutoHandle snap(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()));

  MODULEENTRY32 module = {0};
  module.dwSize = sizeof(MODULEENTRY32);
  if (Module32First(snap, &module)) {
    do {
      nsID pdbSig;
      uint32_t pdbAge;
      char *pdbName = NULL;

      // Load the module again to make sure that its handle will remain remain
      // valid as we attempt to read the PDB information from it.  We load the
      // DLL as a datafile so that if the module actually gets unloaded between
      // the call to Module32Next and the following LoadLibraryEx, we don't end
      // up running the now newly loaded module's DllMain function.  If the
      // module is already loaded, LoadLibraryEx just increments its refcount.
      //
      // Note that because of the race condition above, merely loading the DLL
      // again is not safe enough, therefore we also need to make sure that we
      // can read the memory mapped at the base address before we can safely
      // proceed to actually access those pages.
      HMODULE handleLock = LoadLibraryEx(module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
      MEMORY_BASIC_INFORMATION vmemInfo = {0};
      if (handleLock &&
          sizeof(vmemInfo) == VirtualQuery(module.modBaseAddr, &vmemInfo, sizeof(vmemInfo)) &&
          vmemInfo.State == MEM_COMMIT &&
          GetPdbInfo((uintptr_t)module.modBaseAddr, pdbSig, pdbAge, &pdbName)) {
        std::ostringstream stream;
        stream << pdbSig.ToString() << std::hex << pdbAge;
        std::string breakpadId = stream.str();
        std::string::iterator end =
          std::remove_if(breakpadId.begin(), breakpadId.end(), IsDashOrBraces);
        breakpadId.erase(end, breakpadId.end());
        std::transform(breakpadId.begin(), breakpadId.end(),
                       breakpadId.begin(), toupper);

        SharedLibrary shlib((uintptr_t)module.modBaseAddr,
                            (uintptr_t)module.modBaseAddr+module.modBaseSize,
                            0, // DLLs are always mapped at offset 0 on Windows
                            breakpadId,
                            pdbName);
        sharedLibraryInfo.AddSharedLibrary(shlib);
      }
      FreeLibrary(handleLock); // ok to free null handles
    } while (Module32Next(snap, &module));
  }

  return sharedLibraryInfo;
}