1// Copyright (c) 2010 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/threading/thread.h"
10#include "chrome/common/chrome_constants.h"
11#include "chrome/common/render_messages.h"
12#include "content/browser/browser_thread.h"
13#include "content/browser/renderer_host/render_process_host.h"
14
15using base::Time;
16using base::TimeDelta;
17using base::TimeTicks;
18
19// Negative numbers are never used as sequence numbers.  We explicitly pick a
20// negative number that is "so negative" that even when we add one (as is done
21// when we generated the next sequence number) that it will still be negative.
22// We have code that handles wrapping around on an overflow into negative
23// territory.
24static const int kNeverUsableSequenceNumber = -2;
25
26HistogramSynchronizer::HistogramSynchronizer()
27  : lock_(),
28    received_all_renderer_histograms_(&lock_),
29    callback_task_(NULL),
30    callback_thread_(NULL),
31    last_used_sequence_number_(kNeverUsableSequenceNumber),
32    async_sequence_number_(kNeverUsableSequenceNumber),
33    async_renderers_pending_(0),
34    synchronous_sequence_number_(kNeverUsableSequenceNumber),
35    synchronous_renderers_pending_(0) {
36  DCHECK(histogram_synchronizer_ == NULL);
37  histogram_synchronizer_ = this;
38}
39
40HistogramSynchronizer::~HistogramSynchronizer() {
41  // Just in case we have any pending tasks, clear them out.
42  SetCallbackTaskAndThread(NULL, NULL);
43  histogram_synchronizer_ = NULL;
44}
45
46// static
47HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() {
48  DCHECK(histogram_synchronizer_ != NULL);
49  return histogram_synchronizer_;
50}
51
52void HistogramSynchronizer::FetchRendererHistogramsSynchronously(
53    TimeDelta wait_time) {
54  NotifyAllRenderers(SYNCHRONOUS_HISTOGRAMS);
55
56  TimeTicks start = TimeTicks::Now();
57  TimeTicks end_time = start + wait_time;
58  int unresponsive_renderer_count;
59  {
60    base::AutoLock auto_lock(lock_);
61    while (synchronous_renderers_pending_ > 0 && TimeTicks::Now() < end_time) {
62      wait_time = end_time - TimeTicks::Now();
63      received_all_renderer_histograms_.TimedWait(wait_time);
64    }
65    unresponsive_renderer_count = synchronous_renderers_pending_;
66    synchronous_renderers_pending_ = 0;
67    synchronous_sequence_number_ = kNeverUsableSequenceNumber;
68  }
69  UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingSynchronous",
70                       unresponsive_renderer_count);
71  if (!unresponsive_renderer_count)
72    UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsSynchronously",
73                        TimeTicks::Now() - start);
74}
75
76// static
77void HistogramSynchronizer::FetchRendererHistogramsAsynchronously(
78    MessageLoop* callback_thread,
79    Task* callback_task,
80    int wait_time) {
81  DCHECK(callback_thread != NULL);
82  DCHECK(callback_task != NULL);
83
84  HistogramSynchronizer* current_synchronizer = CurrentSynchronizer();
85
86  if (current_synchronizer == NULL) {
87    // System teardown is happening.
88    callback_thread->PostTask(FROM_HERE, callback_task);
89    return;
90  }
91
92  current_synchronizer->SetCallbackTaskAndThread(callback_thread,
93                                                 callback_task);
94
95  int sequence_number =
96      current_synchronizer->NotifyAllRenderers(ASYNC_HISTOGRAMS);
97
98  // Post a task that would be called after waiting for wait_time.  This acts
99  // as a watchdog, to ensure that a non-responsive renderer won't block us from
100  // making the callback.
101  BrowserThread::PostDelayedTask(
102      BrowserThread::UI, FROM_HERE,
103      NewRunnableMethod(
104          current_synchronizer,
105          &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
106          sequence_number),
107      wait_time);
108}
109
110// static
111void HistogramSynchronizer::DeserializeHistogramList(
112    int sequence_number,
113    const std::vector<std::string>& histograms) {
114  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
115  for (std::vector<std::string>::const_iterator it = histograms.begin();
116       it < histograms.end();
117       ++it) {
118    base::Histogram::DeserializeHistogramInfo(*it);
119  }
120
121  HistogramSynchronizer* current_synchronizer = CurrentSynchronizer();
122  if (current_synchronizer == NULL)
123    return;
124
125  // Record that we have received a histogram from renderer process.
126  current_synchronizer->DecrementPendingRenderers(sequence_number);
127}
128
129int HistogramSynchronizer::NotifyAllRenderers(
130    RendererHistogramRequester requester) {
131  // To iterate over RenderProcessHosts, or to send messages to the hosts, we
132  // need to be on the UI thread.
133  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134
135  int notification_count = 0;
136  for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
137       !it.IsAtEnd(); it.Advance())
138     ++notification_count;
139
140  int sequence_number = GetNextAvailableSequenceNumber(requester,
141                                                       notification_count);
142  for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
143       !it.IsAtEnd(); it.Advance()) {
144    if (!it.GetCurrentValue()->Send(
145        new ViewMsg_GetRendererHistograms(sequence_number)))
146      DecrementPendingRenderers(sequence_number);
147  }
148
149  return sequence_number;
150}
151
152void HistogramSynchronizer::DecrementPendingRenderers(int sequence_number) {
153  bool synchronous_completed = false;
154  bool asynchronous_completed = false;
155
156  {
157    base::AutoLock auto_lock(lock_);
158    if (sequence_number == async_sequence_number_) {
159      if (--async_renderers_pending_ <= 0)
160        asynchronous_completed = true;
161    } else if (sequence_number == synchronous_sequence_number_) {
162      if (--synchronous_renderers_pending_ <= 0)
163        synchronous_completed = true;
164    }
165  }
166
167  if (asynchronous_completed)
168    ForceHistogramSynchronizationDoneCallback(sequence_number);
169  else if (synchronous_completed)
170    received_all_renderer_histograms_.Signal();
171}
172
173void HistogramSynchronizer::SetCallbackTaskAndThread(
174    MessageLoop* callback_thread,
175    Task* callback_task) {
176  Task* old_task = NULL;
177  MessageLoop* old_thread = NULL;
178  TimeTicks old_start_time;
179  int unresponsive_renderers;
180  const TimeTicks now = TimeTicks::Now();
181  {
182    base::AutoLock auto_lock(lock_);
183    old_task = callback_task_;
184    callback_task_ = callback_task;
185    old_thread = callback_thread_;
186    callback_thread_ = callback_thread;
187    unresponsive_renderers = async_renderers_pending_;
188    old_start_time = async_callback_start_time_;
189    async_callback_start_time_ = now;
190    // Prevent premature calling of our new callbacks.
191    async_sequence_number_ = kNeverUsableSequenceNumber;
192  }
193  // Just in case there was a task pending....
194  InternalPostTask(old_thread, old_task, unresponsive_renderers,
195                   old_start_time);
196}
197
198void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
199    int sequence_number) {
200  Task* task = NULL;
201  MessageLoop* thread = NULL;
202  TimeTicks started;
203  int unresponsive_renderers;
204  {
205    base::AutoLock lock(lock_);
206    if (sequence_number != async_sequence_number_)
207      return;
208    task = callback_task_;
209    thread = callback_thread_;
210    callback_task_ = NULL;
211    callback_thread_ = NULL;
212    started = async_callback_start_time_;
213    unresponsive_renderers = async_renderers_pending_;
214  }
215  InternalPostTask(thread, task, unresponsive_renderers, started);
216}
217
218void HistogramSynchronizer::InternalPostTask(MessageLoop* thread, Task* task,
219                                             int unresponsive_renderers,
220                                             const base::TimeTicks& started) {
221  if (!task || !thread)
222    return;
223  UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous",
224                       unresponsive_renderers);
225  if (!unresponsive_renderers) {
226    UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously",
227                        TimeTicks::Now() - started);
228  }
229
230  thread->PostTask(FROM_HERE, task);
231}
232
233int HistogramSynchronizer::GetNextAvailableSequenceNumber(
234    RendererHistogramRequester requester,
235    int renderer_count) {
236  base::AutoLock auto_lock(lock_);
237  ++last_used_sequence_number_;
238  // Watch out for wrapping to a negative number.
239  if (last_used_sequence_number_ < 0) {
240    // Bypass the reserved number, which is used when a renderer spontaneously
241    // decides to send some histogram data.
242    last_used_sequence_number_ =
243        chrome::kHistogramSynchronizerReservedSequenceNumber + 1;
244  }
245  DCHECK_NE(last_used_sequence_number_,
246            chrome::kHistogramSynchronizerReservedSequenceNumber);
247  if (requester == ASYNC_HISTOGRAMS) {
248    async_sequence_number_ = last_used_sequence_number_;
249    async_renderers_pending_ = renderer_count;
250  } else if (requester == SYNCHRONOUS_HISTOGRAMS) {
251    synchronous_sequence_number_ = last_used_sequence_number_;
252    synchronous_renderers_pending_ = renderer_count;
253  }
254  return last_used_sequence_number_;
255}
256
257// static
258HistogramSynchronizer* HistogramSynchronizer::histogram_synchronizer_ = NULL;
259