summaryrefslogtreecommitdiff
path: root/chromium/components/metrics/persistent_histograms.cc
blob: 4b5e777269f952e65687fa2dd2d849c20925e020 (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2018 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 "components/metrics/persistent_histograms.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/metrics/persistent_system_profile.h"
#include "components/variations/variations_associated_data.h"

namespace {

// Creating a "spare" file for persistent metrics involves a lot of I/O and
// isn't important so delay the operation for a while after startup.
#if defined(OS_ANDROID)
// Android needs the spare file and also launches faster.
constexpr bool kSpareFileRequired = true;
constexpr int kSpareFileCreateDelaySeconds = 10;
#else
// Desktop may have to restore a lot of tabs so give it more time before doing
// non-essential work. The spare file is still a performance boost but not as
// significant of one so it's not required.
constexpr bool kSpareFileRequired = false;
constexpr int kSpareFileCreateDelaySeconds = 90;
#endif

#if defined(OS_WIN)

// Windows sometimes creates files of the form MyFile.pma~RF71cb1793.TMP
// when trying to rename a file to something that exists but is in-use, and
// then fails to remove them. See https://crbug.com/934164
void DeleteOldWindowsTempFiles(const base::FilePath& dir) {
  // Look for any temp files older than one day and remove them. The time check
  // ensures that nothing in active transition gets deleted; these names only
  // exists on the order of milliseconds when working properly so "one day" is
  // generous but still ensures no big build up of these files. This is an
  // I/O intensive task so do it in the background (enforced by "file" calls).
  base::Time one_day_ago = base::Time::Now() - base::TimeDelta::FromDays(1);
  base::FileEnumerator file_iter(dir, /*recursive=*/false,
                                 base::FileEnumerator::FILES);
  for (base::FilePath path = file_iter.Next(); !path.empty();
       path = file_iter.Next()) {
    if (base::ToUpperASCII(path.FinalExtension()) !=
            FILE_PATH_LITERAL(".TMP") ||
        base::ToUpperASCII(path.BaseName().value())
                .find(FILE_PATH_LITERAL(".PMA~RF")) < 0) {
      continue;
    }

    const auto& info = file_iter.GetInfo();
    if (info.IsDirectory())
      continue;
    if (info.GetLastModifiedTime() > one_day_ago)
      continue;

    base::DeleteFile(path, /*recursive=*/false);
  }
}

// How much time after startup to run the above function. Two minutes is
// enough for the system to stabilize and get the user what they want before
// spending time on clean-up efforts.
constexpr base::TimeDelta kDeleteOldWindowsTempFilesDelay =
    base::TimeDelta::FromMinutes(2);

#endif  // defined(OS_WIN)

}  // namespace

const char kBrowserMetricsName[] = "BrowserMetrics";

// Check for feature enabling the use of persistent histogram storage and
// enable the global allocator if so.
void InstantiatePersistentHistograms(const base::FilePath& metrics_dir) {
  // Create a directory for storing completed metrics files. Files in this
  // directory must have embedded system profiles. If the directory can't be
  // created, the file will just be deleted below.
  base::FilePath upload_dir = metrics_dir.AppendASCII(kBrowserMetricsName);
  base::CreateDirectory(upload_dir);

  // Metrics files are typically created as a |spare_file| in the profile
  // directory (e.g. "BrowserMetrics-spare.pma") and are then rotated into
  // a subdirectory as a stamped file for upload when no longer in use.
  // (e.g. "BrowserMetrics/BrowserMetrics-1234ABCD-12345.pma")
  base::FilePath upload_file;
  base::FilePath active_file;
  base::FilePath spare_file;
  base::GlobalHistogramAllocator::ConstructFilePathsForUploadDir(
      metrics_dir, upload_dir, kBrowserMetricsName, &upload_file, &active_file,
      &spare_file);

  // This is used to report results to an UMA histogram.
  enum InitResult {
    kLocalMemorySuccess,
    kLocalMemoryFailed,
    kMappedFileSuccess,
    kMappedFileFailed,
    kMappedFileExists,
    kNoSpareFile,
    kNoUploadDir,
    kMaxValue = kNoUploadDir
  };
  InitResult result;

  // Create persistent/shared memory and allow histograms to be stored in
  // it. Memory that is not actualy used won't be physically mapped by the
  // system. BrowserMetrics usage, as reported in UMA, has the 99.99
  // percentile around 3MiB as of 2018-10-22.
  // Please update ServicificationBackgroundServiceTest.java if the |kAllocSize|
  // is changed.
  const size_t kAllocSize = 4 << 20;     // 4 MiB
  const uint32_t kAllocId = 0x935DDD43;  // SHA1(BrowserMetrics)
  std::string storage = variations::GetVariationParamValueByFeature(
      base::kPersistentHistogramsFeature, "storage");

  static const char kMappedFile[] = "MappedFile";
  static const char kLocalMemory[] = "LocalMemory";

#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
  // Linux kernel 4.4.0.* shows a huge number of SIGBUS crashes with persistent
  // histograms enabled using a mapped file.  Change this to use local memory.
  // https://bugs.chromium.org/p/chromium/issues/detail?id=753741
  if (storage.empty() || storage == kMappedFile) {
    int major, minor, bugfix;
    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
    if (major == 4 && minor == 4 && bugfix == 0)
      storage = kLocalMemory;
  }
#endif

  // Don't use mapped-file memory by default on low-end devices, especially
  // Android. The extra disk consumption and/or extra disk access could have
  // a significant performance impact. https://crbug.com/896394
  if (storage.empty() && base::SysInfo::IsLowEndDevice())
    storage = kLocalMemory;

  // Create a global histogram allocator using the desired storage type.
  if (storage.empty() || storage == kMappedFile) {
    if (!base::PathExists(upload_dir)) {
      // Handle failure to create the directory.
      result = kNoUploadDir;
    } else if (base::PathExists(upload_file)) {
      // "upload" filename is supposed to be unique so this shouldn't happen.
      result = kMappedFileExists;
    } else {
      // Move any sparse file into the upload position.
      base::ReplaceFile(spare_file, upload_file, nullptr);
      // Create global allocator using the "upload" file.
      if (kSpareFileRequired && !base::PathExists(upload_file)) {
        result = kNoSpareFile;
      } else if (base::GlobalHistogramAllocator::CreateWithFile(
                     upload_file, kAllocSize, kAllocId, kBrowserMetricsName)) {
        result = kMappedFileSuccess;
      } else {
        result = kMappedFileFailed;
      }
    }
    // Schedule the creation of a "spare" file for use on the next run.
    base::ThreadPool::PostDelayedTask(
        FROM_HERE,
        {base::MayBlock(), base::TaskPriority::LOWEST,
         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
        base::BindOnce(base::IgnoreResult(
                           &base::GlobalHistogramAllocator::CreateSpareFile),
                       std::move(spare_file), kAllocSize),
        base::TimeDelta::FromSeconds(kSpareFileCreateDelaySeconds));
  } else if (storage == kLocalMemory) {
    // Use local memory for storage even though it will not persist across
    // an unclean shutdown. This sets the result but the actual creation is
    // done below.
    result = kLocalMemorySuccess;
  } else {
    // Persistent metric storage is disabled. Must return here.
    return;
  }

  // Get the allocator that was just created and report result. Exit if the
  // allocator could not be created.
  UMA_HISTOGRAM_ENUMERATION("UMA.PersistentHistograms.InitResult", result);

  base::GlobalHistogramAllocator* allocator =
      base::GlobalHistogramAllocator::Get();
  if (!allocator) {
    // If no allocator was created above, try to create a LocalMemomory one
    // here. This avoids repeating the call many times above. In the case where
    // persistence is disabled, an early return is done above.
    base::GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize, kAllocId,
                                                          kBrowserMetricsName);
    allocator = base::GlobalHistogramAllocator::Get();
    if (!allocator)
      return;
  }

  // Store a copy of the system profile in this allocator.
  metrics::GlobalPersistentSystemProfile::GetInstance()
      ->RegisterPersistentAllocator(allocator->memory_allocator());

  // Create tracking histograms for the allocator and record storage file.
  allocator->CreateTrackingHistograms(kBrowserMetricsName);

#if defined(OS_WIN)
  base::ThreadPool::PostDelayedTask(
      FROM_HERE,
      {base::MayBlock(), base::TaskPriority::LOWEST,
       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
      base::BindOnce(&DeleteOldWindowsTempFiles, std::move(metrics_dir)),
      kDeleteOldWindowsTempFilesDelay);
#endif  // defined(OS_WIN)
}