1// Copyright (c) 2012 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 "components/metrics/profiler/tracking_synchronizer.h"
6
7#include "base/bind.h"
8#include "base/metrics/histogram.h"
9#include "base/threading/thread.h"
10#include "base/tracked_objects.h"
11#include "components/metrics/profiler/tracking_synchronizer_observer.h"
12#include "content/public/browser/browser_thread.h"
13#include "content/public/browser/profiler_controller.h"
14#include "content/public/common/process_type.h"
15
16using base::TimeTicks;
17using content::BrowserThread;
18
19namespace {
20
21// Negative numbers are never used as sequence numbers.  We explicitly pick a
22// negative number that is "so negative" that even when we add one (as is done
23// when we generated the next sequence number) that it will still be negative.
24// We have code that handles wrapping around on an overflow into negative
25// territory.
26const int kNeverUsableSequenceNumber = -2;
27
28// This singleton instance should be started during the single threaded
29// portion of main(). It initializes globals to provide support for all future
30// calls. This object is created on the UI thread, and it is destroyed after
31// all the other threads have gone away. As a result, it is ok to call it
32// from the UI thread, or for about:profiler.
33static metrics::TrackingSynchronizer* g_tracking_synchronizer =
34    NULL;
35
36}  // anonymous namespace
37
38namespace metrics {
39
40// The "RequestContext" structure describes an individual request received
41// from the UI. All methods are accessible on UI thread.
42class TrackingSynchronizer::RequestContext {
43 public:
44  // A map from sequence_number_ to the actual RequestContexts.
45  typedef std::map<int, RequestContext*> RequestContextMap;
46
47  RequestContext(
48      const base::WeakPtr<TrackingSynchronizerObserver>& callback_object,
49      int sequence_number)
50      : callback_object_(callback_object),
51        sequence_number_(sequence_number),
52        received_process_group_count_(0),
53        processes_pending_(0) {
54  }
55  ~RequestContext() {}
56
57  void SetReceivedProcessGroupCount(bool done) {
58    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59    received_process_group_count_ = done;
60  }
61
62  // Methods for book keeping of processes_pending_.
63  void IncrementProcessesPending() {
64    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65    ++processes_pending_;
66  }
67
68  void AddProcessesPending(int processes_pending) {
69    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
70    processes_pending_ += processes_pending;
71  }
72
73  void DecrementProcessesPending() {
74    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75    --processes_pending_;
76  }
77
78  // Records that we are waiting for one less tracking data from a process for
79  // the given sequence number. If |received_process_group_count_| and
80  // |processes_pending_| are zero, then delete the current object by calling
81  // Unregister.
82  void DeleteIfAllDone() {
83    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
84
85    if (processes_pending_ <= 0 && received_process_group_count_)
86      RequestContext::Unregister(sequence_number_);
87  }
88
89  // Register |callback_object| in |outstanding_requests_| map for the given
90  // |sequence_number|.
91  static RequestContext* Register(
92      int sequence_number,
93      const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
94    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95
96    RequestContext* request = new RequestContext(
97        callback_object, sequence_number);
98    outstanding_requests_.Get()[sequence_number] = request;
99
100    return request;
101  }
102
103  // Find the |RequestContext| in |outstanding_requests_| map for the given
104  // |sequence_number|.
105  static RequestContext* GetRequestContext(int sequence_number) {
106    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107
108    RequestContextMap::iterator it =
109        outstanding_requests_.Get().find(sequence_number);
110    if (it == outstanding_requests_.Get().end())
111      return NULL;
112
113    RequestContext* request = it->second;
114    DCHECK_EQ(sequence_number, request->sequence_number_);
115    return request;
116  }
117
118  // Delete the entry for the given |sequence_number| from
119  // |outstanding_requests_| map. This method is called when all changes have
120  // been acquired, or when the wait time expires (whichever is sooner).
121  static void Unregister(int sequence_number) {
122    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
123
124    RequestContextMap::iterator it =
125        outstanding_requests_.Get().find(sequence_number);
126    if (it == outstanding_requests_.Get().end())
127      return;
128
129    RequestContext* request = it->second;
130    DCHECK_EQ(sequence_number, request->sequence_number_);
131    bool received_process_group_count = request->received_process_group_count_;
132    int unresponsive_processes = request->processes_pending_;
133
134    if (request->callback_object_.get())
135      request->callback_object_->FinishedReceivingProfilerData();
136
137    delete request;
138    outstanding_requests_.Get().erase(it);
139
140    UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount",
141                          received_process_group_count);
142    UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
143                         unresponsive_processes);
144  }
145
146  // Delete all the entries in |outstanding_requests_| map.
147  static void OnShutdown() {
148    // Just in case we have any pending tasks, clear them out.
149    while (!outstanding_requests_.Get().empty()) {
150      RequestContextMap::iterator it = outstanding_requests_.Get().begin();
151      delete it->second;
152      outstanding_requests_.Get().erase(it);
153    }
154  }
155
156  // Requests are made to asynchronously send data to the |callback_object_|.
157  base::WeakPtr<TrackingSynchronizerObserver> callback_object_;
158
159  // The sequence number used by the most recent update request to contact all
160  // processes.
161  int sequence_number_;
162
163  // Indicates if we have received all pending processes count.
164  bool received_process_group_count_;
165
166  // The number of pending processes (browser, all renderer processes and
167  // browser child processes) that have not yet responded to requests.
168  int processes_pending_;
169
170  // Map of all outstanding RequestContexts, from sequence_number_ to
171  // RequestContext.
172  static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
173};
174
175// static
176base::LazyInstance
177    <TrackingSynchronizer::RequestContext::RequestContextMap>::Leaky
178        TrackingSynchronizer::RequestContext::outstanding_requests_ =
179            LAZY_INSTANCE_INITIALIZER;
180
181// TrackingSynchronizer methods and members.
182
183TrackingSynchronizer::TrackingSynchronizer()
184    : last_used_sequence_number_(kNeverUsableSequenceNumber) {
185  DCHECK(!g_tracking_synchronizer);
186  g_tracking_synchronizer = this;
187  content::ProfilerController::GetInstance()->Register(this);
188}
189
190TrackingSynchronizer::~TrackingSynchronizer() {
191  content::ProfilerController::GetInstance()->Unregister(this);
192
193  // Just in case we have any pending tasks, clear them out.
194  RequestContext::OnShutdown();
195
196  g_tracking_synchronizer = NULL;
197}
198
199// static
200void TrackingSynchronizer::FetchProfilerDataAsynchronously(
201    const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
202  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203
204  if (!g_tracking_synchronizer) {
205    // System teardown is happening.
206    return;
207  }
208
209  int sequence_number = g_tracking_synchronizer->RegisterAndNotifyAllProcesses(
210      callback_object);
211
212  // Post a task that would be called after waiting for wait_time.  This acts
213  // as a watchdog, to cancel the requests for non-responsive processes.
214  BrowserThread::PostDelayedTask(
215      BrowserThread::UI, FROM_HERE,
216      base::Bind(&RequestContext::Unregister, sequence_number),
217      base::TimeDelta::FromMinutes(1));
218}
219
220void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
221                                              int pending_processes,
222                                              bool end) {
223  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224
225  RequestContext* request = RequestContext::GetRequestContext(sequence_number);
226  if (!request)
227    return;
228  request->AddProcessesPending(pending_processes);
229  request->SetReceivedProcessGroupCount(end);
230  request->DeleteIfAllDone();
231}
232
233void TrackingSynchronizer::OnProfilerDataCollected(
234    int sequence_number,
235    const tracked_objects::ProcessDataSnapshot& profiler_data,
236    int process_type) {
237  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238  DecrementPendingProcessesAndSendData(sequence_number, profiler_data,
239                                       process_type);
240}
241
242int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
243    const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
244  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245
246  int sequence_number = GetNextAvailableSequenceNumber();
247
248  RequestContext* request =
249      RequestContext::Register(sequence_number, callback_object);
250
251  // Increment pending process count for sending browser's profiler data.
252  request->IncrementProcessesPending();
253
254  // Get profiler data from renderer and browser child processes.
255  content::ProfilerController::GetInstance()->GetProfilerData(sequence_number);
256
257  // Send profiler_data from browser process.
258  tracked_objects::ProcessDataSnapshot process_data;
259  tracked_objects::ThreadData::Snapshot(false, &process_data);
260  DecrementPendingProcessesAndSendData(sequence_number, process_data,
261                                       content::PROCESS_TYPE_BROWSER);
262
263  return sequence_number;
264}
265
266void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
267    int sequence_number,
268    const tracked_objects::ProcessDataSnapshot& profiler_data,
269    int process_type) {
270  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272  RequestContext* request = RequestContext::GetRequestContext(sequence_number);
273  if (!request)
274    return;
275
276  if (request->callback_object_.get()) {
277    request->callback_object_
278        ->ReceivedProfilerData(profiler_data, process_type);
279  }
280
281  // Delete request if we have heard back from all child processes.
282  request->DecrementProcessesPending();
283  request->DeleteIfAllDone();
284}
285
286int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
287  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288
289  ++last_used_sequence_number_;
290
291  // Watch out for wrapping to a negative number.
292  if (last_used_sequence_number_ < 0)
293    last_used_sequence_number_ = 1;
294  return last_used_sequence_number_;
295}
296
297}  // namespace metrics
298