forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Transform ImagePreReader into PreReadFile (v2).
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
Showing
7 changed files
with
137 additions
and
504 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
Oops, something went wrong.