File: persistent_histograms.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (318 lines) | stat: -rw-r--r-- 12,739 bytes parent folder | download
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// Copyright 2018 The Chromium Authors
// 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/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/metrics/persistent_system_profile.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 BUILDFLAG(IS_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 BUILDFLAG(IS_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::Days(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);
  }
}

// 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::Minutes(2);

#endif  // BUILDFLAG(IS_WIN)

// Create persistent/shared memory and allow histograms to be stored in
// it. Memory that is not actually 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)

// Logged to UMA - keep in sync with enums.xml.
enum InitResult {
  kLocalMemorySuccess,
  kLocalMemoryFailed,
  kMappedFileSuccess,
  kMappedFileFailed,
  kMappedFileExists,
  kNoSpareFile,
  kNoUploadDir,
  kMaxValue = kNoUploadDir
};

base::FilePath GetSpareFilePath(const base::FilePath& metrics_dir) {
  return base::GlobalHistogramAllocator::ConstructFilePath(
      metrics_dir, kBrowserMetricsName + std::string("-spare"));
}

// Initializes persistent histograms with a memory-mapped file.
InitResult InitWithMappedFile(const base::FilePath& metrics_dir,
                              const base::FilePath& upload_dir) {
  // The spare file in the user data dir ("BrowserMetrics-spare.pma") would
  // have been created in the previous session. We will move it to |upload_dir|
  // and rename it with the current time and process id for use as |active_file|
  // (e.g. "BrowserMetrics/BrowserMetrics-1234ABCD-12345.pma").
  // Any unreported metrics in this file will be uploaded next session.
  base::FilePath spare_file = GetSpareFilePath(metrics_dir);
  base::FilePath active_file =
      base::GlobalHistogramAllocator::ConstructFilePathForUploadDir(
          upload_dir, kBrowserMetricsName, base::Time::Now(),
          base::GetCurrentProcId());

  InitResult result;
  if (!base::PathExists(upload_dir)) {
    // Handle failure to create the directory.
    result = kNoUploadDir;
  } else if (base::PathExists(active_file)) {
    // "active" filename is supposed to be unique so this shouldn't happen.
    result = kMappedFileExists;
  } else {
    // Disallow multiple writers (Windows only). Needed to ensure multiple
    // instances of Chrome aren't writing to the same file, which could happen
    // in some rare circumstances observed in the wild (e.g. on FAT FS where the
    // file name ends up not being unique due to truncation and two processes
    // racing on base::PathExists(active_file) above).
    const bool exclusive_write = true;
    // Move any spare file into the active position.
    base::ReplaceFile(spare_file, active_file, nullptr);
    // Create global allocator using the |active_file|.
    if (kSpareFileRequired && !base::PathExists(active_file)) {
      result = kNoSpareFile;
    } else if (base::GlobalHistogramAllocator::CreateWithFile(
                   active_file, kAllocSize, kAllocId, kBrowserMetricsName,
                   exclusive_write)) {
      result = kMappedFileSuccess;
    } else {
      result = kMappedFileFailed;
    }
  }

  return result;
}

enum PersistentHistogramsMode {
  kNotEnabled,
  kMappedFile,
  kLocalMemory,
};

// Implementation of InstantiatePersistentHistograms() that does the work after
// the desired |mode| has been determined.
void InstantiatePersistentHistogramsImpl(const base::FilePath& metrics_dir,
                                         PersistentHistogramsMode mode) {
  // 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);
  // TODO(crbug.com/1183166): Only create the dir in kMappedFile mode.
  base::CreateDirectory(upload_dir);

  InitResult result;

  // Create a global histogram allocator using the desired storage type.
  switch (mode) {
    case kMappedFile:
      result = InitWithMappedFile(metrics_dir, upload_dir);
      break;
    case 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;
      break;
    case kNotEnabled:
      // Persistent metric storage is disabled. Must return here.
      // TODO(crbug.com/1183166): Log the histogram below in this case too.
      return;
  }

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

  base::GlobalHistogramAllocator* allocator =
      base::GlobalHistogramAllocator::Get();
  if (!allocator) {
    // If no allocator was created above, try to create a LocalMemory 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);
}

}  // namespace

