Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(546)

Side by Side Diff: base/file_util_win.cc

Issue 9235053: Add a PartialPreReadImage function to file_util (on Windows). (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Initial CL for Review Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« base/file_util_unittest.cc ('K') | « base/file_util_unittest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/file_util.h" 5 #include "base/file_util.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <propvarutil.h> 8 #include <propvarutil.h>
9 #include <psapi.h> 9 #include <psapi.h>
10 #include <shellapi.h> 10 #include <shellapi.h>
11 #include <shlobj.h> 11 #include <shlobj.h>
12 #include <time.h> 12 #include <time.h>
13 13
14 #include <algorithm>
14 #include <limits> 15 #include <limits>
15 #include <string> 16 #include <string>
16 17
17 #include "base/file_path.h" 18 #include "base/file_path.h"
18 #include "base/logging.h" 19 #include "base/logging.h"
20 #include "base/memory/scoped_ptr.h"
19 #include "base/metrics/histogram.h" 21 #include "base/metrics/histogram.h"
20 #include "base/string_number_conversions.h" 22 #include "base/string_number_conversions.h"
21 #include "base/string_util.h" 23 #include "base/string_util.h"
22 #include "base/threading/thread_restrictions.h" 24 #include "base/threading/thread_restrictions.h"
23 #include "base/time.h" 25 #include "base/time.h"
24 #include "base/utf_string_conversions.h" 26 #include "base/utf_string_conversions.h"
25 #include "base/win/pe_image.h" 27 #include "base/win/pe_image.h"
26 #include "base/win/scoped_comptr.h" 28 #include "base/win/scoped_comptr.h"
27 #include "base/win/scoped_handle.h" 29 #include "base/win/scoped_handle.h"
28 #include "base/win/win_util.h" 30 #include "base/win/win_util.h"
(...skipping 1135 matching lines...) Expand 10 before | Expand all | Expand 10 after
1164 while (offset < actual_size_to_read) { 1166 while (offset < actual_size_to_read) {
1165 uint8 unused = *(touch + offset); 1167 uint8 unused = *(touch + offset);
1166 offset += step_size; 1168 offset += step_size;
1167 } 1169 }
1168 FreeLibrary(dll_module); 1170 FreeLibrary(dll_module);
1169 } 1171 }
1170 1172
1171 return true; 1173 return true;
1172 } 1174 }
1173 1175
1176 namespace internal {
chrisha 2012/01/26 18:23:09 namespace internal is normally for .h files. Why n
Roger McFarlane (Google) 2012/01/27 05:24:08 Because then they wouldn't be accessible for unitt
1177
1178 struct ScopedPtrVirtualFree {
1179 void operator() (void* ptr) {
1180 ::VirtualFree(ptr, 0, MEM_RELEASE);
1181 }
1182 };
1183
1184 bool SetFilePointer(HANDLE file_handle, size_t position) {
1185 return position <= std::numeric_limits<LONG>::max() &&
1186 ::SetFilePointer(file_handle,
1187 static_cast<LONG>(position),
1188 NULL,
1189 FILE_BEGIN) != INVALID_SET_FILE_POINTER;
1190 }
1191
1192 bool ReadNextBytes(HANDLE file_handle, void* buffer, size_t bytes_to_read) {
1193 DWORD bytes_read = 0;
1194 return bytes_to_read <= std::numeric_limits<DWORD>::max() &&
1195 ::ReadFile(file_handle,
1196 buffer,
1197 static_cast<DWORD>(bytes_to_read),
1198 &bytes_read,
1199 NULL) &&
1200 bytes_read == bytes_to_read;
1201 }
1202
1203 BASE_EXPORT bool PartialPreReadPeFile(const wchar_t* file_path,
1204 size_t percentage,
1205 size_t max_chunk_size) {
1206 // TODO(rogerm): change this to be driven by a table within the PE file
1207 // (defaulting to full read if it's not there?) that's initialized
1208 // by the optimization toolchain.
chrisha 2012/01/26 18:23:09 Maybe break this down further? A PreReadChunk func
Roger McFarlane (Google) 2012/01/27 05:24:08 Refactored along the lines you suggest. I haven't
1209 DCHECK(file_path != NULL);
1210
1211 if (percentage == 0)
1212 return true;
1213
1214 // Validate/setup the percentage and max_chunk_size.
1215 const size_t kOneHundredPercent = 100;
1216 const size_t kMinChunkSize = 1024 * 1024;
1217 percentage = std::min(percentage, kOneHundredPercent);
1218 max_chunk_size = std::max(max_chunk_size, kMinChunkSize);
1219
1220 // On Windows Vista and higher, we sequentially read file contents with an
Sigurður Ásgeirsson 2012/01/26 18:59:20 copy/paste?
Roger McFarlane (Google) 2012/01/27 05:24:08 At some point the comment still seemed relevant.
1221 // optional cap on total percentage to read.
1222 base::win::ScopedHandle file(
1223 CreateFile(file_path,
1224 GENERIC_READ,
1225 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1226 NULL,
1227 OPEN_EXISTING,
1228 FILE_FLAG_SEQUENTIAL_SCAN,
1229 NULL));
1230
1231 if (!file.IsValid())
1232 return false;
1233
1234 // Allocate a resizable buffer for the headers. We initially reserve as much
1235 // space as we typically see as the header size for chrome.dll.
1236 const size_t kInitialBufferSize = 0x400;
1237 std::string headers(kInitialBufferSize, char());
Sigurður Ásgeirsson 2012/01/26 18:59:20 nit: I like std::vector<uint8> or such for buffers
Roger McFarlane (Google) 2012/01/27 05:24:08 Done.
1238
1239 // Read, hopefully, all of the headers.
1240 if (!ReadNextBytes(file, &headers[0], headers.size()))
1241 return false;
1242
1243 // The DOS headers start at offset 0, which allow us to get the offset of the
1244 // NT headers. Let's ensure we've read enough to capture the NT headers.
1245 size_t nt_headers_start =
1246 reinterpret_cast<IMAGE_DOS_HEADER*>(&headers[0])->e_lfanew;
1247 size_t nt_headers_end = nt_headers_start + sizeof(IMAGE_NT_HEADERS);
1248 if (nt_headers_end > headers.size()) {
1249 size_t current_length = headers.size();
1250 size_t bytes_to_read = nt_headers_end - current_length;
1251 headers.resize(nt_headers_end);
1252 if (!ReadNextBytes(file, &headers[current_length], bytes_to_read))
1253 return false;
1254 }
1255
1256 // Now that we've got the NT headers we can get the total header size,
1257 // including all of the section headers. Let's ensure we've read enough
1258 // to capture all of the section headers.
1259 size_t size_of_headers = reinterpret_cast<IMAGE_NT_HEADERS*>(
1260 &headers[nt_headers_start])->OptionalHeader.SizeOfHeaders;
1261 if (size_of_headers > headers.size()) {
1262 size_t current_length = headers.size();
1263 size_t bytes_to_read = size_of_headers - current_length;
1264 headers.resize(size_of_headers);
1265 if (!ReadNextBytes(file, &headers[current_length], bytes_to_read))
1266 return false;
1267 }
1268
1269 // Now we have all of the headers.
1270 const IMAGE_DOS_HEADER* dos_header =
1271 reinterpret_cast<IMAGE_DOS_HEADER*>(&headers[0]);
1272 const IMAGE_NT_HEADERS* nt_headers =
1273 reinterpret_cast<IMAGE_NT_HEADERS*>(&headers[dos_header->e_lfanew]);
1274 const IMAGE_SECTION_HEADER* section_headers = IMAGE_FIRST_SECTION(nt_headers);
chrisha 2012/01/26 18:23:09 Could break out the header reading functionality t
Sigurður Ásgeirsson 2012/01/26 18:59:20 You can construct a PEImage instance on the buffer
Roger McFarlane (Google) 2012/01/27 05:24:08 Done. Thanks!
Roger McFarlane (Google) 2012/01/27 05:24:08 I've done some refactoring, PTAL.
1275
1276 // Allocate a buffer to hold the pre-read bytes.
1277 scoped_ptr_malloc<uint8, ScopedPtrVirtualFree> buffer(
1278 reinterpret_cast<uint8*>(
1279 ::VirtualAlloc(NULL, max_chunk_size, MEM_COMMIT, PAGE_READWRITE)));
1280 if (buffer.get() == NULL)
1281 return false;
1282
1283 // Iterate over each section, reading in a percentage of each.
1284 for (size_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i) {
1285 if (!SetFilePointer(file, section_headers[i].PointerToRawData))
1286 return false;
1287 size_t max_bytes =
1288 (section_headers[i].SizeOfRawData * percentage) / kOneHundredPercent;
1289 while (max_bytes > 0) {
1290 size_t chunk_size = std::min(max_bytes, max_chunk_size);
1291 if (!ReadNextBytes(file, buffer.get(), chunk_size))
1292 return false;
1293 max_bytes -= chunk_size;
1294 }
1295 }
1296
1297 // We're done.
1298 return true;
1299 }
1300
1301 BASE_EXPORT bool PartialPreReadPeImage(const wchar_t* file_path,
1302 size_t percentage,
1303 size_t step_size) {
1304 // TODO(rogerm): change this to be driven by a table within the PE image
1305 // (defaulting to full read if it's not there?) that's initialized
1306 // by the optimization toolchain.
1307 DCHECK(file_path != NULL);
1308
1309 if (percentage == 0)
1310 return true;
1311
1312 // Validate/setup the percentage and step_size.
1313 const size_t kOneHundredPercent = 100;
1314 const size_t kMaxStepSize = 4096;
1315 percentage = std::min(percentage, kOneHundredPercent);
1316 step_size = std::min(step_size, kMaxStepSize);
Sigurður Ásgeirsson 2012/01/26 18:59:20 I don't know that it's worth parametrizing the str
Roger McFarlane (Google) 2012/01/27 05:24:08 Done. Switched to always using the system page siz
1317
1318 // WinXP branch. Here, reading the DLL from disk doesn't do
Sigurður Ásgeirsson 2012/01/26 18:59:20 copy/paste?
Roger McFarlane (Google) 2012/01/27 05:24:08 At some point the comment still seemed relevant.
1319 // what we want so instead we pull the pages into memory by loading
1320 // the DLL and touching pages at a stride.
1321 HMODULE dll_module = ::LoadLibraryExW(
1322 file_path,
1323 NULL,
1324 LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES);
1325
1326 if (!dll_module)
1327 return false;
1328
1329 // Iterate over each section, stepping through a percentage of each to page
1330 // it in off the disk.
1331 base::win::PEImage pe_image(dll_module);
Sigurður Ásgeirsson 2012/01/26 18:59:20 May want to CHECK(pe_image.VerifyMagic())?
Roger McFarlane (Google) 2012/01/27 05:24:08 Done.
1332 for (UINT i = 0; i < pe_image.GetNumSections(); ++i) {
1333 // Get the current section and figure out how much of it to touch.
1334 const IMAGE_SECTION_HEADER* section = pe_image.GetSectionHeader(i);
1335 DCHECK(section != NULL);
1336 volatile uint8* touch =
1337 reinterpret_cast<uint8*>(pe_image.RVAToAddr(section->VirtualAddress));
1338 size_t section_length =
Sigurður Ásgeirsson 2012/01/26 18:59:20 Maybe a one-line comment on why we don't only use
Roger McFarlane (Google) 2012/01/27 05:24:08 Done.
1339 std::min(section->SizeOfRawData, section->Misc.VirtualSize);
1340 volatile uint8* max_touch =
1341 touch + ((section_length * percentage) / kOneHundredPercent) - 1;
1342
1343 // Verify that the extent we're going to touch falls inside the section
1344 // we expect it to (and by implication, inside the pe_image).
1345 DCHECK_EQ(section,
1346 pe_image.GetImageSectionFromAddr(const_cast<uint8*>(touch)));
1347 DCHECK_EQ(section,
1348 pe_image.GetImageSectionFromAddr(const_cast<uint8*>(max_touch)));
1349
1350 // Read the memory in the range [touch, max_touch] with a stride of
1351 // step_size. This ensures that it's been paged in.
1352 uint8 unused = *touch;
1353 while (touch < max_touch) {
1354 touch = std::max(touch + step_size, max_touch);
Sigurður Ásgeirsson 2012/01/26 18:59:20 I don't understand, this would always return max_t
Roger McFarlane (Google) 2012/01/27 05:24:08 Oops, should have been std::min()... Nice catch!
1355 unused = *touch;
1356 }
1357 }
1358
1359 FreeLibrary(dll_module);
1360
1361 return true;
1362 }
1363
1364 } // namespace file_util::internal
1365
1366 bool PartialPreReadImage(const wchar_t* file_path,
chrisha 2012/01/26 18:23:09 Confusing function names: PartialPreReadImage vs P
Roger McFarlane (Google) 2012/01/27 05:24:08 Renamed. Are the new names better?
1367 size_t percentage,
1368 size_t step_size) {
1369 base::ThreadRestrictions::AssertIOAllowed();
1370
1371 if (base::win::GetVersion() > base::win::VERSION_XP) {
1372 // Vista+ branch. On these OSes, the forced reads through the DLL actually
1373 // slows warm starts. The solution is to sequentially read file contents
1374 // with an optional cap on total amount to read.
1375 return internal::PartialPreReadPeFile(file_path, percentage, step_size);
1376 }
1377
1378 // WinXP branch. Here, reading the DLL from disk doesn't do what we want
1379 // so instead we pull the pages into memory by loading the DLL and touching
1380 // pages at a stride.
1381 return internal::PartialPreReadPeImage(file_path, percentage, step_size);
1382 }
1383
1174 } // namespace file_util 1384 } // namespace file_util
OLDNEW
« base/file_util_unittest.cc ('K') | « base/file_util_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698