1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/metrics/perf/perf_events_collector.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/files/file_util.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/rand_util.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/system/sys_info.h"
19 #include "base/task/post_task.h"
20 #include "base/task/thread_pool.h"
21 #include "chrome/browser/metrics/perf/cpu_identity.h"
22 #include "chrome/browser/metrics/perf/process_type_collector.h"
23 #include "chrome/browser/metrics/perf/windowed_incognito_observer.h"
24 #include "chrome/browser/ui/browser_list.h"
25 #include "chromeos/dbus/debug_daemon/debug_daemon_client_provider.h"
26 #include "components/variations/variations_associated_data.h"
27 #include "third_party/metrics_proto/sampled_profile.pb.h"
28
29 namespace metrics {
30
31 namespace {
32
33 const char kCWPFieldTrialName[] = "ChromeOSWideProfilingCollection";
34
35 // Name the histogram that represents the success and various failure modes for
36 // parsing CPU frequencies.
37 const char kParseFrequenciesHistogramName[] =
38 "ChromeOS.CWP.ParseCPUFrequencies";
39
40 // Limit the total size of protobufs that can be cached, so they don't take up
41 // too much memory. If the size of cached protobufs exceeds this value, stop
42 // collecting further perf data. The current value is 4 MB.
43 const size_t kCachedPerfDataProtobufSizeThreshold = 4 * 1024 * 1024;
44
45 // Name of the perf events collector. It is appended to the UMA metric names
46 // for reporting collection and upload status.
47 const char kPerfCollectorName[] = "Perf";
48
49 // Gets parameter named by |key| from the map. If it is present and is an
50 // integer, stores the result in |out| and return true. Otherwise return false.
GetInt64Param(const std::map<std::string,std::string> & params,const std::string & key,int64_t * out)51 bool GetInt64Param(const std::map<std::string, std::string>& params,
52 const std::string& key,
53 int64_t* out) {
54 auto it = params.find(key);
55 if (it == params.end())
56 return false;
57 int64_t value;
58 // NB: StringToInt64 will set value even if the conversion fails.
59 if (!base::StringToInt64(it->second, &value))
60 return false;
61 *out = value;
62 return true;
63 }
64
65 // Parses the key. e.g.: "PerfCommand::arm::0" returns "arm"
ExtractPerfCommandCpuSpecifier(const std::string & key,std::string * cpu_specifier)66 bool ExtractPerfCommandCpuSpecifier(const std::string& key,
67 std::string* cpu_specifier) {
68 std::vector<std::string> tokens = base::SplitStringUsingSubstr(
69 key, "::", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
70 if (tokens.size() != 3)
71 return false;
72 if (tokens[0] != "PerfCommand")
73 return false;
74 *cpu_specifier = tokens[1];
75 // tokens[2] is just a unique string (usually an index).
76 return true;
77 }
78
79 // Parses the components of a version string, e.g. major.minor.bugfix
ExtractVersionNumbers(const std::string & version,int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)80 void ExtractVersionNumbers(const std::string& version,
81 int32_t* major_version,
82 int32_t* minor_version,
83 int32_t* bugfix_version) {
84 *major_version = *minor_version = *bugfix_version = 0;
85 // Parse out the version numbers from the string.
86 sscanf(version.c_str(), "%d.%d.%d", major_version, minor_version,
87 bugfix_version);
88 }
89
90 // Returns if a micro-architecture supports the cycles:ppp event.
MicroarchitectureHasCyclesPPPEvent(const std::string & uarch)91 bool MicroarchitectureHasCyclesPPPEvent(const std::string& uarch) {
92 return uarch == "Goldmont" || uarch == "GoldmontPlus" ||
93 uarch == "Broadwell" || uarch == "Kabylake" || uarch == "Tigerlake";
94 }
95
96 // Returns if a micro-architecture supports LBR callgraph profiling.
MicroarchitectureHasLBRCallgraph(const std::string & uarch)97 bool MicroarchitectureHasLBRCallgraph(const std::string& uarch) {
98 return uarch == "Haswell" || uarch == "Broadwell" || uarch == "Skylake" ||
99 uarch == "Kabylake" || uarch == "Tigerlake";
100 }
101
102 // Returns if a kernel release supports LBR callgraph profiling.
KernelReleaseHasLBRCallgraph(const std::string & release)103 bool KernelReleaseHasLBRCallgraph(const std::string& release) {
104 int32_t major, minor, bugfix;
105 ExtractVersionNumbers(release, &major, &minor, &bugfix);
106 return major > 4 || (major == 4 && minor >= 4) || (major == 3 && minor == 18);
107 }
108
109 // Hopefully we never need a space in a command argument.
110 const char kPerfCommandDelimiter[] = " ";
111
112 // Collect precise=3 (:ppp) cycle events on microarchitectures that support it.
113 const char kPerfFPCallgraphPPPCmd[] =
114 "perf record -a -e cycles:ppp -g -c 4000037";
115
116 // Collect default (imprecise) cycle events everywhere else.
117 const char kPerfCyclesCmd[] = "perf record -a -e cycles -c 1000003";
118
119 const char kPerfFPCallgraphCmd[] = "perf record -a -e cycles -g -c 4000037";
120
121 const char kPerfLBRCallgraphCmd[] =
122 "perf record -a -e cycles -c 4000037 --call-graph lbr";
123
124 const char kPerfLBRCmd[] = "perf record -a -e r20c4 -b -c 200011";
125
126 // Silvermont, Airmont, Goldmont don't have a branches taken event. Therefore,
127 // we sample on the branches retired event.
128 const char kPerfLBRCmdAtom[] = "perf record -a -e rc4 -b -c 300001";
129
130 // The following events count misses in the last level caches and level 2 TLBs.
131
132 // TLB miss cycles for IvyBridge, Haswell, Broadwell and SandyBridge.
133 const char kPerfITLBMissCyclesCmdIvyBridge[] =
134 "perf record -a -e itlb_misses.walk_duration -c 30001";
135
136 const char kPerfDTLBMissCyclesCmdIvyBridge[] =
137 "perf record -a -e dtlb_load_misses.walk_duration -g -c 160001";
138
139 // TLB miss cycles for Skylake, Kabylake, Tigerlake.
140 const char kPerfITLBMissCyclesCmdSkylake[] =
141 "perf record -a -e itlb_misses.walk_pending -c 30001";
142
143 const char kPerfDTLBMissCyclesCmdSkylake[] =
144 "perf record -a -e dtlb_load_misses.walk_pending -g -c 160001";
145
146 // TLB miss cycles for Atom, including Silvermont, Airmont and Goldmont.
147 const char kPerfITLBMissCyclesCmdAtom[] =
148 "perf record -a -e page_walks.i_side_cycles -c 30001";
149
150 const char kPerfDTLBMissCyclesCmdAtom[] =
151 "perf record -a -e page_walks.d_side_cycles -g -c 160001";
152
153 const char kPerfLLCMissesCmd[] = "perf record -a -e r412e -g -c 30007";
154 // Precise events (request zero skid) for last level cache misses.
155 const char kPerfLLCMissesPreciseCmd[] =
156 "perf record -a -e r412e:pp -g -c 30007";
157
GetDefaultCommands_x86_64(const CPUIdentity & cpuid)158 const std::vector<RandomSelector::WeightAndValue> GetDefaultCommands_x86_64(
159 const CPUIdentity& cpuid) {
160 using WeightAndValue = RandomSelector::WeightAndValue;
161 std::vector<WeightAndValue> cmds;
162 DCHECK_EQ(cpuid.arch, "x86_64");
163 const std::string cpu_uarch = GetCpuUarch(cpuid);
164
165 // We use different perf events for iTLB, dTLB and LBR profiling on different
166 // microarchitectures. Customize each command based on the microarchitecture.
167 const char* itlb_miss_cycles_cmd = kPerfITLBMissCyclesCmdIvyBridge;
168 const char* dtlb_miss_cycles_cmd = kPerfDTLBMissCyclesCmdIvyBridge;
169 const char* lbr_cmd = kPerfLBRCmd;
170 const char* cycles_cmd = kPerfCyclesCmd;
171 const char* fp_callgraph_cmd = kPerfFPCallgraphCmd;
172 const char* lbr_callgraph_cmd = kPerfLBRCallgraphCmd;
173
174 if (cpu_uarch == "Skylake" || cpu_uarch == "Kabylake" ||
175 cpu_uarch == "Tigerlake" || cpu_uarch == "GoldmontPlus") {
176 itlb_miss_cycles_cmd = kPerfITLBMissCyclesCmdSkylake;
177 dtlb_miss_cycles_cmd = kPerfDTLBMissCyclesCmdSkylake;
178 }
179 if (cpu_uarch == "Silvermont" || cpu_uarch == "Airmont" ||
180 cpu_uarch == "Goldmont") {
181 itlb_miss_cycles_cmd = kPerfITLBMissCyclesCmdAtom;
182 dtlb_miss_cycles_cmd = kPerfDTLBMissCyclesCmdAtom;
183 }
184 if (cpu_uarch == "Silvermont" || cpu_uarch == "Airmont" ||
185 cpu_uarch == "Goldmont" || cpu_uarch == "GoldmontPlus") {
186 lbr_cmd = kPerfLBRCmdAtom;
187 }
188 if (MicroarchitectureHasCyclesPPPEvent(cpu_uarch)) {
189 fp_callgraph_cmd = kPerfFPCallgraphPPPCmd;
190 }
191
192 cmds.emplace_back(WeightAndValue(50.0, cycles_cmd));
193
194 // Haswell and newer big Intel cores support LBR callstack profiling. This
195 // requires kernel support, which was added in kernel 4.4, and it was
196 // backported to kernel 3.18. Collect LBR callstack profiling where
197 // supported in addition to FP callchains. The former works with binaries
198 // compiled with frame pointers disabled, but it only captures callchains
199 // after profiling is enabled, so it's likely missing the lower frames of
200 // the callstack.
201 if (MicroarchitectureHasLBRCallgraph(cpu_uarch) &&
202 KernelReleaseHasLBRCallgraph(cpuid.release)) {
203 cmds.emplace_back(WeightAndValue(10.0, fp_callgraph_cmd));
204 cmds.emplace_back(WeightAndValue(10.0, lbr_callgraph_cmd));
205 } else {
206 cmds.emplace_back(WeightAndValue(20.0, fp_callgraph_cmd));
207 }
208
209 if (cpu_uarch == "IvyBridge" || cpu_uarch == "Haswell" ||
210 cpu_uarch == "Broadwell" || cpu_uarch == "SandyBridge" ||
211 cpu_uarch == "Skylake" || cpu_uarch == "Kabylake" ||
212 cpu_uarch == "Tigerlake" || cpu_uarch == "Silvermont" ||
213 cpu_uarch == "Airmont" || cpu_uarch == "Goldmont" ||
214 cpu_uarch == "GoldmontPlus") {
215 cmds.emplace_back(WeightAndValue(15.0, lbr_cmd));
216 cmds.emplace_back(WeightAndValue(5.0, itlb_miss_cycles_cmd));
217 cmds.emplace_back(WeightAndValue(5.0, dtlb_miss_cycles_cmd));
218 // Only Goldmont and GoldmontPlus support precise events on last level cache
219 // misses.
220 if (cpu_uarch == "Goldmont" || cpu_uarch == "GoldmontPlus") {
221 cmds.emplace_back(WeightAndValue(5.0, kPerfLLCMissesPreciseCmd));
222 } else {
223 cmds.emplace_back(WeightAndValue(5.0, kPerfLLCMissesCmd));
224 }
225 return cmds;
226 }
227 // Other 64-bit x86. We collect LLC misses for other Intel CPUs, but not for
228 // non-Intel CPUs such as AMD, since the event code provided for LLC is
229 // Intel specific.
230 if (cpuid.vendor=="GenuineIntel"){
231 cmds.emplace_back(WeightAndValue(25.0, cycles_cmd));
232 cmds.emplace_back(WeightAndValue(5.0, kPerfLLCMissesCmd));
233 } else {
234 cmds.emplace_back(WeightAndValue(30.0, cycles_cmd));
235 }
236 return cmds;
237 }
238
OnCollectProcessTypes(SampledProfile * sampled_profile)239 void OnCollectProcessTypes(SampledProfile* sampled_profile) {
240 std::map<uint32_t, Process> process_types =
241 ProcessTypeCollector::ChromeProcessTypes();
242 std::map<uint32_t, Thread> thread_types =
243 ProcessTypeCollector::ChromeThreadTypes();
244 if (!process_types.empty() && !thread_types.empty()) {
245 sampled_profile->mutable_process_types()->insert(process_types.begin(),
246 process_types.end());
247 sampled_profile->mutable_thread_types()->insert(thread_types.begin(),
248 thread_types.end());
249 }
250 }
251
252 } // namespace
253
254 namespace internal {
255
GetDefaultCommandsForCpu(const CPUIdentity & cpuid)256 std::vector<RandomSelector::WeightAndValue> GetDefaultCommandsForCpu(
257 const CPUIdentity& cpuid) {
258 using WeightAndValue = RandomSelector::WeightAndValue;
259
260 if (cpuid.arch == "x86_64") // 64-bit x86
261 return GetDefaultCommands_x86_64(cpuid);
262
263 std::vector<WeightAndValue> cmds;
264 if (cpuid.arch == "x86" || // 32-bit x86, or...
265 cpuid.arch == "armv7l" || // ARM32
266 cpuid.arch == "aarch64") { // ARM64
267 cmds.emplace_back(WeightAndValue(80.0, kPerfCyclesCmd));
268 cmds.emplace_back(WeightAndValue(20.0, kPerfFPCallgraphCmd));
269 return cmds;
270 }
271
272 // Unknown CPUs
273 cmds.emplace_back(WeightAndValue(1.0, kPerfCyclesCmd));
274 return cmds;
275 }
276
277 } // namespace internal
278
PerfCollector()279 PerfCollector::PerfCollector()
280 : internal::MetricCollector(kPerfCollectorName, CollectionParams()) {}
281
282 PerfCollector::~PerfCollector() = default;
283
SetUp()284 void PerfCollector::SetUp() {
285 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
286
287 // Create DebugdClientProvider to bind its private DBus connection to the
288 // current sequence.
289 debugd_client_provider_ =
290 std::make_unique<chromeos::DebugDaemonClientProvider>();
291
292 auto task_runner = base::SequencedTaskRunnerHandle::Get();
293 base::ThreadPool::PostTask(
294 FROM_HERE,
295 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
296 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
297 base::BindOnce(&PerfCollector::ParseCPUFrequencies, task_runner,
298 weak_factory_.GetWeakPtr()));
299
300 CHECK(command_selector_.SetOdds(
301 internal::GetDefaultCommandsForCpu(GetCPUIdentity())));
302 std::map<std::string, std::string> params;
303 if (variations::GetVariationParams(kCWPFieldTrialName, ¶ms)) {
304 SetCollectionParamsFromVariationParams(params);
305 }
306 }
307
ToolName() const308 const char* PerfCollector::ToolName() const {
309 return kPerfCollectorName;
310 }
311
312 namespace internal {
313
FindBestCpuSpecifierFromParams(const std::map<std::string,std::string> & params,const CPUIdentity & cpuid)314 std::string FindBestCpuSpecifierFromParams(
315 const std::map<std::string, std::string>& params,
316 const CPUIdentity& cpuid) {
317 std::string ret;
318 // The CPU specified in the variation params could be "default", a system
319 // architecture, a CPU microarchitecture, or a CPU model substring. We should
320 // prefer to match the most specific.
321 enum MatchSpecificity {
322 NO_MATCH,
323 DEFAULT,
324 SYSTEM_ARCH,
325 CPU_UARCH,
326 CPU_MODEL,
327 };
328 MatchSpecificity match_level = NO_MATCH;
329
330 const std::string cpu_uarch = GetCpuUarch(cpuid);
331 const std::string simplified_cpu_model =
332 SimplifyCPUModelName(cpuid.model_name);
333
334 for (const auto& key_val : params) {
335 const std::string& key = key_val.first;
336
337 std::string cpu_specifier;
338 if (!ExtractPerfCommandCpuSpecifier(key, &cpu_specifier))
339 continue;
340
341 if (match_level < DEFAULT && cpu_specifier == "default") {
342 match_level = DEFAULT;
343 ret = cpu_specifier;
344 }
345 if (match_level < SYSTEM_ARCH && cpu_specifier == cpuid.arch) {
346 match_level = SYSTEM_ARCH;
347 ret = cpu_specifier;
348 }
349 if (match_level < CPU_UARCH && !cpu_uarch.empty() &&
350 cpu_specifier == cpu_uarch) {
351 match_level = CPU_UARCH;
352 ret = cpu_specifier;
353 }
354 if (match_level < CPU_MODEL &&
355 simplified_cpu_model.find(cpu_specifier) != std::string::npos) {
356 match_level = CPU_MODEL;
357 ret = cpu_specifier;
358 }
359 }
360 return ret;
361 }
362
363 } // namespace internal
364
SetCollectionParamsFromVariationParams(const std::map<std::string,std::string> & params)365 void PerfCollector::SetCollectionParamsFromVariationParams(
366 const std::map<std::string, std::string>& params) {
367 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
368 int64_t value;
369 CollectionParams& collector_params = collection_params();
370 if (GetInt64Param(params, "ProfileCollectionDurationSec", &value)) {
371 collector_params.collection_duration = base::TimeDelta::FromSeconds(value);
372 }
373 if (GetInt64Param(params, "PeriodicProfilingIntervalMs", &value)) {
374 collector_params.periodic_interval =
375 base::TimeDelta::FromMilliseconds(value);
376 }
377 if (GetInt64Param(params, "ResumeFromSuspend::SamplingFactor", &value)) {
378 collector_params.resume_from_suspend.sampling_factor = value;
379 }
380 if (GetInt64Param(params, "ResumeFromSuspend::MaxDelaySec", &value)) {
381 collector_params.resume_from_suspend.max_collection_delay =
382 base::TimeDelta::FromSeconds(value);
383 }
384 if (GetInt64Param(params, "RestoreSession::SamplingFactor", &value)) {
385 collector_params.restore_session.sampling_factor = value;
386 }
387 if (GetInt64Param(params, "RestoreSession::MaxDelaySec", &value)) {
388 collector_params.restore_session.max_collection_delay =
389 base::TimeDelta::FromSeconds(value);
390 }
391
392 const std::string best_cpu_specifier =
393 internal::FindBestCpuSpecifierFromParams(params, GetCPUIdentity());
394
395 if (best_cpu_specifier.empty()) // No matching cpu specifier. Keep defaults.
396 return;
397
398 std::vector<RandomSelector::WeightAndValue> commands;
399 for (const auto& key_val : params) {
400 const std::string& key = key_val.first;
401 const std::string& val = key_val.second;
402
403 std::string cpu_specifier;
404 if (!ExtractPerfCommandCpuSpecifier(key, &cpu_specifier))
405 continue;
406 if (cpu_specifier != best_cpu_specifier)
407 continue;
408
409 auto split = val.find(" ");
410 if (split == std::string::npos)
411 continue; // Just drop invalid commands.
412 std::string weight_str = std::string(val.begin(), val.begin() + split);
413
414 double weight;
415 if (!(base::StringToDouble(weight_str, &weight) && weight > 0.0))
416 continue; // Just drop invalid commands.
417 std::string command(val.begin() + split + 1, val.end());
418 commands.push_back(RandomSelector::WeightAndValue(weight, command));
419 }
420 command_selector_.SetOdds(commands);
421 }
422
CreatePerfOutputCall(base::TimeDelta duration,const std::vector<std::string> & perf_args,PerfOutputCall::DoneCallback callback)423 std::unique_ptr<PerfOutputCall> PerfCollector::CreatePerfOutputCall(
424 base::TimeDelta duration,
425 const std::vector<std::string>& perf_args,
426 PerfOutputCall::DoneCallback callback) {
427 DCHECK(debugd_client_provider_.get());
428 return std::make_unique<PerfOutputCall>(
429 debugd_client_provider_->debug_daemon_client(), duration, perf_args,
430 std::move(callback));
431 }
432
OnPerfOutputComplete(std::unique_ptr<WindowedIncognitoObserver> incognito_observer,std::unique_ptr<SampledProfile> sampled_profile,bool has_cycles,std::string perf_stdout)433 void PerfCollector::OnPerfOutputComplete(
434 std::unique_ptr<WindowedIncognitoObserver> incognito_observer,
435 std::unique_ptr<SampledProfile> sampled_profile,
436 bool has_cycles,
437 std::string perf_stdout) {
438 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
439
440 current_trigger_ = SampledProfile::UNKNOWN_TRIGGER_EVENT;
441 // We are done using |perf_output_call| and may destroy it.
442 perf_output_call_ = nullptr;
443
444 ParseOutputProtoIfValid(std::move(incognito_observer),
445 std::move(sampled_profile), has_cycles,
446 std::move(perf_stdout));
447 }
448
ParseOutputProtoIfValid(std::unique_ptr<WindowedIncognitoObserver> incognito_observer,std::unique_ptr<SampledProfile> sampled_profile,bool has_cycles,std::string perf_stdout)449 void PerfCollector::ParseOutputProtoIfValid(
450 std::unique_ptr<WindowedIncognitoObserver> incognito_observer,
451 std::unique_ptr<SampledProfile> sampled_profile,
452 bool has_cycles,
453 std::string perf_stdout) {
454 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
455
456 // Check whether an incognito window had been opened during profile
457 // collection. If there was an incognito window, discard the incoming data.
458 if (incognito_observer->IncognitoLaunched()) {
459 AddToUmaHistogram(CollectionAttemptStatus::INCOGNITO_LAUNCHED);
460 return;
461 }
462 if (has_cycles) {
463 // Store CPU max frequencies in the sampled profile.
464 std::copy(max_frequencies_mhz_.begin(), max_frequencies_mhz_.end(),
465 google::protobuf::RepeatedFieldBackInserter(
466 sampled_profile->mutable_cpu_max_frequency_mhz()));
467 }
468
469 bool posted = base::ThreadPool::PostTaskAndReply(
470 FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
471 base::BindOnce(&OnCollectProcessTypes, sampled_profile.get()),
472 base::BindOnce(&PerfCollector::SaveSerializedPerfProto,
473 weak_factory_.GetWeakPtr(), std::move(sampled_profile),
474 std::move(perf_stdout)));
475 DCHECK(posted);
476 }
477
GetWeakPtr()478 base::WeakPtr<internal::MetricCollector> PerfCollector::GetWeakPtr() {
479 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
480 return weak_factory_.GetWeakPtr();
481 }
482
ShouldCollect() const483 bool PerfCollector::ShouldCollect() const {
484 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
485 // Only allow one active collection.
486 if (perf_output_call_) {
487 AddToUmaHistogram(CollectionAttemptStatus::ALREADY_COLLECTING);
488 return false;
489 }
490
491 // Do not collect further data if we've already collected a substantial amount
492 // of data, as indicated by |kCachedPerfDataProtobufSizeThreshold|.
493 if (cached_data_size_ >= kCachedPerfDataProtobufSizeThreshold) {
494 AddToUmaHistogram(CollectionAttemptStatus::NOT_READY_TO_COLLECT);
495 return false;
496 }
497
498 return true;
499 }
500
501 namespace internal {
502
CommandSamplesCPUCycles(const std::vector<std::string> & args)503 bool CommandSamplesCPUCycles(const std::vector<std::string>& args) {
504 // Command must start with "perf record".
505 if (args.size() < 4 || args[0] != "perf" || args[1] != "record")
506 return false;
507 // Cycles event can be either the raw 'cycles' event, or the event name can be
508 // annotated with some qualifier suffix. Check for all cases.
509 for (size_t i = 2; i + 1 < args.size(); ++i) {
510 if (args[i] == "-e" &&
511 (args[i + 1] == "cycles" || args[i + 1].rfind("cycles:", 0) == 0))
512 return true;
513 }
514 return false;
515 }
516
517 } // namespace internal
518
CollectProfile(std::unique_ptr<SampledProfile> sampled_profile)519 void PerfCollector::CollectProfile(
520 std::unique_ptr<SampledProfile> sampled_profile) {
521 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
522
523 auto incognito_observer = WindowedIncognitoMonitor::CreateObserver();
524 // For privacy reasons, Chrome should only collect perf data if there is no
525 // incognito session active (or gets spawned during the collection).
526 if (incognito_observer->IncognitoActive()) {
527 AddToUmaHistogram(CollectionAttemptStatus::INCOGNITO_ACTIVE);
528 return;
529 }
530
531 std::vector<std::string> command =
532 base::SplitString(command_selector_.Select(), kPerfCommandDelimiter,
533 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
534 bool has_cycles = internal::CommandSamplesCPUCycles(command);
535
536 DCHECK(sampled_profile->has_trigger_event());
537 current_trigger_ = sampled_profile->trigger_event();
538
539 perf_output_call_ = CreatePerfOutputCall(
540 collection_params().collection_duration, command,
541 base::BindOnce(&PerfCollector::OnPerfOutputComplete,
542 weak_factory_.GetWeakPtr(), std::move(incognito_observer),
543 std::move(sampled_profile), has_cycles));
544 }
545
546 // static
ParseCPUFrequencies(scoped_refptr<base::SequencedTaskRunner> task_runner,base::WeakPtr<PerfCollector> perf_collector)547 void PerfCollector::ParseCPUFrequencies(
548 scoped_refptr<base::SequencedTaskRunner> task_runner,
549 base::WeakPtr<PerfCollector> perf_collector) {
550 const char kCPUMaxFreqPath[] =
551 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq";
552 int num_cpus = base::SysInfo::NumberOfProcessors();
553 int num_zeros = 0;
554 std::vector<uint32_t> frequencies_mhz;
555 for (int i = 0; i < num_cpus; ++i) {
556 std::string content;
557 unsigned int frequency_khz = 0;
558 auto path = base::StringPrintf(kCPUMaxFreqPath, i);
559 if (ReadFileToString(base::FilePath(path), &content)) {
560 DCHECK(!content.empty());
561 base::StringToUint(content, &frequency_khz);
562 }
563 if (frequency_khz == 0) {
564 num_zeros++;
565 }
566 // Convert kHz frequencies to MHz.
567 frequencies_mhz.push_back(static_cast<uint32_t>(frequency_khz / 1000));
568 }
569 if (num_cpus == 0) {
570 base::UmaHistogramEnumeration(kParseFrequenciesHistogramName,
571 ParseFrequencyStatus::kNumCPUsIsZero);
572 } else if (num_zeros == num_cpus) {
573 base::UmaHistogramEnumeration(kParseFrequenciesHistogramName,
574 ParseFrequencyStatus::kAllZeroCPUFrequencies);
575 } else if (num_zeros > 0) {
576 base::UmaHistogramEnumeration(
577 kParseFrequenciesHistogramName,
578 ParseFrequencyStatus::kSomeZeroCPUFrequencies);
579 } else {
580 base::UmaHistogramEnumeration(kParseFrequenciesHistogramName,
581 ParseFrequencyStatus::kSuccess);
582 }
583 task_runner->PostTask(FROM_HERE,
584 base::BindOnce(&PerfCollector::SaveCPUFrequencies,
585 perf_collector, frequencies_mhz));
586 }
587
SaveCPUFrequencies(const std::vector<uint32_t> & frequencies)588 void PerfCollector::SaveCPUFrequencies(
589 const std::vector<uint32_t>& frequencies) {
590 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
591 max_frequencies_mhz_ = frequencies;
592 }
593
StopCollection()594 void PerfCollector::StopCollection() {
595 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
596 // StopCollection() can be called when a jank lasts for longer than the max
597 // collection duration, and a new collection is requested by another trigger.
598 // In this case, ignore the request to stop the collection.
599 if (current_trigger_ != SampledProfile::JANKY_TASK)
600 return;
601
602 if (perf_output_call_)
603 perf_output_call_->Stop();
604 }
605
606 } // namespace metrics
607