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