summaryrefslogtreecommitdiffstats
path: root/chrome/browser/fragmentation_checker_win.cc
blob: 04d4ac63e59789a674c80ccd5771fb9cd55c30a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// 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 <windows.h>
#include <winioctl.h>

#include <vector>

#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<uint8> retrieval_pointers_buffer(output_size);

    DWORD bytes_returned = 0;

    bool result = false;
    do {
      result = DeviceIoControl(
          file_handle,
          FSCTL_GET_RETRIEVAL_POINTERS,
          reinterpret_cast<void*>(&starting_vcn_input_buffer),
          sizeof(starting_vcn_input_buffer),
          reinterpret_cast<void*>(&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*>(
              &retrieval_pointers_buffer[0]);
      file_extents_count = static_cast<int>(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