histogram_synchronizer.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2009 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/histogram_synchronizer.h"
6
7#include "base/metrics/histogram.h"
8#include "base/logging.h"
9#include "base/thread.h"
10#include "chrome/browser/browser_thread.h"
11#include "chrome/browser/renderer_host/render_process_host.h"
12#include "chrome/common/chrome_constants.h"
13#include "chrome/common/render_messages.h"
14
15using base::Time;
16using base::TimeDelta;
17using base::TimeTicks;
18
19HistogramSynchronizer::HistogramSynchronizer()
20  : lock_(),
21    received_all_renderer_historgrams_(&lock_),
22    callback_task_(NULL),
23    callback_thread_(NULL),
24    io_message_loop_(NULL),
25    next_available_sequence_number_(0),
26    async_sequence_number_(0),
27    async_renderers_pending_(0),
28    async_callback_start_time_(TimeTicks::Now()),
29    synchronous_sequence_number_(0),
30    synchronous_renderers_pending_(0) {
31  DCHECK(histogram_synchronizer_ == NULL);
32  histogram_synchronizer_ = this;
33}
34
35HistogramSynchronizer::~HistogramSynchronizer() {
36  // Clean up.
37  delete callback_task_;
38  callback_task_ = NULL;
39  callback_thread_ = NULL;
40  histogram_synchronizer_ = NULL;
41}
42
43// static
44HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() {
45  DCHECK(histogram_synchronizer_ != NULL);
46  return histogram_synchronizer_;
47}
48
49void HistogramSynchronizer::FetchRendererHistogramsSynchronously(
50    TimeDelta wait_time) {
51  DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
52
53  int sequence_number = GetNextAvaibleSequenceNumber(SYNCHRONOUS_HISTOGRAMS);
54  for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
55       !it.IsAtEnd(); it.Advance()) {
56    it.GetCurrentValue()->Send(
57        new ViewMsg_GetRendererHistograms(sequence_number));
58    IncrementPendingRenderers(SYNCHRONOUS_HISTOGRAMS);
59  }
60
61  TimeTicks start = TimeTicks::Now();
62  TimeTicks end_time = start + wait_time;
63  int unresponsive_renderer_count;
64  {
65    AutoLock auto_lock(lock_);
66    while (synchronous_renderers_pending_ > 0 &&
67           TimeTicks::Now() < end_time) {
68      wait_time = end_time - TimeTicks::Now();
69      received_all_renderer_historgrams_.TimedWait(wait_time);
70    }
71    unresponsive_renderer_count = synchronous_renderers_pending_;
72    synchronous_renderers_pending_ = 0;
73    synchronous_sequence_number_ = 0;
74  }
75  UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingSynchronous",
76                       unresponsive_renderer_count);
77  if (!unresponsive_renderer_count)
78    UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsSynchronously",
79                        TimeTicks::Now() - start);
80}
81
82// static
83void HistogramSynchronizer::FetchRendererHistogramsAsynchronously(
84    MessageLoop* callback_thread,
85    Task* callback_task,
86    int wait_time) {
87  DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
88  DCHECK(callback_thread != NULL);
89  DCHECK(callback_task != NULL);
90
91  HistogramSynchronizer* current_synchronizer =
92      HistogramSynchronizer::CurrentSynchronizer();
93
94  if (current_synchronizer == NULL) {
95    // System teardown is happening.
96    callback_thread->PostTask(FROM_HERE, callback_task);
97    return;
98  }
99
100  // callback_task_ member can only be accessed on IO thread.
101  BrowserThread::PostTask(
102      BrowserThread::IO, FROM_HERE,
103      NewRunnableMethod(
104          current_synchronizer,
105          &HistogramSynchronizer::SetCallbackTaskToCallAfterGettingHistograms,
106          callback_thread,
107          callback_task));
108
109  // Tell all renderer processes to send their histograms.
110  int sequence_number =
111      current_synchronizer->GetNextAvaibleSequenceNumber(ASYNC_HISTOGRAMS);
112  for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
113       !it.IsAtEnd(); it.Advance()) {
114    it.GetCurrentValue()->Send(
115        new ViewMsg_GetRendererHistograms(sequence_number));
116    current_synchronizer->IncrementPendingRenderers(ASYNC_HISTOGRAMS);
117  }
118
119  // Post a task that would be called after waiting for wait_time.
120  BrowserThread::PostDelayedTask(
121      BrowserThread::IO, FROM_HERE,
122      NewRunnableMethod(
123          current_synchronizer,
124          &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
125          sequence_number),
126      wait_time);
127}
128
129// static
130void HistogramSynchronizer::DeserializeHistogramList(
131    int sequence_number,
132    const std::vector<std::string>& histograms) {
133  HistogramSynchronizer* current_synchronizer =
134      HistogramSynchronizer::CurrentSynchronizer();
135  if (current_synchronizer == NULL)
136    return;
137
138  DCHECK(current_synchronizer->IsOnIoThread());
139
140  for (std::vector<std::string>::const_iterator it = histograms.begin();
141       it < histograms.end();
142       ++it) {
143    base::Histogram::DeserializeHistogramInfo(*it);
144  }
145
146  // Record that we have received a histogram from renderer process.
147  current_synchronizer->RecordRendererHistogram(sequence_number);
148}
149
150bool HistogramSynchronizer::RecordRendererHistogram(int sequence_number) {
151  DCHECK(IsOnIoThread());
152
153  if (sequence_number == async_sequence_number_) {
154    if ((async_renderers_pending_ == 0) ||
155        (--async_renderers_pending_ > 0))
156      return false;
157    DCHECK(callback_task_ != NULL);
158    CallCallbackTaskAndResetData();
159    return true;
160  }
161
162  {
163    AutoLock auto_lock(lock_);
164    if (sequence_number != synchronous_sequence_number_) {
165      // No need to do anything if the sequence_number does not match current
166      // synchronous_sequence_number_ or async_sequence_number_.
167      return true;
168    }
169    if ((synchronous_renderers_pending_ == 0) ||
170        (--synchronous_renderers_pending_ > 0))
171      return false;
172    DCHECK_EQ(synchronous_renderers_pending_, 0);
173  }
174
175  // We could call Signal() without holding the lock.
176  received_all_renderer_historgrams_.Signal();
177  return true;
178}
179
180// This method is called on the IO thread.
181void HistogramSynchronizer::SetCallbackTaskToCallAfterGettingHistograms(
182    MessageLoop* callback_thread,
183    Task* callback_task) {
184  DCHECK(IsOnIoThread());
185
186  // Test for the existence of a previous task, and post call to post it if it
187  // exists. We promised to post it after some timeout... and at this point, we
188  // should just force the posting.
189  if (callback_task_ != NULL) {
190    CallCallbackTaskAndResetData();
191  }
192
193  // Assert there was no callback_task_ already.
194  DCHECK(callback_task_ == NULL);
195
196  // Save the thread and the callback_task.
197  DCHECK(callback_thread != NULL);
198  DCHECK(callback_task != NULL);
199  callback_task_ = callback_task;
200  callback_thread_ = callback_thread;
201  async_callback_start_time_ = TimeTicks::Now();
202}
203
204void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
205    int sequence_number) {
206  DCHECK(IsOnIoThread());
207
208  if (sequence_number == async_sequence_number_) {
209     CallCallbackTaskAndResetData();
210  }
211}
212
213// If wait time has elapsed or if we have received all the histograms from all
214// the renderers, call the callback_task if a callback_task exists. This is
215// called on IO Thread.
216void HistogramSynchronizer::CallCallbackTaskAndResetData() {
217  DCHECK(IsOnIoThread());
218
219  // callback_task_ would be set to NULL, if we have heard from all renderers
220  // and we would have called the callback_task already.
221  if (callback_task_ == NULL) {
222    return;
223  }
224
225  UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous",
226                       async_renderers_pending_);
227  if (!async_renderers_pending_)
228    UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously",
229                        TimeTicks::Now() - async_callback_start_time_);
230
231  DCHECK(callback_thread_ != NULL);
232  DCHECK(callback_task_ != NULL);
233  callback_thread_->PostTask(FROM_HERE, callback_task_);
234  async_renderers_pending_ = 0;
235  async_callback_start_time_ = TimeTicks::Now();
236  callback_task_ = NULL;
237  callback_thread_ = NULL;
238}
239
240int HistogramSynchronizer::GetNextAvaibleSequenceNumber(
241    RendererHistogramRequester requester) {
242  AutoLock auto_lock(lock_);
243  ++next_available_sequence_number_;
244  if (0 > next_available_sequence_number_) {
245    // We wrapped around.
246    next_available_sequence_number_ =
247        chrome::kHistogramSynchronizerReservedSequenceNumber + 1;
248  }
249  DCHECK_NE(next_available_sequence_number_,
250            chrome::kHistogramSynchronizerReservedSequenceNumber);
251  if (requester == ASYNC_HISTOGRAMS) {
252    async_sequence_number_ = next_available_sequence_number_;
253    async_renderers_pending_ = 0;
254  } else if (requester == SYNCHRONOUS_HISTOGRAMS) {
255    synchronous_sequence_number_ = next_available_sequence_number_;
256    synchronous_renderers_pending_ = 0;
257  }
258  return next_available_sequence_number_;
259}
260
261void HistogramSynchronizer::IncrementPendingRenderers(
262    RendererHistogramRequester requester) {
263  if (requester == ASYNC_HISTOGRAMS) {
264    async_renderers_pending_++;
265  } else {
266    synchronous_renderers_pending_++;
267  }
268}
269
270bool HistogramSynchronizer::IsOnIoThread() {
271  if (io_message_loop_ == NULL) {
272    io_message_loop_ = MessageLoop::current();
273  }
274  return (MessageLoop::current() == io_message_loop_);
275}
276
277// static
278HistogramSynchronizer* HistogramSynchronizer::histogram_synchronizer_ = NULL;
279