OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |