1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "components/metrics/histogram_controller.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include "base/functional/bind.h"
8*6777b538SAndroid Build Coastguard Worker #include "base/location.h"
9*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_functions.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/process/process_handle.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/rand_util.h"
13*6777b538SAndroid Build Coastguard Worker #include "components/metrics/histogram_subscriber.h"
14*6777b538SAndroid Build Coastguard Worker #include "components/metrics/public/mojom/histogram_fetcher.mojom.h"
15*6777b538SAndroid Build Coastguard Worker #include "mojo/public/cpp/bindings/callback_helpers.h"
16*6777b538SAndroid Build Coastguard Worker
17*6777b538SAndroid Build Coastguard Worker namespace metrics {
18*6777b538SAndroid Build Coastguard Worker
19*6777b538SAndroid Build Coastguard Worker namespace {
GetPingHistogramName(mojom::UmaPingCallSource call_source)20*6777b538SAndroid Build Coastguard Worker const char* GetPingHistogramName(mojom::UmaPingCallSource call_source) {
21*6777b538SAndroid Build Coastguard Worker switch (call_source) {
22*6777b538SAndroid Build Coastguard Worker case mojom::UmaPingCallSource::PERIODIC:
23*6777b538SAndroid Build Coastguard Worker return "UMA.ChildProcess.Ping.Periodic";
24*6777b538SAndroid Build Coastguard Worker case mojom::UmaPingCallSource::SHARED_MEMORY_SET_UP:
25*6777b538SAndroid Build Coastguard Worker return "UMA.ChildProcess.Ping.SharedMemorySetUp";
26*6777b538SAndroid Build Coastguard Worker }
27*6777b538SAndroid Build Coastguard Worker }
28*6777b538SAndroid Build Coastguard Worker } // namespace
29*6777b538SAndroid Build Coastguard Worker
30*6777b538SAndroid Build Coastguard Worker struct HistogramController::ChildHistogramFetcher {
31*6777b538SAndroid Build Coastguard Worker mojo::Remote<mojom::ChildHistogramFetcher> remote;
32*6777b538SAndroid Build Coastguard Worker ChildProcessMode mode;
33*6777b538SAndroid Build Coastguard Worker };
34*6777b538SAndroid Build Coastguard Worker
GetInstance()35*6777b538SAndroid Build Coastguard Worker HistogramController* HistogramController::GetInstance() {
36*6777b538SAndroid Build Coastguard Worker return base::Singleton<HistogramController, base::LeakySingletonTraits<
37*6777b538SAndroid Build Coastguard Worker HistogramController>>::get();
38*6777b538SAndroid Build Coastguard Worker }
39*6777b538SAndroid Build Coastguard Worker
HistogramController()40*6777b538SAndroid Build Coastguard Worker HistogramController::HistogramController() : subscriber_(nullptr) {
41*6777b538SAndroid Build Coastguard Worker // Unretained is safe because |this| is leaky.
42*6777b538SAndroid Build Coastguard Worker timer_.Start(FROM_HERE, base::Minutes(5),
43*6777b538SAndroid Build Coastguard Worker base::BindRepeating(&HistogramController::PingChildProcesses,
44*6777b538SAndroid Build Coastguard Worker base::Unretained(this)));
45*6777b538SAndroid Build Coastguard Worker }
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Worker HistogramController::~HistogramController() = default;
48*6777b538SAndroid Build Coastguard Worker
Register(HistogramSubscriber * subscriber)49*6777b538SAndroid Build Coastguard Worker void HistogramController::Register(HistogramSubscriber* subscriber) {
50*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
51*6777b538SAndroid Build Coastguard Worker DCHECK(!subscriber_);
52*6777b538SAndroid Build Coastguard Worker subscriber_ = subscriber;
53*6777b538SAndroid Build Coastguard Worker }
54*6777b538SAndroid Build Coastguard Worker
Unregister(const HistogramSubscriber * subscriber)55*6777b538SAndroid Build Coastguard Worker void HistogramController::Unregister(const HistogramSubscriber* subscriber) {
56*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
57*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(subscriber_, subscriber);
58*6777b538SAndroid Build Coastguard Worker subscriber_ = nullptr;
59*6777b538SAndroid Build Coastguard Worker }
60*6777b538SAndroid Build Coastguard Worker
NotifyChildDied(HistogramChildProcess * host)61*6777b538SAndroid Build Coastguard Worker void HistogramController::NotifyChildDied(HistogramChildProcess* host) {
62*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
63*6777b538SAndroid Build Coastguard Worker RemoveChildHistogramFetcherInterface(
64*6777b538SAndroid Build Coastguard Worker MayBeDangling<HistogramChildProcess>(host));
65*6777b538SAndroid Build Coastguard Worker }
66*6777b538SAndroid Build Coastguard Worker
SetHistogramMemory(HistogramChildProcess * host,base::UnsafeSharedMemoryRegion shared_region,ChildProcessMode mode)67*6777b538SAndroid Build Coastguard Worker void HistogramController::SetHistogramMemory(
68*6777b538SAndroid Build Coastguard Worker HistogramChildProcess* host,
69*6777b538SAndroid Build Coastguard Worker base::UnsafeSharedMemoryRegion shared_region,
70*6777b538SAndroid Build Coastguard Worker ChildProcessMode mode) {
71*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
72*6777b538SAndroid Build Coastguard Worker mojo::Remote<mojom::ChildHistogramFetcherFactory> factory;
73*6777b538SAndroid Build Coastguard Worker host->BindChildHistogramFetcherFactory(factory.BindNewPipeAndPassReceiver());
74*6777b538SAndroid Build Coastguard Worker
75*6777b538SAndroid Build Coastguard Worker mojo::Remote<mojom::ChildHistogramFetcher> fetcher;
76*6777b538SAndroid Build Coastguard Worker factory->CreateFetcher(std::move(shared_region),
77*6777b538SAndroid Build Coastguard Worker fetcher.BindNewPipeAndPassReceiver());
78*6777b538SAndroid Build Coastguard Worker PingChildProcess(fetcher.get(),
79*6777b538SAndroid Build Coastguard Worker mojom::UmaPingCallSource::SHARED_MEMORY_SET_UP);
80*6777b538SAndroid Build Coastguard Worker InsertChildHistogramFetcherInterface(host, std::move(fetcher), mode);
81*6777b538SAndroid Build Coastguard Worker }
82*6777b538SAndroid Build Coastguard Worker
InsertChildHistogramFetcherInterface(HistogramChildProcess * host,mojo::Remote<mojom::ChildHistogramFetcher> child_histogram_fetcher,ChildProcessMode mode)83*6777b538SAndroid Build Coastguard Worker void HistogramController::InsertChildHistogramFetcherInterface(
84*6777b538SAndroid Build Coastguard Worker HistogramChildProcess* host,
85*6777b538SAndroid Build Coastguard Worker mojo::Remote<mojom::ChildHistogramFetcher> child_histogram_fetcher,
86*6777b538SAndroid Build Coastguard Worker ChildProcessMode mode) {
87*6777b538SAndroid Build Coastguard Worker // Broken pipe means remove this from the map. The map size is a proxy for
88*6777b538SAndroid Build Coastguard Worker // the number of known processes
89*6777b538SAndroid Build Coastguard Worker //
90*6777b538SAndroid Build Coastguard Worker // `RemoveChildHistogramFetcherInterface` will only use `host` for address
91*6777b538SAndroid Build Coastguard Worker // comparison without being dereferenced , therefore it's not going to create
92*6777b538SAndroid Build Coastguard Worker // a UAF.
93*6777b538SAndroid Build Coastguard Worker child_histogram_fetcher.set_disconnect_handler(
94*6777b538SAndroid Build Coastguard Worker base::BindOnce(&HistogramController::RemoveChildHistogramFetcherInterface,
95*6777b538SAndroid Build Coastguard Worker base::Unretained(this), base::UnsafeDangling(host)));
96*6777b538SAndroid Build Coastguard Worker child_histogram_fetchers_.emplace(
97*6777b538SAndroid Build Coastguard Worker host, ChildHistogramFetcher{std::move(child_histogram_fetcher), mode});
98*6777b538SAndroid Build Coastguard Worker }
99*6777b538SAndroid Build Coastguard Worker
PingChildProcesses()100*6777b538SAndroid Build Coastguard Worker void HistogramController::PingChildProcesses() {
101*6777b538SAndroid Build Coastguard Worker // Only ping ~10% of child processes to avoid possibly "waking up" too many
102*6777b538SAndroid Build Coastguard Worker // and causing unnecessary work.
103*6777b538SAndroid Build Coastguard Worker for (const auto& fetcher : child_histogram_fetchers_) {
104*6777b538SAndroid Build Coastguard Worker if (base::RandGenerator(/*range=*/10) == 0) {
105*6777b538SAndroid Build Coastguard Worker PingChildProcess(fetcher.second.remote.get(),
106*6777b538SAndroid Build Coastguard Worker mojom::UmaPingCallSource::PERIODIC);
107*6777b538SAndroid Build Coastguard Worker }
108*6777b538SAndroid Build Coastguard Worker }
109*6777b538SAndroid Build Coastguard Worker }
110*6777b538SAndroid Build Coastguard Worker
PingChildProcess(mojom::ChildHistogramFetcherProxy * fetcher,mojom::UmaPingCallSource call_source)111*6777b538SAndroid Build Coastguard Worker void HistogramController::PingChildProcess(
112*6777b538SAndroid Build Coastguard Worker mojom::ChildHistogramFetcherProxy* fetcher,
113*6777b538SAndroid Build Coastguard Worker mojom::UmaPingCallSource call_source) {
114*6777b538SAndroid Build Coastguard Worker // 1) Emit a histogram, 2) ping the child process (which should also emit a
115*6777b538SAndroid Build Coastguard Worker // histogram), and 3) call Pong(), which again emits a histogram.
116*6777b538SAndroid Build Coastguard Worker // If no histograms are lost, in total, the histograms should all be emitted
117*6777b538SAndroid Build Coastguard Worker // roughly the same amount of times. The exception is for 1), which may be
118*6777b538SAndroid Build Coastguard Worker // emitted more often because this may be called early on in the lifecycle of
119*6777b538SAndroid Build Coastguard Worker // the child process, and some child processes are killed very early on,
120*6777b538SAndroid Build Coastguard Worker // before any IPC messages are processed.
121*6777b538SAndroid Build Coastguard Worker base::UmaHistogramEnumeration(GetPingHistogramName(call_source),
122*6777b538SAndroid Build Coastguard Worker mojom::UmaChildPingStatus::BROWSER_SENT_IPC);
123*6777b538SAndroid Build Coastguard Worker // Unretained is safe because |this| is leaky.
124*6777b538SAndroid Build Coastguard Worker fetcher->Ping(call_source,
125*6777b538SAndroid Build Coastguard Worker base::BindOnce(&HistogramController::Pong,
126*6777b538SAndroid Build Coastguard Worker base::Unretained(this), call_source));
127*6777b538SAndroid Build Coastguard Worker }
128*6777b538SAndroid Build Coastguard Worker
Pong(mojom::UmaPingCallSource call_source)129*6777b538SAndroid Build Coastguard Worker void HistogramController::Pong(mojom::UmaPingCallSource call_source) {
130*6777b538SAndroid Build Coastguard Worker base::UmaHistogramEnumeration(
131*6777b538SAndroid Build Coastguard Worker GetPingHistogramName(call_source),
132*6777b538SAndroid Build Coastguard Worker mojom::UmaChildPingStatus::BROWSER_REPLY_CALLBACK);
133*6777b538SAndroid Build Coastguard Worker }
134*6777b538SAndroid Build Coastguard Worker
RemoveChildHistogramFetcherInterface(MayBeDangling<HistogramChildProcess> host)135*6777b538SAndroid Build Coastguard Worker void HistogramController::RemoveChildHistogramFetcherInterface(
136*6777b538SAndroid Build Coastguard Worker MayBeDangling<HistogramChildProcess> host) {
137*6777b538SAndroid Build Coastguard Worker child_histogram_fetchers_.erase(host);
138*6777b538SAndroid Build Coastguard Worker }
139*6777b538SAndroid Build Coastguard Worker
GetHistogramData(int sequence_number)140*6777b538SAndroid Build Coastguard Worker void HistogramController::GetHistogramData(int sequence_number) {
141*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
142*6777b538SAndroid Build Coastguard Worker
143*6777b538SAndroid Build Coastguard Worker int pending_processes = 0;
144*6777b538SAndroid Build Coastguard Worker for (const auto& fetcher : child_histogram_fetchers_) {
145*6777b538SAndroid Build Coastguard Worker if (fetcher.second.mode != ChildProcessMode::kGetHistogramData) {
146*6777b538SAndroid Build Coastguard Worker continue;
147*6777b538SAndroid Build Coastguard Worker }
148*6777b538SAndroid Build Coastguard Worker
149*6777b538SAndroid Build Coastguard Worker fetcher.second.remote->GetChildNonPersistentHistogramData(
150*6777b538SAndroid Build Coastguard Worker mojo::WrapCallbackWithDefaultInvokeIfNotRun(
151*6777b538SAndroid Build Coastguard Worker base::BindOnce(&HistogramController::OnHistogramDataCollected,
152*6777b538SAndroid Build Coastguard Worker base::Unretained(this), sequence_number),
153*6777b538SAndroid Build Coastguard Worker std::vector<std::string>()));
154*6777b538SAndroid Build Coastguard Worker ++pending_processes;
155*6777b538SAndroid Build Coastguard Worker }
156*6777b538SAndroid Build Coastguard Worker
157*6777b538SAndroid Build Coastguard Worker if (subscriber_) {
158*6777b538SAndroid Build Coastguard Worker subscriber_->OnPendingProcesses(sequence_number, pending_processes, true);
159*6777b538SAndroid Build Coastguard Worker }
160*6777b538SAndroid Build Coastguard Worker }
161*6777b538SAndroid Build Coastguard Worker
OnHistogramDataCollected(int sequence_number,const std::vector<std::string> & pickled_histograms)162*6777b538SAndroid Build Coastguard Worker void HistogramController::OnHistogramDataCollected(
163*6777b538SAndroid Build Coastguard Worker int sequence_number,
164*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& pickled_histograms) {
165*6777b538SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
166*6777b538SAndroid Build Coastguard Worker if (subscriber_) {
167*6777b538SAndroid Build Coastguard Worker subscriber_->OnHistogramDataCollected(sequence_number, pickled_histograms);
168*6777b538SAndroid Build Coastguard Worker }
169*6777b538SAndroid Build Coastguard Worker }
170*6777b538SAndroid Build Coastguard Worker
171*6777b538SAndroid Build Coastguard Worker } // namespace metrics
172