// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_ #define CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_ #include <string> #include <vector> #include "base/basictypes.h" #include "base/callback.h" #include "base/memory/singleton.h" #include "base/synchronization/lock.h" #include "base/time/time.h" #include "content/browser/histogram_subscriber.h" namespace base { class MessageLoop; } namespace content { // This class maintains state that is used to upload histogram data from the // various child processes, into the browser process. Such transactions are // usually instigated by the browser. In general, a child process will respond // by gathering snapshots of all internal histograms, calculating what has // changed since its last upload, and transmitting a pickled collection of // deltas. // // There are actually two modes of update request. One is synchronous (and // blocks the UI thread, waiting to populate an about:histograms tab) and the // other is asynchronous, and used by the metrics services in preparation for a // log upload. // // To assure that all the processes have responded, a counter is maintained to // indicate the number of pending (not yet responsive) processes. To avoid // confusion about a response (i.e., is the process responding to a current // request for an update, or to an old request for an update) we tag each group // of requests with a sequence number. When an update arrives we can ignore it // (relative to the counter) if it does not relate to a current outstanding // sequence number. // // There is one final mode of use, where a renderer spontaneously decides to // transmit a collection of histogram data. This is designed for use when the // renderer is terminating. Unfortunately, renders may be terminated without // warning, and the best we can do is periodically acquire data from a tab, such // as when a page load has completed. In this mode, the renderer uses a // reserved sequence number, different from any sequence number that might be // specified by a browser request. Since this sequence number can't match an // outstanding sequence number, the pickled data is accepted into the browser, // but there is no impact on the counters. class HistogramSynchronizer : public HistogramSubscriber { public: enum ProcessHistogramRequester { UNKNOWN, ASYNC_HISTOGRAMS, }; // Return pointer to the singleton instance for the current process, or NULL // if none. static HistogramSynchronizer* GetInstance(); // Contact all processes, and get them to upload to the browser any/all // changes to histograms. This method is called from about:histograms. static void FetchHistograms(); // Contact all child processes, and get them to upload to the browser any/all // changes to histograms. When all changes have been acquired, or when the // wait time expires (whichever is sooner), post the callback to the // specified message loop. Note the callback is posted exactly once. static void FetchHistogramsAsynchronously(base::MessageLoop* callback_thread, const base::Closure& callback, base::TimeDelta wait_time); private: friend struct DefaultSingletonTraits<HistogramSynchronizer>; class RequestContext; HistogramSynchronizer(); virtual ~HistogramSynchronizer(); // Establish a new sequence number, and use it to notify all processes // (renderers, plugins, GPU, etc) of the need to supply, to the browser, // any/all changes to their histograms. |wait_time| specifies the amount of // time to wait before cancelling the requests for non-responsive processes. void RegisterAndNotifyAllProcesses(ProcessHistogramRequester requester, base::TimeDelta wait_time); // ------------------------------------------------------- // HistogramSubscriber methods for browser child processes // ------------------------------------------------------- // Update the number of pending processes for the given |sequence_number|. // This is called on UI thread. virtual void OnPendingProcesses(int sequence_number, int pending_processes, bool end) OVERRIDE; // Send histogram_data back to caller and also record that we are waiting // for one less histogram data from child process for the given sequence // number. This method is accessible on UI thread. virtual void OnHistogramDataCollected( int sequence_number, const std::vector<std::string>& pickled_histograms) OVERRIDE; // Set the callback_thread_ and callback_ members. If these members already // had values, then as a side effect, post the old callback_ to the old // callaback_thread_. This side effect should not generally happen, but is in // place to assure correctness (that any tasks that were set, are eventually // called, and never merely discarded). void SetCallbackTaskAndThread(base::MessageLoop* callback_thread, const base::Closure& callback); void ForceHistogramSynchronizationDoneCallback(int sequence_number); // Internal helper function, to post task, and record callback stats. void InternalPostTask(base::MessageLoop* thread, const base::Closure& callback); // Gets a new sequence number to be sent to processes from browser process. int GetNextAvailableSequenceNumber(ProcessHistogramRequester requester); // This lock_ protects access to all members. base::Lock lock_; // When a request is made to asynchronously update the histograms, we store // the task and thread we use to post a completion notification in // callback_ and callback_thread_. base::Closure callback_; base::MessageLoop* callback_thread_; // We don't track the actual processes that are contacted for an update, only // the count of the number of processes, and we can sometimes time-out and // give up on a "slow to respond" process. We use a sequence_number to be // sure a response from a process is associated with the current round of // requests (and not merely a VERY belated prior response). // All sequence numbers used are non-negative. // last_used_sequence_number_ is the most recently used number (used to avoid // reuse for a long time). int last_used_sequence_number_; // The sequence number used by the most recent asynchronous update request to // contact all processes. int async_sequence_number_; DISALLOW_COPY_AND_ASSIGN(HistogramSynchronizer); }; } // namespace content #endif // CONTENT_BROWSER_HISTOGRAM_SYNCHRONIZER_H_