• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 using base::TimeTicks;
17 using content::BrowserThread;
18 
19 namespace {
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.
26 const 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.
33 static metrics::TrackingSynchronizer* g_tracking_synchronizer =
34     NULL;
35 
36 }  // anonymous namespace
37 
38 namespace metrics {
39 
40 // The "RequestContext" structure describes an individual request received
41 // from the UI. All methods are accessible on UI thread.
42 class TrackingSynchronizer::RequestContext {
43  public:
44   // A map from sequence_number_ to the actual RequestContexts.
45   typedef std::map<int, RequestContext*> RequestContextMap;
46 
RequestContext(const base::WeakPtr<TrackingSynchronizerObserver> & callback_object,int sequence_number)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   }
~RequestContext()55   ~RequestContext() {}
56 
SetReceivedProcessGroupCount(bool done)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_.
IncrementProcessesPending()63   void IncrementProcessesPending() {
64     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65     ++processes_pending_;
66   }
67 
AddProcessesPending(int processes_pending)68   void AddProcessesPending(int processes_pending) {
69     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
70     processes_pending_ += processes_pending;
71   }
72 
DecrementProcessesPending()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.
DeleteIfAllDone()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|.
Register(int sequence_number,const base::WeakPtr<TrackingSynchronizerObserver> & callback_object)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|.
GetRequestContext(int 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).
Unregister(int sequence_number)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.
OnShutdown()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
176 base::LazyInstance
177     <TrackingSynchronizer::RequestContext::RequestContextMap>::Leaky
178         TrackingSynchronizer::RequestContext::outstanding_requests_ =
179             LAZY_INSTANCE_INITIALIZER;
180 
181 // TrackingSynchronizer methods and members.
182 
TrackingSynchronizer()183 TrackingSynchronizer::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 
~TrackingSynchronizer()190 TrackingSynchronizer::~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
FetchProfilerDataAsynchronously(const base::WeakPtr<TrackingSynchronizerObserver> & callback_object)200 void 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 
OnPendingProcesses(int sequence_number,int pending_processes,bool end)220 void 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 
OnProfilerDataCollected(int sequence_number,const tracked_objects::ProcessDataSnapshot & profiler_data,int process_type)233 void 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 
RegisterAndNotifyAllProcesses(const base::WeakPtr<TrackingSynchronizerObserver> & callback_object)242 int 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 
DecrementPendingProcessesAndSendData(int sequence_number,const tracked_objects::ProcessDataSnapshot & profiler_data,int process_type)266 void 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 
GetNextAvailableSequenceNumber()286 int 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