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 1135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 |
OLD | NEW |