// Copyright (c) 2011 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/browser/fragmentation_checker_win.h" #include #include #include #include "base/file_path.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/platform_file.h" #include "base/path_service.h" namespace { size_t ComputeRetrievalPointersBufferSize(int number_of_extents) { // We make a redundant access to buffer.ExtentCount (which is always 0) below // to avoid C4101 on MSVC2010. RETRIEVAL_POINTERS_BUFFER buffer = {0}; return sizeof(buffer) + (number_of_extents - 1) * sizeof(buffer.Extents) + buffer.ExtentCount; } } // namespace namespace fragmentation_checker { int CountFileExtents(const FilePath& file_path) { int file_extents_count = 0; base::PlatformFileError error_code = base::PLATFORM_FILE_ERROR_FAILED; base::PlatformFile file_handle = CreatePlatformFile( file_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, &error_code); if (error_code == base::PLATFORM_FILE_OK) { STARTING_VCN_INPUT_BUFFER starting_vcn_input_buffer = {0}; // Compute an output size capable of holding 16 extents at first. This will // fail when the number of extents exceeds 16, in which case we make // a bigger buffer capable of holding up to kMaxExtentCounts. int extents_guess = 16; size_t output_size = ComputeRetrievalPointersBufferSize(extents_guess); std::vector retrieval_pointers_buffer(output_size); DWORD bytes_returned = 0; bool result = false; do { result = DeviceIoControl( file_handle, FSCTL_GET_RETRIEVAL_POINTERS, reinterpret_cast(&starting_vcn_input_buffer), sizeof(starting_vcn_input_buffer), reinterpret_cast(&retrieval_pointers_buffer[0]), retrieval_pointers_buffer.size(), &bytes_returned, NULL) != FALSE; if (!result) { if (GetLastError() == ERROR_MORE_DATA) { // Grow the extents we can handle extents_guess *= 2; if (extents_guess > kMaxExtentCount) { LOG(ERROR) << "FSCTL_GET_RETRIEVAL_POINTERS output buffer exceeded " "maximum size."; file_extents_count = kMaxExtentCount; break; } output_size = ComputeRetrievalPointersBufferSize(extents_guess); retrieval_pointers_buffer.assign(output_size, 0); } else { PLOG(ERROR) << "FSCTL_GET_RETRIEVAL_POINTERS failed."; break; } } } while (!result); if (result) { RETRIEVAL_POINTERS_BUFFER* retrieval_pointers = reinterpret_cast( &retrieval_pointers_buffer[0]); file_extents_count = static_cast(retrieval_pointers->ExtentCount); } else { LOG(ERROR) << "Failed to retrieve extents."; } } else { LOG(ERROR) << "Failed to open module file to check extents. Error code = " << error_code; } return file_extents_count; } void RecordFragmentationMetricForCurrentModule() { FilePath module_path; if (PathService::Get(base::FILE_MODULE, &module_path)) { int file_extent_count = CountFileExtents(module_path); UMA_HISTOGRAM_CUSTOM_COUNTS("Fragmentation.ModuleExtents", file_extent_count, 0, kMaxExtentCount, 50); } else { NOTREACHED() << "Could not get path to current module."; } } } // namespace fragmentation_checker