Skip to content

Commit

Permalink
Transform ImagePreReader into PreReadFile (v2).
Browse files Browse the repository at this point in the history
We can reduce startup time by pre-reading different non-PE files
(e.g. ChromeDWriteFontCache, GPU cache, resources).

This CL transforms ImagePreReader into PreReadFile, which can
pre-read any file type on Vista+. On XP, PreReadFile can only
pre-read PE files.

A similar patch was already submitted
(https://codereview.chromium.org/1412673006/). It was reverted because
of an XP-only perf regression (crbug.com/551488). This new version
loads files in memory using ::LoadLibraryExW instead of
base::MemoryMappedFile on XP. This was the existing behavior of
ImagePreReader, so it should no longer cause a perf regression.
Unfortunately, it means that on XP, only PE files can be pre-read.

BUG=547794

Review URL: https://codereview.chromium.org/1432973004

Cr-Commit-Position: refs/heads/master@{#358917}
  • Loading branch information
fdoray authored and Commit bot committed Nov 10, 2015
1 parent f2fb28d commit ab55daf
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 504 deletions.
8 changes: 4 additions & 4 deletions chrome/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ if (!is_android) {
deps += [
":chrome_dll",
":chrome_exe_version",
":image_pre_reader",
":file_pre_reader",

# 'chrome_nacl_win64" TODO(GYP) bug 512869
# '../win8/delegate_execute/delegate_execute.gyp:*', TODO(GYP) bug 512867
Expand Down Expand Up @@ -517,10 +517,10 @@ if (is_win) {
output = "$target_gen_dir/other_version.rc"
}

source_set("image_pre_reader") {
source_set("file_pre_reader") {
sources = [
"app/image_pre_reader_win.cc",
"app/image_pre_reader_win.h",
"app/file_pre_reader_win.cc",
"app/file_pre_reader_win.h",
]
deps = [
"//base",
Expand Down
104 changes: 104 additions & 0 deletions chrome/app/file_pre_reader_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) 2012 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 "chrome/app/file_pre_reader_win.h"

#include <windows.h>
#include <stdint.h>

#include "base/files/file.h"
#include "base/logging.h"
#include "base/scoped_native_library.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/pe_image.h"
#include "base/win/windows_version.h"

namespace {

// A helper function to touch all pages in the range
// [base_addr, base_addr + length).
void TouchPagesInRange(const void* base_addr, uint32_t length) {
DCHECK(base_addr);
DCHECK_GT(length, static_cast<uint32_t>(0));

// Get the system info so we know the page size. Also, make sure we use a
// non-zero value for the page size; GetSystemInfo() is hookable/patchable,
// and you never know what shenanigans someone could get up to.
SYSTEM_INFO system_info = {};
::GetSystemInfo(&system_info);
if (system_info.dwPageSize == 0)
system_info.dwPageSize = 4096;

// We don't want to read outside the byte range (which could trigger an
// access violation), so let's figure out the exact locations of the first
// and final bytes we want to read.
volatile uint8_t const* touch_ptr =
reinterpret_cast<uint8_t const*>(base_addr);
volatile uint8_t const* final_touch_ptr = touch_ptr + length - 1;

// Read the memory in the range [touch_ptr, final_touch_ptr] with a stride
// of the system page size, to ensure that it's been paged in.
uint8_t dummy;
for (; touch_ptr < final_touch_ptr; touch_ptr += system_info.dwPageSize)
dummy = *touch_ptr;
dummy = *final_touch_ptr;
}

} // namespace

bool PreReadFile(const base::FilePath& file_path, int step_size) {
DCHECK_GT(step_size, 0);
base::ThreadRestrictions::AssertIOAllowed();

if (base::win::GetVersion() > base::win::VERSION_XP) {
// Vista+ branch. On these OSes, the forced reads through the file actually
// slows warm starts. The solution is to sequentially read file contents.
base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_SEQUENTIAL_SCAN);
if (!file.IsValid())
return false;

char* buffer = reinterpret_cast<char*>(::VirtualAlloc(
nullptr, static_cast<DWORD>(step_size), MEM_COMMIT, PAGE_READWRITE));
if (!buffer)
return false;

while (file.ReadAtCurrentPos(buffer, step_size) > 0) {}

::VirtualFree(buffer, 0, MEM_RELEASE);
} else {
// WinXP branch. Here, reading the DLL from disk doesn't do what we want so
// instead we pull the pages into memory and touch pages at a stride. We use
// the system's page size as the stride, ignoring the passed in |step_size|,
// to make sure each page in the range is touched.

// Don't show an error popup when |file_path| is not a valid PE file.
UINT previous_error_mode = ::SetErrorMode(SEM_FAILCRITICALERRORS);
::SetErrorMode(previous_error_mode | SEM_FAILCRITICALERRORS);

base::ScopedNativeLibrary dll_module(::LoadLibraryExW(
file_path.value().c_str(), NULL,
LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES));

::SetErrorMode(previous_error_mode);

// Pre-reading non-PE files is not supported on XP.
if (!dll_module.is_valid())
return false;

base::win::PEImage pe_image(dll_module.get());
if (!pe_image.VerifyMagic())
return false;

// We don't want to read past the end of the module (which could trigger
// an access violation), so make sure to check the image size.
PIMAGE_NT_HEADERS nt_headers = pe_image.GetNTHeaders();
const uint32_t dll_module_length = nt_headers->OptionalHeader.SizeOfImage;

// Page in the module.
TouchPagesInRange(dll_module.get(), dll_module_length);
}

return true;
}
23 changes: 23 additions & 0 deletions chrome/app/file_pre_reader_win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2012 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.

// This file defines a function to pre-read a file in order to avoid touching
// the disk when it is subsequently used.

#ifndef CHROME_APP_FILE_PRE_READER_WIN_H_
#define CHROME_APP_FILE_PRE_READER_WIN_H_

namespace base {
class FilePath;
}

// Reads |file_path| to avoid touching the disk when the file is actually used.
// The function checks the Windows version to determine which pre-reading
// mechanism to use. On Vista+, chunks of |step_size| bytes are read into a
// buffer. The bigger |step_size| is, the faster the file is pre-read (up to
// about 4MB according to local tests), but also the more memory is allocated
// for the buffer. On XP, pre-reading non-PE files is not supported.
bool PreReadFile(const base::FilePath& file_path, int step_size);

#endif // CHROME_APP_FILE_PRE_READER_WIN_H_
Loading

0 comments on commit ab55daf

Please sign in to comment.