BASE_FEATURE(
    kPersistentHistogramsFeature,
    "PersistentHistograms",
#if BUILDFLAG(IS_FUCHSIA)
    // TODO(crbug.com/1295119): Enable once writable mmap() is supported. Also
    // move the initialization earlier to chrome/app/chrome_main_delegate.cc.
    base::FEATURE_DISABLED_BY_DEFAULT
#else
    base::FEATURE_ENABLED_BY_DEFAULT
#endif  // BUILDFLAG(IS_FUCHSIA)
);

const char kPersistentHistogramStorageMappedFile[] = "MappedFile";
const char kPersistentHistogramStorageLocalMemory[] = "LocalMemory";

const base::FeatureParam<std::string> kPersistentHistogramsStorage{
    &kPersistentHistogramsFeature, "storage",
    kPersistentHistogramStorageMappedFile};

const char kBrowserMetricsName[] = "BrowserMetrics";
const char kDeferredBrowserMetricsName[] = "DeferredBrowserMetrics";

void InstantiatePersistentHistograms(const base::FilePath& metrics_dir,
                                     bool persistent_histograms_enabled,
                                     base::StringPiece storage) {
  PersistentHistogramsMode mode = kNotEnabled;
  // Note: The extra feature check is needed so that we don't use the default
  // value of the storage param if the feature is disabled.
  if (persistent_histograms_enabled) {
    if (storage == kPersistentHistogramStorageMappedFile) {
      mode = kMappedFile;
    } else if (storage == kPersistentHistogramStorageLocalMemory) {
      mode = kLocalMemory;
    }
  }

// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
  // 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 (mode == kMappedFile) {
    int major, minor, bugfix;
    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
    if (major == 4 && minor == 4 && bugfix == 0)
      mode = kLocalMemory;
  }
#endif

  InstantiatePersistentHistogramsImpl(metrics_dir, mode);
}

void PersistentHistogramsCleanup(const base::FilePath& metrics_dir) {
  base::FilePath spare_file = GetSpareFilePath(metrics_dir);

  // 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::Seconds(kSpareFileCreateDelaySeconds));

#if BUILDFLAG(IS_WIN)
  // Post a best effort task that will delete files. Unlike SKIP_ON_SHUTDOWN,
  // which will block on the deletion if the task already started,
  // CONTINUE_ON_SHUTDOWN will not block shutdown on the task completing. It's
  // not a *necessity* to delete the files the same session they are "detected".
  // On shutdown, the deletion will be interrupted.
  base::ThreadPool::PostDelayedTask(
      FROM_HERE,
      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
      base::BindOnce(&DeleteOldWindowsTempFiles, metrics_dir),
      kDeleteOldWindowsTempFilesDelay);
#endif  // BUILDFLAG(IS_WIN)
}

void InstantiatePersistentHistogramsWithFeaturesAndCleanup(
    const base::FilePath& metrics_dir) {
  InstantiatePersistentHistograms(
      metrics_dir, base::FeatureList::IsEnabled(kPersistentHistogramsFeature),
      kPersistentHistogramsStorage.Get());
  PersistentHistogramsCleanup(metrics_dir);
}

bool DeferBrowserMetrics(const base::FilePath& metrics_dir) {
  base::GlobalHistogramAllocator* allocator =
      base::GlobalHistogramAllocator::Get();

  if (!allocator || !allocator->HasPersistentLocation()) {
    return false;
  }

  base::FilePath deferred_metrics_dir =
      metrics_dir.AppendASCII(kDeferredBrowserMetricsName);

  if (!base::CreateDirectory(deferred_metrics_dir)) {
    return false;
  }

  return allocator->MovePersistentFile(deferred_metrics_dir);
}