Commit 3f073d71 authored by Hans Wennborg's avatar Hans Wennborg Committed by Commit Bot

Add build config and docs for generating Windows link order files

There are three pieces to this puzzle:

1) Passing the -finstrument-functions-after-inlining flag to all
   compiler invocations.

2) Implementing the __cyg_profile_func_enter function.

3) Making all .exe and .dll targets link in the library that provides
   that function.

This patch implements those three, losely modeled on how the Android
build works, and adds documentation on how to use this to update the
order files.

Bug: 728324
Change-Id: I778bc8c0c2800244b8f18037197d62223ef92203
Reviewed-on: https://chromium-review.googlesource.com/783830
Commit-Queue: Hans Wennborg <hans@chromium.org>
Reviewed-by: default avatarBrett Wilson <brettw@chromium.org>
Reviewed-by: default avatarReid Kleckner <rnk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#519429}
parent 16fd4d8d
...@@ -269,6 +269,10 @@ group("exe_and_shlib_deps") { ...@@ -269,6 +269,10 @@ group("exe_and_shlib_deps") {
if (use_afl) { if (use_afl) {
public_deps += [ "//third_party/afl" ] public_deps += [ "//third_party/afl" ]
} }
if (is_win && generate_order_files && !is_nacl) {
public_deps += [ "//tools/cygprofile_win" ]
}
} }
# Executable configs ----------------------------------------------------------- # Executable configs -----------------------------------------------------------
......
...@@ -557,6 +557,11 @@ if (is_android) { ...@@ -557,6 +557,11 @@ if (is_android) {
[ "//build/config/android:default_cygprofile_instrumentation" ] [ "//build/config/android:default_cygprofile_instrumentation" ]
} }
if (is_win) {
default_compiler_configs +=
[ "//build/config/win:default_cygprofile_instrumentation" ]
}
if (is_clang && !is_nacl) { if (is_clang && !is_nacl) {
default_compiler_configs += [ default_compiler_configs += [
"//build/config/clang:find_bad_constructs", "//build/config/clang:find_bad_constructs",
......
...@@ -11,6 +11,10 @@ declare_args() { ...@@ -11,6 +11,10 @@ declare_args() {
# Break chrome.dll into multple pieces based on process type. Only available # Break chrome.dll into multple pieces based on process type. Only available
# on Windows. # on Windows.
is_multi_dll_chrome = is_win && !is_component_build is_multi_dll_chrome = is_win && !is_component_build
# Turn this on to generate order files. See
# https://chromium.googlesource.com/chromium/src/+/master/docs/win_order_files.md
generate_order_files = false
} }
# Refers to the subdirectory for branding in various places including # Refers to the subdirectory for branding in various places including
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import("//build/config/chrome_build.gni")
import("//build/config/clang/clang.gni") import("//build/config/clang/clang.gni")
import("//build/config/compiler/compiler.gni") import("//build/config/compiler/compiler.gni")
import("//build/config/sanitizers/sanitizers.gni") import("//build/config/sanitizers/sanitizers.gni")
...@@ -450,3 +451,21 @@ config("target_winrt") { ...@@ -450,3 +451,21 @@ config("target_winrt") {
"/EHsc", "/EHsc",
] ]
} }
# Generating order files -------------------------------------------------------
config("default_cygprofile_instrumentation") {
if (generate_order_files) {
assert(is_clang, "cygprofile instrumentation only works with clang")
assert(is_official_build, "order files should be made w/ official builds")
assert(!is_chrome_branded, "order files could leak internal symbol names")
configs = [ ":cygprofile_instrumentation" ]
}
}
config("cygprofile_instrumentation") {
cflags = [
"-Xclang",
"-finstrument-functions-after-inlining",
]
}
How to create the order files See
============================= https://chromium.googlesource.com/chromium/src/+/master/docs/win_order_files.md
TODO(hans): Add proper instructions here. for how to update the order files.
The current order files were created with this hack:
https://codereview.chromium.org/2924093003
# Updating the Windows .order files
The `chrome/build/*.orderfile` files are used to specify the order in which
the linker should lay out symbols in the binary it's producing. By ordering
functions in the order they're typically executed during start-up, the start-up
time can be reduced slightly.
The order files are used automatically when building with Clang for Windows with
the gn flag `is_official_build` set to `true`.
To update the order files:
1. Build with instrumentation enabled:
The instrumentation will capture the first million or so function calls
in a binary as it runs and write them to a file in the `\src\tmp` directory.
Make sure this directory exists.
```shell
gn gen out\instrument --args="is_debug=false is_official_build=true generate_order_files=true symbol_level=1"
ninja -C out\instrument chrome
```
(If you have access to Goma, add `use_goma=true symbol_level=1` to the gn
args and `-j500` to the Ninja invocation.)
1. Run the instrumented binaries:
(Some binaries such as `mksnapshot`, `yasm`, and `protoc` already ran with
instrumentation during the build process. The instrumentation output should
be available under `\src\tmp`.)
Open the Task Manager's Details tab or
[Process Explorer](https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer)
to be able to see the Process IDs of running programs.
Run Chrome with the sandbox disabled (otherwise the render process
instrumentation doesn't get written to disk) and with a startup dialog
for each renderer:
```shell
out\instrument\chrome --no-sandbox --renderer-startup-dialog
```
Note the Process IDs of the browser and render process (there is sometimes
more than one; you want the one that loads the New Tab Page).
Check in `\src\tmp\` for instrumentation output from those processes, for
example `cygprofile_14652.txt` and `cygprofile_23592.txt`. The files are
only written once a certain number of function calls have been made, so
sometimes the renderer needs to be reloaded in order for the file to be
produced.
1. If the files appear to have sensible contents (a long list of function names
that eventually seem related to what the browser and render process should
do), copy them into `chrome\build\`:
```shell
copy \src\tmp\cygprofile_25392.txt chrome\build\chrome.x64.orderfile
copy \src\tmp\cygprofile_14652.txt chrome\build\chrome_child.x64.orderfile
```
1. Repeat the previous steps with a 32-bit build, i.e. passing `target_cpu="x86"` to gn and storing the files as `.x86.orderfile`.
1. Upload the order files to Google Cloud Storage. They will get downloaded
by a `gclient` hook based on the contents of the `.orderfile.sha1` files.
You need to have write access to the `chromium-browser-clang` GCS bucket
for this step.
```shell
cd chrome\build\
upload_to_google_storage.py -b chromium-browser-clang/orderfiles chrome.x64.orderfile chrome.x86.orderfile chrome_child.x64.orderfile chrome_child.x86.orderfile
gsutil.py setacl public-read gs://chromium-browser-clang/orderfiles/*
```
1. Check in the `.sha1` files corresponding to the orderfiles, created by the
previous step.
# Copyright 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.
static_library("cygprofile_win") {
sources = [
"cygprofile.cc",
]
# Don't instrument this library.
configs -= [ "//build/config/win:default_cygprofile_instrumentation" ]
}
// 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"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment