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

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: Address feedback from siggi and chrisha 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.h ('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 1110 matching lines...) Expand 10 before | Expand all | Expand 10 after
1139 size_t total_read = 0; 1141 size_t total_read = 0;
1140 while (::ReadFile(file, buffer, actual_step_size, &len, NULL) && 1142 while (::ReadFile(file, buffer, actual_step_size, &len, NULL) &&
1141 len > 0 && 1143 len > 0 &&
1142 (size_to_read ? total_read < size_to_read : true)) { 1144 (size_to_read ? total_read < size_to_read : true)) {
1143 total_read += static_cast<size_t>(len); 1145 total_read += static_cast<size_t>(len);
1144 } 1146 }
1145 ::VirtualFree(buffer, 0, MEM_RELEASE); 1147 ::VirtualFree(buffer, 0, MEM_RELEASE);
1146 } else { 1148 } else {
1147 // WinXP branch. Here, reading the DLL from disk doesn't do 1149 // WinXP branch. Here, reading the DLL from disk doesn't do
1148 // what we want so instead we pull the pages into memory by loading 1150 // what we want so instead we pull the pages into memory by loading
1149 // the DLL and touching pages at a stride. 1151 // the DLL and touching pages at a stride.
robertshield 2012/01/26 22:43:44 Consider adding a note indicating that we intentio
Roger McFarlane (Google) 2012/01/27 05:24:09 Done.
1150 HMODULE dll_module = ::LoadLibraryExW( 1152 HMODULE dll_module = ::LoadLibraryExW(
1151 file_path, 1153 file_path,
1152 NULL, 1154 NULL,
1153 LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES); 1155 LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES);
1154 1156
1155 if (!dll_module) 1157 if (!dll_module)
1156 return false; 1158 return false;
1157 1159
1158 base::win::PEImage pe_image(dll_module); 1160 base::win::PEImage pe_image(dll_module);
1159 PIMAGE_NT_HEADERS nt_headers = pe_image.GetNTHeaders(); 1161 PIMAGE_NT_HEADERS nt_headers = pe_image.GetNTHeaders();
1162 SYSTEM_INFO system_info;
1163 GetSystemInfo(&system_info);
1164
1160 size_t actual_size_to_read = size_to_read ? size_to_read : 1165 size_t actual_size_to_read = size_to_read ? size_to_read :
1161 nt_headers->OptionalHeader.SizeOfImage; 1166 nt_headers->OptionalHeader.SizeOfImage;
1162 volatile uint8* touch = reinterpret_cast<uint8*>(dll_module); 1167 volatile uint8* touch = reinterpret_cast<uint8*>(dll_module);
1163 size_t offset = 0; 1168 size_t offset = 0;
1164 while (offset < actual_size_to_read) { 1169 while (offset < actual_size_to_read) {
1165 uint8 unused = *(touch + offset); 1170 uint8 unused = *(touch + offset);
1166 offset += step_size; 1171 offset += system_info.dwPageSize;
1167 } 1172 }
1173 uint8 unused2 = *(touch + size_to_read - 1);
1168 FreeLibrary(dll_module); 1174 FreeLibrary(dll_module);
1169 } 1175 }
1170 1176
1171 return true; 1177 return true;
1172 } 1178 }
1173 1179
1180 namespace {
1181
1182 struct ScopedPtrVirtualFree {
1183 void operator() (void* ptr) {
1184 ::VirtualFree(ptr, 0, MEM_RELEASE);
1185 }
1186 };
1187
1188 bool SetFilePointer(HANDLE file_handle, size_t position) {
1189 return position <= std::numeric_limits<LONG>::max() &&
1190 ::SetFilePointer(file_handle,
1191 static_cast<LONG>(position),
1192 NULL,
1193 FILE_BEGIN) != INVALID_SET_FILE_POINTER;
1194 }
1195
1196 bool ReadNextBytes(HANDLE file_handle, void* buffer, size_t bytes_to_read) {
1197 DWORD bytes_read = 0;
1198 return bytes_to_read <= std::numeric_limits<DWORD>::max() &&
1199 ::ReadFile(file_handle,
1200 buffer,
1201 static_cast<DWORD>(bytes_to_read),
1202 &bytes_read,
1203 NULL) &&
1204 bytes_read == bytes_to_read;
1205 }
1206
1207 } // namespace
1208
1209 namespace internal {
1210
1211 BASE_EXPORT bool PartialPreReadImageOnDisk(const wchar_t* file_path,
1212 size_t percentage,
1213 size_t max_chunk_size) {
1214 // TODO(rogerm): change this to be driven by a table within the PE file
1215 // (defaulting to full read if it's not there?) that's initialized
1216 // by the optimization toolchain.
1217 DCHECK(file_path != NULL);
1218
1219 if (percentage == 0)
1220 return true;
1221
1222 const size_t kOneHundredPercent = 100;
1223 if (percentage > kOneHundredPercent)
1224 percentage = kOneHundredPercent;
1225
1226 // Validate/setup the percentage and max_chunk_size.
1227 const size_t kMinChunkSize = 1024 * 1024;
1228 percentage = std::min(percentage, kOneHundredPercent);
robertshield 2012/01/26 22:43:44 This line is redundant with lines 1223-1224.
Roger McFarlane (Google) 2012/01/27 05:24:09 Oops, was supposed to have been deleted.
1229 max_chunk_size = std::max(max_chunk_size, kMinChunkSize);
1230
1231 // Open the file.
1232 base::win::ScopedHandle file(
1233 CreateFile(file_path,
1234 GENERIC_READ,
1235 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1236 NULL,
1237 OPEN_EXISTING,
1238 FILE_FLAG_SEQUENTIAL_SCAN,
1239 NULL));
1240
1241 if (!file.IsValid())
1242 return false;
1243
1244 // Allocate a resizable buffer for the headers. We initially reserve as much
1245 // space as we typically see as the header size for chrome.dll and other
1246 // PE images.
1247 const size_t kInitialBufferSize = 0x400;
1248 std::vector<uint8> headers(kInitialBufferSize);
1249
1250 // Read, hopefully, all of the headers.
1251 if (!ReadNextBytes(file, &headers[0], headers.size()))
1252 return false;
1253
1254 // The DOS headers start at offset 0, which allow us to get the offset of the
1255 // NT headers. Let's ensure we've read enough to capture the NT headers.
1256 size_t nt_headers_start =
1257 reinterpret_cast<IMAGE_DOS_HEADER*>(&headers[0])->e_lfanew;
1258 size_t nt_headers_end = nt_headers_start + sizeof(IMAGE_NT_HEADERS);
1259 if (nt_headers_end > headers.size()) {
1260 size_t current_length = headers.size();
1261 size_t bytes_to_read = nt_headers_end - current_length;
1262 headers.resize(nt_headers_end);
1263 if (!ReadNextBytes(file, &headers[current_length], bytes_to_read))
1264 return false;
1265 }
1266
1267 // Now that we've got the NT headers we can get the total header size,
1268 // including all of the section headers. Let's ensure we've read enough
1269 // to capture all of the section headers.
1270 size_t size_of_headers = reinterpret_cast<IMAGE_NT_HEADERS*>(
1271 &headers[nt_headers_start])->OptionalHeader.SizeOfHeaders;
1272 if (size_of_headers > headers.size()) {
1273 size_t current_length = headers.size();
1274 size_t bytes_to_read = size_of_headers - current_length;
1275 headers.resize(size_of_headers);
1276 if (!ReadNextBytes(file, &headers[current_length], bytes_to_read))
1277 return false;
1278 }
1279
1280 // Now we have all of the headers. This is enough to let us use the PEImage
1281 // wrapper to query the structure of the image.
1282 base::win::PEImage pe_image(reinterpret_cast<HMODULE>(&headers[0]));
1283 CHECK(pe_image.VerifyMagic());
1284
1285 // Allocate a buffer to hold the pre-read bytes.
1286 scoped_ptr_malloc<uint8, ScopedPtrVirtualFree> buffer(
1287 reinterpret_cast<uint8*>(
1288 ::VirtualAlloc(NULL, max_chunk_size, MEM_COMMIT, PAGE_READWRITE)));
1289 if (buffer.get() == NULL)
1290 return false;
1291
1292 // Iterate over each section, reading in a percentage of each.
1293 for (UINT i = 0; i < pe_image.GetNumSections(); ++i) {
1294 // Get the current section and figure out how much of it to touch.
1295 const IMAGE_SECTION_HEADER* section = pe_image.GetSectionHeader(i);
1296 CHECK(section != NULL);
1297 CHECK_LE(reinterpret_cast<const uint8*>(section + 1),
1298 &headers[0] + headers.size());
1299
1300 if (!SetFilePointer(file, section->PointerToRawData))
1301 return false;
1302
1303 size_t max_bytes =
1304 (section->SizeOfRawData * percentage) / kOneHundredPercent;
1305 while (max_bytes > 0) {
1306 size_t chunk_size = std::min(max_bytes, max_chunk_size);
1307 if (!ReadNextBytes(file, buffer.get(), chunk_size))
1308 return false;
1309 max_bytes -= chunk_size;
1310 }
1311 }
1312
1313 // We're done.
1314 return true;
1315 }
1316
1317 BASE_EXPORT bool PartialPreReadImageInMemory(const wchar_t* file_path,
1318 size_t percentage) {
1319 // TODO(rogerm): change this to be driven by a table within the PE image
1320 // (defaulting to full read if it's not there?) that's initialized
1321 // by the optimization toolchain.
1322 DCHECK(file_path != NULL);
1323
1324 if (percentage == 0)
1325 return true;
1326
1327 const size_t kOneHundredPercent = 100;
robertshield 2012/01/26 22:43:44 consider pulling this up out of the function since
Roger McFarlane (Google) 2012/01/27 05:24:09 Done.
1328 if (percentage > kOneHundredPercent)
1329 percentage = kOneHundredPercent;
1330
1331 HMODULE dll_module = ::LoadLibraryExW(
1332 file_path,
1333 NULL,
1334 LOAD_WITH_ALTERED_SEARCH_PATH | DONT_RESOLVE_DLL_REFERENCES);
1335
1336 if (!dll_module)
1337 return false;
1338
1339 base::win::PEImage pe_image(dll_module);
1340 CHECK(pe_image.VerifyMagic());
1341
1342 // Iterate over each section, stepping through a percentage of each to page
1343 // it in off the disk.
1344 for (UINT i = 0; i < pe_image.GetNumSections(); ++i) {
1345 // Get the current section and figure out how much of it to touch. We
1346 // start from the beginning of the section, and step through all of the
1347 // initialized (on disk) data in the section; this is the lesser of the
1348 // size of the raw data in the section and the virtual size of the section
1349 // because sections can have the tails implicitly initialized to zero (i.e,
1350 // their virtual size is larger than the raw size) and the raw data is
1351 // padded to the PE page size if the entire section is initialized (i.e.,
1352 // raw data size will be larger than the virtual size).
1353 const IMAGE_SECTION_HEADER* section = pe_image.GetSectionHeader(i);
1354 DCHECK(section != NULL);
1355 volatile uint8* touch =
1356 reinterpret_cast<uint8*>(pe_image.RVAToAddr(section->VirtualAddress));
1357 size_t section_length =
1358 std::min(section->SizeOfRawData, section->Misc.VirtualSize);
1359 volatile uint8* max_touch =
1360 touch + ((section_length * percentage) / kOneHundredPercent) - 1;
1361
1362 // Verify that the extent we're going to touch falls inside the section
1363 // we expect it to (and by implication, inside the pe_image).
1364 DCHECK_EQ(section,
1365 pe_image.GetImageSectionFromAddr(const_cast<uint8*>(touch)));
1366 DCHECK_EQ(section,
1367 pe_image.GetImageSectionFromAddr(const_cast<uint8*>(max_touch)));
1368
1369 // Get the system info so we know the page size.
1370 SYSTEM_INFO system_info;
1371 GetSystemInfo(&system_info);
1372
1373 // Read the memory in the range [touch, max_touch] with a stride of
1374 // the system page size. This ensures that it's been paged in.
1375 while (touch < max_touch) {
1376 uint8 unused1 = *touch;
1377 touch += system_info.dwPageSize;
1378 }
1379 uint8 unused2 = *max_touch;
1380 }
1381
1382 FreeLibrary(dll_module);
1383
1384 return true;
1385 }
1386
1387 } // namespace file_util::internal
1388
1389 bool PartialPreReadImage(const wchar_t* file_path,
1390 size_t percentage,
1391 size_t max_chunk_size) {
1392 base::ThreadRestrictions::AssertIOAllowed();
1393
1394 if (percentage >= 100) {
1395 // If we're reading the whole image, we don't need to parse headers and
1396 // navigate sections, the basic PreReadImage() can be used to just step
1397 // blindly through the entire file / address-space.
1398 return PreReadImage(file_path, 0, max_chunk_size);
1399 }
1400
1401 using internal::PartialPreReadImageInMemory;
1402 using internal::PartialPreReadImageOnDisk;
1403
1404 if (base::win::GetVersion() > base::win::VERSION_XP) {
1405 // Vista+ branch. On these OSes, we warm up the Image by reading its
1406 // file off the disk.
1407 return PartialPreReadImageOnDisk(file_path, percentage, max_chunk_size);
1408 }
1409
1410 // WinXP branch. For XP, reading the image from disk doesn't do what we want
1411 // so instead we pull the pages into memory by loading the DLL and touching
1412 // initialized pages at a stride.
1413 return PartialPreReadImageInMemory(file_path, percentage);
1414 }
1415
1174 } // namespace file_util 1416 } // namespace file_util
OLDNEW
« base/file_util.h ('K') | « base/file_util_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698