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 #ifndef CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_ 6 #define CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_ 7 8 #include <string> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/callback.h" 13 #include "base/memory/singleton.h" 14 #include "base/synchronization/lock.h" 15 #include "base/time/time.h" 16 #include "content/browser/histogram_subscriber.h" 17 18 namespace base { 19 class MessageLoop; 20 } 21 22 namespace content { 23 24 // This class maintains state that is used to upload histogram data from the 25 // various child processes, into the browser process. Such transactions are 26 // usually instigated by the browser. In general, a child process will respond 27 // by gathering snapshots of all internal histograms, calculating what has 28 // changed since its last upload, and transmitting a pickled collection of 29 // deltas. 30 // 31 // There are actually two modes of update request. One is synchronous (and 32 // blocks the UI thread, waiting to populate an about:histograms tab) and the 33 // other is asynchronous, and used by the metrics services in preparation for a 34 // log upload. 35 // 36 // To assure that all the processes have responded, a counter is maintained to 37 // indicate the number of pending (not yet responsive) processes. To avoid 38 // confusion about a response (i.e., is the process responding to a current 39 // request for an update, or to an old request for an update) we tag each group 40 // of requests with a sequence number. When an update arrives we can ignore it 41 // (relative to the counter) if it does not relate to a current outstanding 42 // sequence number. 43 // 44 // There is one final mode of use, where a renderer spontaneously decides to 45 // transmit a collection of histogram data. This is designed for use when the 46 // renderer is terminating. Unfortunately, renders may be terminated without 47 // warning, and the best we can do is periodically acquire data from a tab, such 48 // as when a page load has completed. In this mode, the renderer uses a 49 // reserved sequence number, different from any sequence number that might be 50 // specified by a browser request. Since this sequence number can't match an 51 // outstanding sequence number, the pickled data is accepted into the browser, 52 // but there is no impact on the counters. 53 54 class HistogramSynchronizer : public HistogramSubscriber { 55 public: 56 enum ProcessHistogramRequester { 57 UNKNOWN, 58 ASYNC_HISTOGRAMS, 59 }; 60 61 // Return pointer to the singleton instance for the current process, or NULL 62 // if none. 63 static HistogramSynchronizer* GetInstance(); 64 65 // Contact all processes, and get them to upload to the browser any/all 66 // changes to histograms. This method is called from about:histograms. 67 static void FetchHistograms(); 68 69 // Contact all child processes, and get them to upload to the browser any/all 70 // changes to histograms. When all changes have been acquired, or when the 71 // wait time expires (whichever is sooner), post the callback to the 72 // specified message loop. Note the callback is posted exactly once. 73 static void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread, 74 const base::Closure& callback, 75 base::TimeDelta wait_time); 76 77 private: 78 friend struct DefaultSingletonTraits<HistogramSynchronizer>; 79 80 class RequestContext; 81 82 HistogramSynchronizer(); 83 virtual ~HistogramSynchronizer(); 84 85 // Establish a new sequence number, and use it to notify all processes 86 // (renderers, plugins, GPU, etc) of the need to supply, to the browser, 87 // any/all changes to their histograms. |wait_time| specifies the amount of 88 // time to wait before cancelling the requests for non-responsive processes. 89 void RegisterAndNotifyAllProcesses(ProcessHistogramRequester requester, 90 base::TimeDelta wait_time); 91 92 // ------------------------------------------------------- 93 // HistogramSubscriber methods for browser child processes 94 // ------------------------------------------------------- 95 96 // Update the number of pending processes for the given |sequence_number|. 97 // This is called on UI thread. 98 virtual void OnPendingProcesses(int sequence_number, 99 int pending_processes, 100 bool end) OVERRIDE; 101 102 // Send histogram_data back to caller and also record that we are waiting 103 // for one less histogram data from child process for the given sequence 104 // number. This method is accessible on UI thread. 105 virtual void OnHistogramDataCollected( 106 int sequence_number, 107 const std::vector<std::string>& pickled_histograms) OVERRIDE; 108 109 // Set the callback_thread_ and callback_ members. If these members already 110 // had values, then as a side effect, post the old callback_ to the old 111 // callaback_thread_. This side effect should not generally happen, but is in 112 // place to assure correctness (that any tasks that were set, are eventually 113 // called, and never merely discarded). 114 void SetCallbackTaskAndThread(base::MessageLoop* callback_thread, 115 const base::Closure& callback); 116 117 void ForceHistogramSynchronizationDoneCallback(int sequence_number); 118 119 // Internal helper function, to post task, and record callback stats. 120 void InternalPostTask(base::MessageLoop* thread, 121 const base::Closure& callback); 122 123 // Gets a new sequence number to be sent to processes from browser process. 124 int GetNextAvailableSequenceNumber(ProcessHistogramRequester requester); 125 126 // This lock_ protects access to all members. 127 base::Lock lock_; 128 129 // When a request is made to asynchronously update the histograms, we store 130 // the task and thread we use to post a completion notification in 131 // callback_ and callback_thread_. 132 base::Closure callback_; 133 base::MessageLoop* callback_thread_; 134 135 // We don't track the actual processes that are contacted for an update, only 136 // the count of the number of processes, and we can sometimes time-out and 137 // give up on a "slow to respond" process. We use a sequence_number to be 138 // sure a response from a process is associated with the current round of 139 // requests (and not merely a VERY belated prior response). 140 // All sequence numbers used are non-negative. 141 // last_used_sequence_number_ is the most recently used number (used to avoid 142 // reuse for a long time). 143 int last_used_sequence_number_; 144 145 // The sequence number used by the most recent asynchronous update request to 146 // contact all processes. 147 int async_sequence_number_; 148 149 DISALLOW_COPY_AND_ASSIGN(HistogramSynchronizer); 150 }; 151 152 } // namespace content 153 154 #endif // CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_ 155