File: startup_metric_utils.cc

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (173 lines) | stat: -rw-r--r-- 6,434 bytes parent folder | download | duplicates (6)
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
// Copyright 2023 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/startup_metric_utils/common/startup_metric_utils.h"

#include <stddef.h>
#include <stdint.h>

#include <string>
#include <vector>

#include "base/dcheck_is_on.h"
#include "base/location.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/threading/scoped_thread_priority.h"
#include "base/trace_event/trace_event.h"

namespace startup_metric_utils {

CommonStartupMetricRecorder& GetCommon() {
  // If this ceases to be true, Get{Common,Browser} need to be changed to use
  // base::NoDestructor.
  static_assert(
      std::is_trivially_destructible<CommonStartupMetricRecorder>::value,
      "Startup metric recorder classes must be trivially destructible.");

  static CommonStartupMetricRecorder instance;
  return instance;
}

void CommonStartupMetricRecorder::RecordStartupProcessCreationTime(
    base::Time time) {
  // In certain cases, getting the process creation can fail, and no metrics
  // should be emitted in that case.
  bool time_is_valid = time != base::Time();
  if (time_is_valid) {
    RecordStartupProcessCreationTime(StartupTimeToTimeTicks(time));
  }
  base::UmaHistogramBoolean("Startup.LoadTime.RecordedProcessCreation",
                            time_is_valid);
}

void CommonStartupMetricRecorder::RecordStartupProcessCreationTime(
    base::TimeTicks ticks) {
  DCHECK(process_creation_ticks_.is_null());
  process_creation_ticks_ = ticks;
  DCHECK(!process_creation_ticks_.is_null());
}

void CommonStartupMetricRecorder::RecordApplicationStartTime(
    base::TimeTicks ticks) {
  DCHECK(application_start_ticks_.is_null());
  application_start_ticks_ = ticks;
  DCHECK(!application_start_ticks_.is_null());
}

void CommonStartupMetricRecorder::RecordChromeMainEntryTime(
    base::TimeTicks ticks) {
  DCHECK(chrome_main_entry_ticks_.is_null());
  chrome_main_entry_ticks_ = ticks;
  DCHECK(!chrome_main_entry_ticks_.is_null());
}

void CommonStartupMetricRecorder::RecordPreReadTime(base::TimeTicks start_ticks,
                                                    base::TimeTicks end_ticks) {
  preread_begin_ticks_ = start_ticks;
  preread_end_ticks_ = end_ticks;
  // These aren't necessarily non-null after setting, because if the process in
  // question did not PreRead (if `--no-pre-read-main-dll` was passed, for
  // example), they will be null.
}

void CommonStartupMetricRecorder::ResetSessionForTesting() {
#if DCHECK_IS_ON()
  GetSessionLog().clear();
#endif  // DCHECK_IS_ON()
}

base::TimeTicks CommonStartupMetricRecorder::MainEntryPointTicks() const {
  return chrome_main_entry_ticks_;
}

base::TimeTicks CommonStartupMetricRecorder::StartupTimeToTimeTicks(
    base::Time time) {
  // First get a base which represents the same point in time in both units.
  // Bump the priority of this thread while doing this as the wall clock time it
  // takes to resolve these two calls affects the precision of this method and
  // bumping the priority reduces the likelihood of a context switch interfering
  // with this computation.
  std::optional<base::ScopedBoostPriority> scoped_boost_priority;

  // Enabling this logic on OS X causes a significant performance regression.
  // TODO(crbug.com/40464036): Remove IS_APPLE ifdef once utility processes
  // set their desired main thread priority.
#if !BUILDFLAG(IS_APPLE)
  static bool statics_initialized = false;
  if (!statics_initialized) {
    statics_initialized = true;
    scoped_boost_priority.emplace(base::ThreadType::kDisplayCritical);
  }
#endif  // !BUILDFLAG(IS_APPLE)

  static const base::Time time_base = base::Time::Now();
  static const base::TimeTicks trace_ticks_base = base::TimeTicks::Now();

  // Then use the TimeDelta common ground between the two units to make the
  // conversion.
  const base::TimeDelta delta_since_base = time_base - time;
  return trace_ticks_base - delta_since_base;
}

void CommonStartupMetricRecorder::AddStartupEventsForTelemetry() {
  // Record the event only if RecordChromeMainEntryTime() was called, which is
  // not the case for some tests.
  if (chrome_main_entry_ticks_.is_null()) {
    return;
  }

  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
      "startup", "Startup.BrowserMainEntryPoint", 0, chrome_main_entry_ticks_);
}

#if DCHECK_IS_ON()
base::flat_set<const void*>& CommonStartupMetricRecorder::GetSessionLog() {
  static base::NoDestructor<base::flat_set<const void*>> session_log;
  return *session_log;
}
#endif  // DCHECK_IS_ON()

// DCHECKs that this is the first time |method_id| is passed to this assertion
// in this session (a session is typically process-lifetime but this can be
// reset in tests via ResetSessionForTesting()). Callers should use FROM_HERE as
// a unique id.

void CommonStartupMetricRecorder::AssertFirstCallInSession(
    base::Location from_here) {
#if DCHECK_IS_ON()
  DCHECK(GetSessionLog().insert(from_here.program_counter()).second);
#endif  // DCHECK_IS_ON()
}

void CommonStartupMetricRecorder::EmitHistogramWithTraceEvent(
    HistogramTimeFunction* histogram_function,
    const char* name,
    base::TimeTicks begin_ticks,
    base::TimeTicks end_ticks) {
  (*histogram_function)(name, end_ticks - begin_ticks);
  EmitTraceEvent(name, begin_ticks, end_ticks);
}

void CommonStartupMetricRecorder::EmitTraceEvent(const char* name,
                                                 base::TimeTicks begin_ticks,
                                                 base::TimeTicks end_ticks) {
  // TODO(325307453): Emit the trace events asynchronously onto a
  // perfetto::Track. Ordering is a problem if doing it simplistically as
  // the async events are emitted out-of-order with identical start times
  // and thus Perfetto is unable to sort them in a reasonable parent-child
  // relationship.
  TRACE_EVENT_BEGIN("startup", perfetto::StaticString(name),
                    perfetto::Track(reinterpret_cast<uintptr_t>(name)),
                    begin_ticks);
  TRACE_EVENT_END("startup", perfetto::Track(reinterpret_cast<uintptr_t>(name)),
                  end_ticks);
}

void CommonStartupMetricRecorder::EmitInstantEvent(const char* name) {
  TRACE_EVENT_INSTANT("startup", perfetto::StaticString(name),
                      perfetto::Track(reinterpret_cast<uintptr_t>(name)));
}

}  // namespace startup_metric_utils