summaryrefslogtreecommitdiff
path: root/chromium/tools/cygprofile_win/cygprofile.cc
blob: 87045a75fde98daac8cbacbeaddef695f388e62b (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
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdio.h>
#include <atomic>
#include <string>
#include <unordered_set>

#include <windows.h>  // Needs to be included before the others.

#include <dbghelp.h>
#include <process.h>

namespace {

// The main purpose of the order file is to optimize startup time,
// so capturing the first N function calls is enough.
static constexpr int kSamplesCapacity = 25 * 1024 * 1024;

void* samples[kSamplesCapacity];
std::atomic_int num_samples;
std::atomic_int done;

// Path to the dump file. %lu will be substituted by the process id.
static const char kDumpFile[] = "/src/tmp/cygprofile_%lu.txt";

// Symbolize the samples and write them to disk.
void dump(void*) {
  HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
  auto sym_from_addr = reinterpret_cast<decltype(::SymFromAddr)*>(
      ::GetProcAddress(dbghelp, "SymFromAddr"));
  auto sym_initialize = reinterpret_cast<decltype(::SymInitialize)*>(
      ::GetProcAddress(dbghelp, "SymInitialize"));
  auto sym_set_options = reinterpret_cast<decltype(::SymSetOptions)*>(
      ::GetProcAddress(dbghelp, "SymSetOptions"));

  char filename[MAX_PATH];
  snprintf(filename, sizeof(filename), kDumpFile, ::GetCurrentProcessId());
  FILE* f = fopen(filename, "w");
  if (!f)
    return;

  sym_initialize(::GetCurrentProcess(), NULL, TRUE);
  sym_set_options(SYMOPT_DEFERRED_LOADS | SYMOPT_PUBLICS_ONLY);
  char sym_buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];

  std::unordered_set<void*> seen;
  std::unordered_set<std::string> seen_names;

  for (void* sample : samples) {
    // Only print the first call of a function.
    if (seen.count(sample))
      continue;
    seen.insert(sample);

    SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(sym_buf);
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    symbol->MaxNameLen = MAX_SYM_NAME;
    DWORD64 offset = 0;

    if (sym_from_addr(::GetCurrentProcess(), reinterpret_cast<DWORD64>(sample),
                      &offset, symbol)) {
      const char* name = symbol->Name;
      if (name[0] == '_')
        name++;
      if (seen_names.count(name))
        continue;
      seen_names.insert(name);

      fprintf(f, "%s\n", name);
    }
  }

  fclose(f);
}

}  // namespace

extern "C" {

void __cyg_profile_func_enter(void* this_fn, void* call_site_unused) {
  if (done)
    return;

  // Get our index for the samples array atomically.
  int n = num_samples++;

  if (n < kSamplesCapacity) {
    samples[n] = this_fn;

    if (n + 1 == kSamplesCapacity) {
      // This is the final sample; start dumping the samples to a file (on a
      // separate thread so as not to disturb the main program).
      done = 1;
      _beginthread(dump, 0, nullptr);
    }
  }
}

void __cyg_profile_func_exit(void* this_fn, void* call_site) {}

}  // extern "C"