1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Copied from Chromium's /src/tools/cygprofile_win/cygprofile.cc.
10 #include <unordered_set>
12 #include <windows.h> // Needs to be included before the others.
17 #include "mozilla/Sprintf.h"
18 #include "mozilla/Types.h"
22 // The main purpose of the order file is to optimize startup time,
23 // so capturing the first N function calls is enough.
24 static constexpr int kSamplesCapacity
= 25 * 1024 * 1024;
26 void* samples
[kSamplesCapacity
];
27 std::atomic_int num_samples
;
30 // Symbolize the samples and write them to disk.
32 HMODULE dbghelp
= LoadLibraryA("dbghelp.dll");
33 auto sym_from_addr
= reinterpret_cast<decltype(::SymFromAddr
)*>(
34 ::GetProcAddress(dbghelp
, "SymFromAddr"));
35 auto sym_initialize
= reinterpret_cast<decltype(::SymInitialize
)*>(
36 ::GetProcAddress(dbghelp
, "SymInitialize"));
37 auto sym_set_options
= reinterpret_cast<decltype(::SymSetOptions
)*>(
38 ::GetProcAddress(dbghelp
, "SymSetOptions"));
40 // Path to the dump file. %s will be substituted by objdir path.
41 static const char kDumpFile
[] = "%s/cygprofile.txt";
43 char filename
[MAX_PATH
];
44 const char* objdir
= ::getenv("MOZ_OBJDIR");
47 fprintf(stderr
, "ERROR: cannot determine objdir\n");
51 SprintfLiteral(filename
, kDumpFile
, objdir
);
53 FILE* f
= fopen(filename
, "w");
55 fprintf(stderr
, "ERROR: Cannot open %s\n", filename
);
59 sym_initialize(::GetCurrentProcess(), NULL
, TRUE
);
60 sym_set_options(SYMOPT_DEFERRED_LOADS
| SYMOPT_PUBLICS_ONLY
);
61 char sym_buf
[sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
* sizeof(TCHAR
)];
63 std::unordered_set
<void*> seen
;
64 std::unordered_set
<std::string
> seen_names
;
66 for (void* sample
: samples
) {
67 // Only print the first call of a function.
68 if (seen
.count(sample
)) continue;
71 SYMBOL_INFO
* symbol
= reinterpret_cast<SYMBOL_INFO
*>(sym_buf
);
72 symbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
73 symbol
->MaxNameLen
= MAX_SYM_NAME
;
76 if (sym_from_addr(::GetCurrentProcess(), reinterpret_cast<DWORD64
>(sample
),
78 const char* name
= symbol
->Name
;
79 if (name
[0] == '_') name
++;
80 if (seen_names
.count(name
)) continue;
81 seen_names
.insert(name
);
83 fprintf(f
, "%s\n", name
);
94 MOZ_EXPORT
void __cyg_profile_func_enter(void* this_fn
,
95 void* call_site_unused
) {
98 // Get our index for the samples array atomically.
99 int n
= num_samples
++;
101 if (n
< kSamplesCapacity
) {
102 samples
[n
] = this_fn
;
104 if (n
+ 1 == kSamplesCapacity
) {
105 // This is the final sample; start dumping the samples to a file (on a
106 // separate thread so as not to disturb the main program).
108 _beginthread(dump
, 0, nullptr);
113 MOZ_EXPORT
void __cyg_profile_func_exit(void* this_fn
, void* call_site
) {}