Back to index

lightning-sunbird  0.9+nobinonly
nsMetricsService.h
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim:set ts=2 sw=2 sts=2 et cindent: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Metrics extension.
00017  *
00018  * The Initial Developer of the Original Code is Google Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Darin Fisher <darin@meer.net>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #ifndef nsMetricsService_h__
00040 #define nsMetricsService_h__
00041 
00042 #include "nsIMetricsService.h"
00043 #include "nsMetricsModule.h"
00044 #include "nsMetricsConfig.h"
00045 #include "nsIAboutModule.h"
00046 #include "nsIStreamListener.h"
00047 #include "nsIOutputStream.h"
00048 #include "nsIObserver.h"
00049 #include "nsITimer.h"
00050 #include "nsCOMPtr.h"
00051 #include "prio.h"
00052 #include "prlog.h"
00053 #include "nsIWritablePropertyBag2.h"
00054 #include "nsDataHashtable.h"
00055 #include "nsInterfaceHashtable.h"
00056 #include "nsPtrHashKey.h"
00057 #include "blapit.h"
00058 
00059 class nsILocalFile;
00060 class nsIDOMWindow;
00061 class nsIDOMDocument;
00062 class nsIDOMNode;
00063 class nsIMetricsCollector;
00064 class nsIDocShellTreeItem;
00065 
00066 #ifdef PR_LOGGING
00067 // Shared log for the metrics service and collectors.
00068 // (NSPR_LOG_MODULES=nsMetricsService:5)
00069 extern PRLogModuleInfo *gMetricsLog;
00070 #endif
00071 #define MS_LOG(args) PR_LOG(gMetricsLog, PR_LOG_DEBUG, args)
00072 #define MS_LOG_ENABLED() PR_LOG_TEST(gMetricsLog, PR_LOG_DEBUG)
00073 
00074 // This is the namespace for the built-in metrics events.
00075 #define NS_METRICS_NAMESPACE "http://www.mozilla.org/metrics"
00076 
00077 // This class manages the metrics datastore.  It is responsible for persisting
00078 // the data when the browser closes and for uploading it to the metrics server
00079 // periodically.
00080 
00081 class nsMetricsService : public nsIMetricsService
00082                        , public nsIAboutModule
00083                        , public nsIStreamListener
00084                        , public nsIObserver
00085                        , public nsITimerCallback
00086 {
00087 public:
00088   NS_DECL_ISUPPORTS
00089   NS_DECL_NSIMETRICSSERVICE
00090   NS_DECL_NSIABOUTMODULE
00091   NS_DECL_NSIREQUESTOBSERVER
00092   NS_DECL_NSISTREAMLISTENER
00093   NS_DECL_NSIOBSERVER
00094   NS_DECL_NSITIMERCALLBACK
00095   
00096   // Get the metrics service singleton.  This method will call do_GetService if
00097   // necessary to fetch the metrics service.  It relies on the service manager
00098   // to keep the singleton instance alive.  This method may return null!
00099   static nsMetricsService* get();
00100 
00101   // Create the metrics service singleton, called only by the XPCOM factory for
00102   // this class.
00103   static NS_METHOD Create(nsISupports *outer, const nsIID &iid, void **result);
00104 
00105   // Helper function for logging events in the default namespace
00106   nsresult LogEvent(const nsAString &eventName,
00107                     nsIWritablePropertyBag2 *eventProperties)
00108   {
00109     nsCOMPtr<nsIPropertyBag> bag = do_QueryInterface(eventProperties);
00110     NS_ENSURE_STATE(bag);
00111     return LogSimpleEvent(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), eventName,
00112                           bag);
00113   }
00114 
00115   // Creates an EventItem in the default metrics namespace.
00116   nsresult CreateEventItem(const nsAString &name, nsIMetricsEventItem **item)
00117   {
00118     return CreateEventItem(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
00119                            name, item);
00120   }
00121 
00122   // More convenient non-scriptable version of GetWindowID().
00123   static PRUint32 GetWindowID(nsIDOMWindow *window);
00124 
00125   // VC6 needs this to be public :-(
00126   nsresult Init();
00127 
00128   // Returns the window id map (readonly)
00129   const nsDataHashtable< nsPtrHashKey<nsIDOMWindow>, PRUint32 >&
00130   WindowMap() const
00131   {
00132     return mWindowMap;
00133   }
00134 
00135   // Creates a one-way hash of the given string.
00136   nsresult HashUTF8(const nsCString &str, nsCString &hashed);
00137 
00138   // Convenience method for hashing UTF-16 strings.
00139   // The string is converted to UTF-8, then HashUTF8() is called.
00140   nsresult HashUTF16(const nsString &str, nsCString &hashed) {
00141     return HashUTF8(NS_ConvertUTF16toUTF8(str), hashed);
00142   }
00143 
00144 private:
00145   nsMetricsService();
00146   ~nsMetricsService();
00147 
00148   // Post-profile-initialization startup code
00149   nsresult ProfileStartup();
00150 
00151   // Reads the config, starts a new session, and turns on collectors
00152   nsresult StartCollection();
00153 
00154   // Stops collectors and removes all metrics-related prefs and files
00155   nsresult StopCollection();
00156 
00157   // Starts and stops collectors based on the current configuration
00158   void EnableCollectors();
00159   
00160   // Creates a new root element to hold event nodes
00161   nsresult CreateRoot();
00162 
00163   nsresult UploadData();
00164   nsresult GetDataFile(nsCOMPtr<nsILocalFile> *result);
00165   nsresult OpenDataFile(PRUint32 flags, PRFileDesc **result);
00166   nsresult GetDataFileForUpload(nsCOMPtr<nsILocalFile> *result);
00167 
00168   // This method returns an input stream containing the complete XML for the
00169   // data to upload.
00170   nsresult OpenCompleteXMLStream(nsILocalFile *dataFile,
00171                                  nsIInputStream **result);
00172 
00173   // Initialize our timer for the next upload.
00174   // If immediate is true, the timer will be fired as soon as possible,
00175   // which is at the deferred-upload time if one exists, or immediately
00176   // if not.
00177   void InitUploadTimer(PRBool immediate);
00178 
00179   // A reference to the local file containing our current configuration
00180   void GetConfigFile(nsIFile **result);
00181 
00182   // A reference to the local file where we'll download the server response.
00183   // We don't replace the real config file until we know the new one is valid.
00184   void GetConfigTempFile(nsIFile **result);
00185 
00186   // Generate a new random client id string
00187   nsresult GenerateClientID(nsCString &clientID);
00188 
00189   // Check if a built-in event is enabled
00190   PRBool IsEventEnabled(const nsAString &event) const
00191   {
00192     return mConfig.IsEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE),
00193                                   event);
00194   }
00195 
00196   // Builds up a DOMElement tree from the given item and its children
00197   nsresult BuildEventItem(nsIMetricsEventItem *item,
00198                           nsIDOMElement **itemElement);
00199 
00200   // Called to persist mEventCount.  Returns "true" if succeeded.
00201   PRBool PersistEventCount();
00202 
00203   // Hashes a byte buffer of the given length
00204   nsresult HashBytes(const PRUint8 *bytes, PRUint32 length,
00205                      nsACString &result);
00206 
00207   // Does the real work of GetWindowID().
00208   PRUint32 GetWindowIDInternal(nsIDOMWindow *window);
00209 
00210   // Tries to load a new config.  If successful, the old config file is
00211   // replaced with the new one.  If the new config couldn't be loaded,
00212   // a config file is written which disables collection and preserves the
00213   // upload interval from the old config.  Returns true if the new config
00214   // file was loaded successfully.
00215   PRBool LoadNewConfig(nsIFile *newConfig, nsIFile *oldConfig);
00216 
00217   // Removes the existing data file (metrics.xml)
00218   void RemoveDataFile();
00219 
00220   // Generates a random interval, in seconds, between 12 and 36 hours.
00221   PRInt32 GetRandomUploadInterval();
00222 
00223   static PLDHashOperator PR_CALLBACK
00224   PruneDisabledCollectors(const nsAString &key,
00225                           nsCOMPtr<nsIMetricsCollector> &value,
00226                           void *userData);
00227 
00228   static PLDHashOperator PR_CALLBACK
00229   DetachCollector(const nsAString &key,
00230                   nsIMetricsCollector *value, void *userData);
00231 
00232   static PLDHashOperator PR_CALLBACK
00233   NotifyNewLog(const nsAString &key,
00234                nsIMetricsCollector *value, void *userData);
00235 
00236   // Helpers to set a pref and then flush the pref file to disk.
00237   static nsresult FlushIntPref(const char *prefName, PRInt32 prefValue);
00238   static nsresult FlushCharPref(const char *prefName, const char *prefValue);
00239   static nsresult FlushClearPref(const char *prefName);
00240 
00241   // Returns true if the pref to enable collection is set to true
00242   static PRBool CollectionEnabled();
00243 
00244 private:
00245   class BadCertListener;
00246 
00247   // Pointer to the metrics service singleton
00248   static nsMetricsService* sMetricsService;
00249 
00250   nsMetricsConfig mConfig;
00251 
00252   // This output stream is non-null when we are downloading the config file.
00253   nsCOMPtr<nsIOutputStream> mConfigOutputStream;
00254 
00255   // XML document containing events to be flushed.
00256   nsCOMPtr<nsIDOMDocument> mDocument;
00257 
00258   // Root element of the XML document
00259   nsCOMPtr<nsIDOMNode> mRoot;
00260 
00261   // MD5 hashing object for collectors to use
00262   MD5Context *mMD5Context;
00263 
00264   // Window to incrementing-id map.  The keys are nsIDOMWindow*.
00265   nsDataHashtable< nsPtrHashKey<nsIDOMWindow>, PRUint32 > mWindowMap;
00266 
00267   // All of the active observers, keyed by name.
00268   nsInterfaceHashtable<nsStringHashKey, nsIMetricsCollector> mCollectorMap;
00269 
00270   // Timer object used for uploads
00271   nsCOMPtr<nsITimer> mUploadTimer;
00272 
00273   // The max number of times we'll retry the upload before stopping
00274   // for several hours.
00275   static const PRUint32 kMaxRetries;
00276 
00277   // This is the logging format version that is sent with each upload.
00278   // The version should be incremented whenver a change is made that
00279   // affects the log output, including but not limited to:
00280   //  - adding or removing a collector
00281   //  - adding or removing a property
00282   //  - changing the meaning or interpretation of a property value
00283   static const PRUint32 kMetricsVersion;
00284 
00285   PRInt32 mEventCount;
00286   PRInt32 mSuspendCount;
00287   PRBool mUploading;
00288   nsString mSessionID;
00289   // the next window id to hand out
00290   PRUint32 mNextWindowID;
00291 
00292   // The number of times we've tried to upload
00293   PRUint32 mRetryCount;
00294 };
00295 
00296 class nsMetricsUtils
00297 {
00298 public:
00299   // Creates a new nsIWritablePropertyBag2 instance.
00300   static nsresult NewPropertyBag(nsIWritablePropertyBag2 **result);
00301 
00302   // Creates a new item with the given properties, and appends it to the parent
00303   static nsresult AddChildItem(nsIMetricsEventItem *parent,
00304                                const nsAString &childName,
00305                                nsIPropertyBag *childProperties);
00306 
00307   // Loops until the given number of random bytes have been returned
00308   // from the OS.  Returns true on success, or false if no random
00309   // bytes are available
00310   static PRBool GetRandomNoise(void *buf, PRSize size);
00311 
00312   // Creates a new element in the metrics namespace, using the given
00313   // ownerDocument and tag.
00314   static nsresult CreateElement(nsIDOMDocument *ownerDoc,
00315                                 const nsAString &tag, nsIDOMElement **element);
00316 
00317   // Returns true if the docshell should be considered a subframe.
00318   static PRBool IsSubframe(nsIDocShellTreeItem *docShell);
00319 
00320   // Finds the window id for the DOMWindow containing |node|.
00321   // Returns 0 if the window could not be found.
00322   static PRUint32 FindWindowForNode(nsIDOMNode *node);
00323 };
00324 
00325 #endif  // nsMetricsService_h__