зеркало из https://github.com/mozilla/pjs.git
Redo the way that collectors are created and destroyed to be more stable and extensible (bug 335120). Collectors are now instantiated by contract id and held onto by the metrics service directly. r=darin
This commit is contained in:
Родитель
cf3c10880d
Коммит
589c17408d
|
@ -45,7 +45,10 @@ include $(DEPTH)/config/autoconf.mk
|
|||
MODULE = metrics
|
||||
XPI_NAME = metrics
|
||||
|
||||
XPIDLSRCS = nsIMetricsService.idl
|
||||
XPIDLSRCS = \
|
||||
nsIMetricsService.idl \
|
||||
nsIMetricsCollector.idl \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS = \
|
||||
nsMetricsModule.h \
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Metrics extension.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Ryner <bryner@brianryner.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* The nsIMetricsCollector interface is implemented by any object that collects
|
||||
* data on behalf of the MetricsService. When the configuration file reequests
|
||||
* collector "foo" in namespace "http://www.mozilla.org/metrics",
|
||||
* the contract id
|
||||
* "@mozilla.org/metrics/collector;1?name=http://www.mozilla.org/metrics:foo"
|
||||
* is instantiated (using getSerivce). The collector is responsible for
|
||||
* calling nsIMetricsService::logEvent() when it has something to log.
|
||||
*/
|
||||
[scriptable, uuid(b6cba4ad-363a-4780-90fa-9d7cc8115854)]
|
||||
interface nsIMetricsCollector : nsISupports
|
||||
{
|
||||
/**
|
||||
* Notification that this collector is no longer enabled. The collector
|
||||
* should unregister itself from observer and event notifications so that
|
||||
* the object can be freed.
|
||||
*/
|
||||
void onDetach();
|
||||
|
||||
/**
|
||||
* Notification that the MetricsService is starting a new event log.
|
||||
* This happens after any onDetach() notifications that result from parsing
|
||||
* the new configuration.
|
||||
*/
|
||||
void onNewLog();
|
||||
};
|
|
@ -39,6 +39,7 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPropertyBag;
|
||||
interface nsIDOMWindow;
|
||||
|
||||
/**
|
||||
* This file defines the interfaces for the Metrics Service.
|
||||
|
@ -129,7 +130,7 @@ interface nsIMetricsEventItem : nsISupports
|
|||
readonly attribute long childCount;
|
||||
};
|
||||
|
||||
[scriptable, uuid(ff320b73-ecd4-4e08-a09d-b37537770df6)]
|
||||
[scriptable, uuid(289cd0d3-00b4-4554-9e6d-71eded08d1d8)]
|
||||
interface nsIMetricsService : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -188,4 +189,26 @@ interface nsIMetricsService : nsISupports
|
|||
* re-enable log collection.
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* Gets a numeric window id corresponding to the given DOMWindow.
|
||||
* The id remains constant for as long as the window exists.
|
||||
*/
|
||||
unsigned long getWindowID(in nsIDOMWindow window);
|
||||
};
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* Observer notifications
|
||||
*/
|
||||
|
||||
/**
|
||||
* These work like NS[_CHROME]_WEBNAVIGATION_DESTROY, except that the
|
||||
* MetricsService is guaranteed to still know about the window which is being
|
||||
* destroyed (via getWindowID). Collectors should use these notifications
|
||||
* instead of the docshell-provided ones.
|
||||
*/
|
||||
#define NS_METRICS_WEBNAVIGATION_DESTROY "metrics-webnavigation-destroy"
|
||||
#define NS_METRICS_CHROME_WEBNAVIGATION_DESTROY \
|
||||
"metrics-chrome-webnavigation-destroy"
|
||||
%}
|
||||
|
|
|
@ -184,9 +184,15 @@ static PRBool GetMemUsage(MemUsage *result)
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static nsLoadCollector *sLoadCollector = nsnull;
|
||||
nsLoadCollector::nsLoadCollector()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsLoadCollector,
|
||||
nsLoadCollector::~nsLoadCollector()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS3(nsLoadCollector, nsIMetricsCollector,
|
||||
nsIWebProgressListener, nsISupportsWeakReference)
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -345,43 +351,38 @@ nsLoadCollector::OnSecurityChange(nsIWebProgress *webProgress,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
nsLoadCollector::SetEnabled(PRBool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
if (!sLoadCollector) {
|
||||
sLoadCollector = new nsLoadCollector();
|
||||
NS_ENSURE_TRUE(sLoadCollector, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ADDREF(sLoadCollector);
|
||||
|
||||
nsresult rv = sLoadCollector->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
MS_LOG(("Failed to initialize the load collector"));
|
||||
NS_RELEASE(sLoadCollector);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NS_IF_RELEASE(sLoadCollector);
|
||||
GetMemUsage_Shutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLoadCollector::Init()
|
||||
{
|
||||
NS_ENSURE_TRUE(mRequestMap.Init(32), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Attach the LoadCollector as a global web progress listener
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIWebProgress> progress =
|
||||
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_STATE(progress);
|
||||
|
||||
rv = progress->AddProgressListener(this,
|
||||
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
nsresult rv = progress->AddProgressListener(
|
||||
this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoadCollector::OnDetach()
|
||||
{
|
||||
nsCOMPtr<nsIWebProgress> progress =
|
||||
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_STATE(progress);
|
||||
|
||||
nsresult rv = progress->RemoveProgressListener(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoadCollector::OnNewLog()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -62,33 +62,27 @@
|
|||
// window: The id of the window where the document loaded (uint16).
|
||||
// loadtime: The elapsed time for the load, in milliseconds (uint32).
|
||||
|
||||
#include "nsIMetricsCollector.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsLoadCollector : public nsIWebProgressListener,
|
||||
class nsLoadCollector : public nsIMetricsCollector,
|
||||
public nsIWebProgressListener,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMETRICSCOLLECTOR
|
||||
NS_DECL_NSIWEBPROGRESSLISTENER
|
||||
|
||||
// Enables or disables the load collector.
|
||||
// The collector should be shut down with SetEnabled(PR_FALSE)
|
||||
// when the metrics service is shut down.
|
||||
static nsresult SetEnabled(PRBool enabled);
|
||||
|
||||
protected:
|
||||
// Instances of this class should only be created by SetEnabled().
|
||||
nsLoadCollector() {}
|
||||
nsLoadCollector();
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
~nsLoadCollector() {}
|
||||
|
||||
// Object initialization, to be called by SetEnabled().
|
||||
nsresult Init();
|
||||
~nsLoadCollector();
|
||||
|
||||
struct RequestEntry {
|
||||
nsCOMPtr<nsIWritablePropertyBag2> properties;
|
||||
|
@ -99,4 +93,8 @@ class nsLoadCollector : public nsIWebProgressListener,
|
|||
nsDataHashtable<nsISupportsHashKey, RequestEntry> mRequestMap;
|
||||
};
|
||||
|
||||
#define NS_LOADCOLLECTOR_CLASSNAME "Load Collector"
|
||||
#define NS_LOADCOLLECTOR_CID \
|
||||
{ 0xa97357a0, 0xa2f3, 0x4b1f, {0x93, 0xd3, 0x36, 0xdc, 0xb7, 0xee, 0x24, 0x63}}
|
||||
|
||||
#endif // nsLoadCollector_h_
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "prprf.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#define NS_DEFAULT_UPLOAD_INTERVAL 3600 // 1 hour
|
||||
|
||||
|
@ -236,3 +237,16 @@ nsMetricsConfig::IsEventEnabled(const nsAString &eventNS,
|
|||
NS_ASSERTION(mEventSet.IsInitialized(), "nsMetricsConfig::Init not called");
|
||||
return mEventSet.GetEntry(MakeKey(eventNS, eventName)) != nsnull;
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator PR_CALLBACK
|
||||
nsMetricsConfig::CopyKey(nsStringHashKey *entry, void *userData)
|
||||
{
|
||||
NS_STATIC_CAST(nsTArray<nsString> *, userData)->
|
||||
AppendElement(entry->GetKey());
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsMetricsConfig::GetEvents(nsTArray<nsString> &events) {
|
||||
mEventSet.EnumerateEntries(CopyKey, &events);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
|
||||
class nsIDOMElement;
|
||||
class nsIFile;
|
||||
template<class E> class nsTArray;
|
||||
|
||||
class nsMetricsConfig
|
||||
{
|
||||
|
@ -73,6 +74,12 @@ public:
|
|||
PRBool IsEventEnabled(const nsAString &eventNS,
|
||||
const nsAString &eventName) const;
|
||||
|
||||
/**
|
||||
* Call this method to get a list of all events that are enabled.
|
||||
* The event names are prefixed with the namespace, separated by a colon.
|
||||
*/
|
||||
void GetEvents(nsTArray<nsString> &events);
|
||||
|
||||
/**
|
||||
* Get the limit on the number of events that should be collected.
|
||||
*/
|
||||
|
@ -109,6 +116,9 @@ private:
|
|||
void ProcessToplevelElement(nsIDOMElement *elem);
|
||||
void ProcessCollectorElement(nsIDOMElement *elem);
|
||||
|
||||
static PLDHashOperator PR_CALLBACK CopyKey(nsStringHashKey *key,
|
||||
void *userData);
|
||||
|
||||
nsTHashtable<nsStringHashKey> mEventSet;
|
||||
PRInt32 mEventLimit;
|
||||
PRInt32 mUploadInterval;
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
|
||||
#include "nsMetricsModule.h"
|
||||
#include "nsMetricsService.h"
|
||||
#include "nsLoadCollector.h"
|
||||
#include "nsWindowCollector.h"
|
||||
#include "nsProfileCollector.h"
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
@ -49,6 +52,9 @@
|
|||
|
||||
NS_DECL_CLASSINFO(nsMetricsService)
|
||||
|
||||
#define COLLECTOR_CONTRACTID(type) \
|
||||
"@mozilla.org/metrics/collector;1?name=" type ":" NS_METRICS_NAMESPACE
|
||||
|
||||
static NS_METHOD
|
||||
nsMetricsServiceRegisterSelf(nsIComponentManager *compMgr,
|
||||
nsIFile *path,
|
||||
|
@ -67,6 +73,10 @@ nsMetricsServiceRegisterSelf(nsIComponentManager *compMgr,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLoadCollector, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowCollector, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsProfileCollector)
|
||||
|
||||
static const nsModuleComponentInfo components[] = {
|
||||
{
|
||||
NS_METRICSSERVICE_CLASSNAME,
|
||||
|
@ -86,6 +96,24 @@ static const nsModuleComponentInfo components[] = {
|
|||
NS_METRICSSERVICE_CID,
|
||||
NS_ABOUT_MODULE_CONTRACTID_PREFIX "metrics",
|
||||
nsMetricsService::Create
|
||||
},
|
||||
{
|
||||
NS_LOADCOLLECTOR_CLASSNAME,
|
||||
NS_LOADCOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("document"),
|
||||
nsLoadCollectorConstructor
|
||||
},
|
||||
{
|
||||
NS_WINDOWCOLLECTOR_CLASSNAME,
|
||||
NS_WINDOWCOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("window"),
|
||||
nsWindowCollectorConstructor
|
||||
},
|
||||
{
|
||||
NS_PROFILECOLLECTOR_CLASSNAME,
|
||||
NS_PROFILECOLLECTOR_CID,
|
||||
COLLECTOR_CONTRACTID("profile"),
|
||||
nsProfileCollectorConstructor
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include "nsMetricsService.h"
|
||||
#include "nsMetricsEventItem.h"
|
||||
#include "nsIMetricsCollector.h"
|
||||
#include "nsStringUtils.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
@ -53,9 +54,6 @@
|
|||
#include "nsIPrefBranch.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsLoadCollector.h"
|
||||
#include "nsWindowCollector.h"
|
||||
#include "nsProfileCollector.h"
|
||||
#include "nsIPropertyBag.h"
|
||||
#include "nsIProperty.h"
|
||||
#include "nsIVariant.h"
|
||||
|
@ -84,6 +82,7 @@
|
|||
#include "nsIBadCertListener.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
// We need to suppress inclusion of nsString.h
|
||||
#define nsString_h___
|
||||
|
@ -282,9 +281,19 @@ nsMetricsService::nsMetricsService()
|
|||
sMetricsService = this;
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator PR_CALLBACK
|
||||
nsMetricsService::DetachCollector(const nsAString &key,
|
||||
nsIMetricsCollector *value, void *userData)
|
||||
{
|
||||
value->OnDetach();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsMetricsService::~nsMetricsService()
|
||||
{
|
||||
NS_ASSERTION(sMetricsService == this, ">1 MetricsService object created");
|
||||
|
||||
mCollectorMap.EnumerateRead(DetachCollector, nsnull);
|
||||
sMetricsService = nsnull;
|
||||
}
|
||||
|
||||
|
@ -660,22 +669,92 @@ nsMetricsService::OnStopRequest(nsIRequest *request, nsISupports *context,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
struct DisabledCollectorsClosure
|
||||
{
|
||||
DisabledCollectorsClosure(const nsTArray<nsString> &enabled)
|
||||
: enabledCollectors(enabled) { }
|
||||
|
||||
// Collectors which are enabled in the new config
|
||||
const nsTArray<nsString> &enabledCollectors;
|
||||
|
||||
// Collector instances which should no longer be enabled
|
||||
nsTArray< nsCOMPtr<nsIMetricsCollector> > disabledCollectors;
|
||||
};
|
||||
|
||||
/* static */ PLDHashOperator PR_CALLBACK
|
||||
nsMetricsService::PruneDisabledCollectors(const nsAString &key,
|
||||
nsCOMPtr<nsIMetricsCollector> &value,
|
||||
void *userData)
|
||||
{
|
||||
DisabledCollectorsClosure *dc =
|
||||
NS_STATIC_CAST(DisabledCollectorsClosure *, userData);
|
||||
|
||||
// The frozen string API doesn't expose operator==, so we can't use
|
||||
// IndexOf() here.
|
||||
for (PRUint32 i = 0; i < dc->enabledCollectors.Length(); ++i) {
|
||||
if (dc->enabledCollectors[i].Equals(key)) {
|
||||
// The collector is enabled, continue
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find the collector |key| in the list of enabled collectors,
|
||||
// so move it from the hash table to the disabledCollectors list.
|
||||
MS_LOG(("Disabling collector %s", NS_ConvertUTF16toUTF8(key).get()));
|
||||
dc->disabledCollectors.AppendElement(value);
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
/* static */ PLDHashOperator PR_CALLBACK
|
||||
nsMetricsService::NotifyNewLog(const nsAString &key,
|
||||
nsIMetricsCollector *value, void *userData)
|
||||
{
|
||||
value->OnNewLog();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMetricsService::EnableCollectors()
|
||||
{
|
||||
// Start and stop collectors based on the current config.
|
||||
nsresult rv;
|
||||
rv = nsProfileCollector::SetEnabled(
|
||||
IsEventEnabled(NS_LITERAL_STRING("profile")));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsTArray<nsString> enabledCollectors;
|
||||
mConfig.GetEvents(enabledCollectors);
|
||||
|
||||
rv = nsLoadCollector::SetEnabled(
|
||||
IsEventEnabled(NS_LITERAL_STRING("document")));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// We need to find two sets of collectors:
|
||||
// (1) collectors which are running but not in |collectors|.
|
||||
// We'll call onDetach() on them and let them be released.
|
||||
// (2) collectors which are in |collectors| but not running.
|
||||
// We need to instantiate these collectors.
|
||||
|
||||
rv = nsWindowCollector::SetEnabled(
|
||||
IsEventEnabled(NS_LITERAL_STRING("window")));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
DisabledCollectorsClosure dc(enabledCollectors);
|
||||
mCollectorMap.Enumerate(PruneDisabledCollectors, &dc);
|
||||
|
||||
// Notify this set of collectors that they're going away, and release them.
|
||||
PRUint32 i;
|
||||
for (i = 0; i < dc.disabledCollectors.Length(); ++i) {
|
||||
dc.disabledCollectors[i]->OnDetach();
|
||||
}
|
||||
dc.disabledCollectors.Clear();
|
||||
|
||||
// Now instantiate any newly-enabled collectors.
|
||||
for (i = 0; i < enabledCollectors.Length(); ++i) {
|
||||
const nsString &name = enabledCollectors[i];
|
||||
if (!mCollectorMap.GetWeak(name)) {
|
||||
nsCString contractID("@mozilla.org/metrics/collector;1?name=");
|
||||
contractID.Append(NS_ConvertUTF16toUTF8(name));
|
||||
|
||||
nsCOMPtr<nsIMetricsCollector> coll = do_GetService(contractID.get());
|
||||
if (coll) {
|
||||
MS_LOG(("Created collector %s", contractID.get()));
|
||||
mCollectorMap.Put(name, coll);
|
||||
} else {
|
||||
MS_LOG(("Couldn't instantiate collector %s", contractID.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, notify all collectors that we've restarted the log.
|
||||
mCollectorMap.EnumerateRead(NotifyNewLog, nsnull);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -719,21 +798,30 @@ nsMetricsService::Observe(nsISupports *subject, const char *topic,
|
|||
{
|
||||
if (strcmp(topic, kQuitApplicationTopic) == 0) {
|
||||
Flush();
|
||||
nsLoadCollector::SetEnabled(PR_FALSE);
|
||||
nsWindowCollector::SetEnabled(PR_FALSE);
|
||||
nsProfileCollector::SetEnabled(PR_FALSE);
|
||||
|
||||
// We don't detach the collectors here, to allow them to log events
|
||||
// as we're shutting down. The collectors will be detached and released
|
||||
// when the MetricsService goes away.
|
||||
} else if (strcmp(topic, "profile-after-change") == 0) {
|
||||
nsresult rv = ProfileStartup();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (strcmp(topic, NS_WEBNAVIGATION_DESTROY) == 0 ||
|
||||
strcmp(topic, NS_CHROME_WEBNAVIGATION_DESTROY) == 0) {
|
||||
// We handle dispatching to the window collector, if it's enabled,
|
||||
// to avoid having an observer ordering dependency.
|
||||
nsWindowCollector *wc = nsWindowCollector::GetInstance();
|
||||
if (wc) {
|
||||
wc->Observe(subject, topic, data);
|
||||
|
||||
// Dispatch our notification before removing the window from the map.
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
NS_ENSURE_STATE(obsSvc);
|
||||
|
||||
const char *newTopic;
|
||||
if (strcmp(topic, NS_WEBNAVIGATION_DESTROY) == 0) {
|
||||
newTopic = NS_METRICS_WEBNAVIGATION_DESTROY;
|
||||
} else {
|
||||
newTopic = NS_METRICS_CHROME_WEBNAVIGATION_DESTROY;
|
||||
}
|
||||
|
||||
obsSvc->NotifyObservers(subject, newTopic, data);
|
||||
|
||||
// Remove the window from our map.
|
||||
mWindowMap.Remove(subject);
|
||||
}
|
||||
|
@ -774,8 +862,9 @@ nsMetricsService::ProfileStartup()
|
|||
mCryptoHash = do_CreateInstance("@mozilla.org/security/hash;1");
|
||||
NS_ENSURE_TRUE(mCryptoHash, NS_ERROR_FAILURE);
|
||||
|
||||
// Set up the window id map
|
||||
// Set up our hashtables
|
||||
NS_ENSURE_TRUE(mWindowMap.Init(32), NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_TRUE(mCollectorMap.Init(16), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Create an XML document to serve as the owner document for elements.
|
||||
mDocument = do_CreateInstance("@mozilla.org/xml/xml-document;1");
|
||||
|
@ -789,11 +878,6 @@ nsMetricsService::ProfileStartup()
|
|||
rv = EnableCollectors();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsProfileCollector *pc = nsProfileCollector::GetInstance();
|
||||
if (pc) {
|
||||
pc->LogProfile();
|
||||
}
|
||||
|
||||
RegisterUploadTimer();
|
||||
|
||||
// If we didn't load a config, immediately upload our empty log.
|
||||
|
@ -1189,10 +1273,23 @@ nsMetricsService::GetWindowID(nsIDOMWindow *window)
|
|||
return PR_UINT32_MAX;
|
||||
}
|
||||
|
||||
return sMetricsService->GetWindowIDInternal(window);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMetricsService::GetWindowID(nsIDOMWindow *window, PRUint32 *id)
|
||||
{
|
||||
*id = GetWindowIDInternal(window);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsMetricsService::GetWindowIDInternal(nsIDOMWindow *window)
|
||||
{
|
||||
PRUint32 id;
|
||||
if (!sMetricsService->mWindowMap.Get(window, &id)) {
|
||||
id = sMetricsService->mNextWindowID++;
|
||||
sMetricsService->mWindowMap.Put(window, id);
|
||||
if (!mWindowMap.Get(window, &id)) {
|
||||
id = mNextWindowID++;
|
||||
mWindowMap.Put(window, id);
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
|
@ -52,12 +52,14 @@
|
|||
#include "prlog.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
|
||||
class nsILocalFile;
|
||||
class nsIDOMWindow;
|
||||
class nsIDOMDocument;
|
||||
class nsIDOMNode;
|
||||
class nsICryptoHash;
|
||||
class nsIMetricsCollector;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
// Shared log for the metrics service and collectors.
|
||||
|
@ -115,8 +117,7 @@ public:
|
|||
name, item);
|
||||
}
|
||||
|
||||
// Get the window id for the given DOMWindow. If a window id has not
|
||||
// yet been assigned for the window, the next id will be used.
|
||||
// More convenient non-scriptable version of GetWindowID().
|
||||
static PRUint32 GetWindowID(nsIDOMWindow *window);
|
||||
|
||||
// VC6 needs this to be public :-(
|
||||
|
@ -181,6 +182,22 @@ private:
|
|||
nsresult HashBytes(const PRUint8 *bytes, PRUint32 length,
|
||||
nsACString &result);
|
||||
|
||||
// Does the real work of GetWindowID().
|
||||
PRUint32 GetWindowIDInternal(nsIDOMWindow *window);
|
||||
|
||||
static PLDHashOperator PR_CALLBACK
|
||||
PruneDisabledCollectors(const nsAString &key,
|
||||
nsCOMPtr<nsIMetricsCollector> &value,
|
||||
void *userData);
|
||||
|
||||
static PLDHashOperator PR_CALLBACK
|
||||
DetachCollector(const nsAString &key,
|
||||
nsIMetricsCollector *value, void *userData);
|
||||
|
||||
static PLDHashOperator PR_CALLBACK
|
||||
NotifyNewLog(const nsAString &key,
|
||||
nsIMetricsCollector *value, void *userData);
|
||||
|
||||
private:
|
||||
class BadCertListener;
|
||||
|
||||
|
@ -204,6 +221,9 @@ private:
|
|||
// Window to incrementing-id map. The keys are nsIDOMWindow*.
|
||||
nsDataHashtable<nsVoidPtrHashKey, PRUint32> mWindowMap;
|
||||
|
||||
// All of the active observers, keyed by name.
|
||||
nsInterfaceHashtable<nsStringHashKey, nsIMetricsCollector> mCollectorMap;
|
||||
|
||||
PRInt32 mEventCount;
|
||||
PRInt32 mSuspendCount;
|
||||
PRBool mUploading;
|
||||
|
|
|
@ -100,26 +100,30 @@ class nsProfileCollector::ExtensionEnumerator
|
|||
nsCOMPtr<nsIRDFResource> mDisabledResource;
|
||||
};
|
||||
|
||||
nsProfileCollector *nsProfileCollector::sInstance = nsnull;
|
||||
|
||||
/* static */ nsresult
|
||||
nsProfileCollector::SetEnabled(PRBool enabled)
|
||||
nsProfileCollector::nsProfileCollector()
|
||||
: mLoggedProfile(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
nsProfileCollector::~nsProfileCollector()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsProfileCollector, nsIMetricsCollector)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsProfileCollector::OnDetach()
|
||||
{
|
||||
if (enabled) {
|
||||
if (!sInstance) {
|
||||
sInstance = new nsProfileCollector();
|
||||
NS_ENSURE_TRUE(sInstance, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
} else {
|
||||
delete sInstance;
|
||||
sInstance = nsnull;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsProfileCollector::LogProfile()
|
||||
NS_IMETHODIMP
|
||||
nsProfileCollector::OnNewLog()
|
||||
{
|
||||
if (mLoggedProfile) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsMetricsService *ms = nsMetricsService::get();
|
||||
|
||||
nsCOMPtr<nsIMetricsEventItem> profileItem;
|
||||
|
@ -138,6 +142,7 @@ nsProfileCollector::LogProfile()
|
|||
nsresult rv = ms->LogEvent(profileItem);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mLoggedProfile = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#ifndef nsProfileCollector_h_
|
||||
#define nsProfileCollector_h_
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsIMetricsCollector.h"
|
||||
|
||||
class nsISupports;
|
||||
class nsIMetricsEventItem;
|
||||
|
@ -50,27 +50,16 @@ class nsAString;
|
|||
// a profile event at application startup. The profile event logs
|
||||
// various information about the user's software and hardware configuration.
|
||||
|
||||
class nsProfileCollector
|
||||
class nsProfileCollector : public nsIMetricsCollector
|
||||
{
|
||||
public:
|
||||
// Enables or disables the profile collector.
|
||||
// The collector should be shut down with SetEnabled(PR_FALSE)
|
||||
// when the metrics service is shut down.
|
||||
static nsresult SetEnabled(PRBool enabled);
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMETRICSCOLLECTOR
|
||||
|
||||
// Returns the singleton nsProfileCollector object, or NULL
|
||||
// if the profile collector is not enabled.
|
||||
static nsProfileCollector *GetInstance() { return sInstance; }
|
||||
|
||||
// Called to log a profile event to the metrics service.
|
||||
nsresult LogProfile();
|
||||
|
||||
protected:
|
||||
// Instances of this class are only created from SetEnabled().
|
||||
nsProfileCollector() { }
|
||||
nsProfileCollector();
|
||||
|
||||
private:
|
||||
~nsProfileCollector() { }
|
||||
~nsProfileCollector();
|
||||
|
||||
// These methods each create a particular item and append it to the profile.
|
||||
nsresult LogCPU(nsIMetricsEventItem *profile);
|
||||
|
@ -84,8 +73,12 @@ class nsProfileCollector
|
|||
class PluginEnumerator;
|
||||
class ExtensionEnumerator;
|
||||
|
||||
// the profile collector singleton
|
||||
static nsProfileCollector *sInstance;
|
||||
// Track whether we've logged the profile yet this session.
|
||||
PRBool mLoggedProfile;
|
||||
};
|
||||
|
||||
#define NS_PROFILECOLLECTOR_CLASSNAME "Profile Collector"
|
||||
#define NS_PROFILECOLLECTOR_CID \
|
||||
{ 0x9d5d472d, 0x88c7, 0x4cb2, {0xa6, 0xfb, 0x1f, 0x8e, 0x4d, 0xb5, 0x7e, 0x7e}}
|
||||
|
||||
#endif // nsProfileCollector_h_
|
||||
|
|
|
@ -50,56 +50,24 @@
|
|||
#include "nsITimer.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
||||
nsWindowCollector *nsWindowCollector::sInstance = nsnull;
|
||||
|
||||
/* static */ nsresult
|
||||
nsWindowCollector::SetEnabled(PRBool enabled)
|
||||
nsWindowCollector::nsWindowCollector()
|
||||
{
|
||||
if (enabled) {
|
||||
if (!sInstance) {
|
||||
sInstance = new nsWindowCollector();
|
||||
NS_ENSURE_TRUE(sInstance, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ADDREF(sInstance);
|
||||
|
||||
nsresult rv = sInstance->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
MS_LOG(("Failed to initialize the window collector"));
|
||||
NS_RELEASE(sInstance);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We want to release our reference to sInstance so that it can
|
||||
// be destroyed. However, window destroy events can happen during xpcom
|
||||
// shutdown (after we've been notified), and we need to still be able to
|
||||
// access sInstance to log those correctly. So, this releases the
|
||||
// reference but keeps sInstance around until the destructor runs.
|
||||
|
||||
if (sInstance) {
|
||||
sInstance->Release();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsWindowCollector, nsIObserver)
|
||||
|
||||
nsWindowCollector::~nsWindowCollector()
|
||||
{
|
||||
NS_ASSERTION(sInstance == this, "two window collectors created?");
|
||||
sInstance = nsnull;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsWindowCollector, nsIMetricsCollector, nsIObserver)
|
||||
|
||||
nsresult
|
||||
nsWindowCollector::Init()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
do_GetService("@mozilla.org/observer-service;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
NS_ENSURE_STATE(obsSvc);
|
||||
|
||||
rv = obsSvc->AddObserver(this, NS_WEBNAVIGATION_CREATE, PR_FALSE);
|
||||
nsresult rv = obsSvc->AddObserver(this, NS_WEBNAVIGATION_CREATE, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->AddObserver(this, NS_CHROME_WEBNAVIGATION_CREATE, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -107,20 +75,60 @@ nsWindowCollector::Init()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->AddObserver(this, "domwindowclosed", PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We receive NS_WEBNAVIGATION_DESTROY and NS_CHROME_WEBNAVIGATION_DESTROY
|
||||
// directly from the MetricsService. This way, we avoid a dependency
|
||||
// on the order in which observers fire, which is not guaranteed.
|
||||
rv = obsSvc->AddObserver(this, NS_METRICS_WEBNAVIGATION_DESTROY, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->AddObserver(this,
|
||||
NS_METRICS_CHROME_WEBNAVIGATION_DESTROY, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowCollector::OnDetach()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
NS_ENSURE_STATE(obsSvc);
|
||||
|
||||
nsresult rv = obsSvc->RemoveObserver(this, NS_WEBNAVIGATION_CREATE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->RemoveObserver(this, NS_CHROME_WEBNAVIGATION_CREATE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->RemoveObserver(this, "domwindowopened");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->RemoveObserver(this, "domwindowclosed");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->RemoveObserver(this, NS_METRICS_WEBNAVIGATION_DESTROY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = obsSvc->RemoveObserver(this, NS_METRICS_CHROME_WEBNAVIGATION_DESTROY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowCollector::OnNewLog()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct WindowOpenClosure
|
||||
{
|
||||
WindowOpenClosure(nsISupports *subj, nsWindowCollector *coll)
|
||||
: subject(subj), collector(coll) { }
|
||||
|
||||
nsCOMPtr<nsISupports> subject;
|
||||
nsRefPtr<nsWindowCollector> collector;
|
||||
};
|
||||
|
||||
/* static */ void
|
||||
nsWindowCollector::WindowOpenCallback(nsITimer *timer, void *closure)
|
||||
{
|
||||
if (sInstance) {
|
||||
sInstance->LogWindowOpen(timer, NS_STATIC_CAST(nsISupports *, closure));
|
||||
}
|
||||
WindowOpenClosure *wc = NS_STATIC_CAST(WindowOpenClosure *, closure);
|
||||
wc->collector->LogWindowOpen(timer, wc->subject);
|
||||
|
||||
delete wc;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -129,9 +137,6 @@ nsWindowCollector::LogWindowOpen(nsITimer *timer, nsISupports *subject)
|
|||
mWindowOpenTimers.RemoveElement(timer);
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(subject);
|
||||
|
||||
// Balance the addref we did in Observe()
|
||||
NS_RELEASE(subject);
|
||||
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
@ -207,11 +212,11 @@ nsWindowCollector::Observe(nsISupports *subject,
|
|||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
NS_ENSURE_STATE(timer);
|
||||
|
||||
// Addref the window so it can't go away before our timer fires.
|
||||
NS_ADDREF(subject);
|
||||
WindowOpenClosure *wc = new WindowOpenClosure(subject, this);
|
||||
NS_ENSURE_TRUE(wc, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = timer->InitWithFuncCallback(nsWindowCollector::WindowOpenCallback,
|
||||
subject, 0, nsITimer::TYPE_ONE_SHOT);
|
||||
wc, 0, nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mWindowOpenTimers.AppendElement(timer);
|
||||
|
@ -219,8 +224,8 @@ nsWindowCollector::Observe(nsISupports *subject,
|
|||
// Log a window close event.
|
||||
action.Assign("close");
|
||||
window = do_QueryInterface(subject);
|
||||
} else if (strcmp(topic, NS_WEBNAVIGATION_DESTROY) == 0 ||
|
||||
strcmp(topic, NS_CHROME_WEBNAVIGATION_DESTROY) == 0) {
|
||||
} else if (strcmp(topic, NS_METRICS_WEBNAVIGATION_DESTROY) == 0 ||
|
||||
strcmp(topic, NS_METRICS_CHROME_WEBNAVIGATION_DESTROY) == 0) {
|
||||
// Log a window destroy event.
|
||||
action.Assign("destroy");
|
||||
window = do_GetInterface(subject);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#ifndef nsWindowCollector_h_
|
||||
#define nsWindowCollector_h_
|
||||
|
||||
#include "nsIMetricsCollector.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -78,40 +79,30 @@ class nsITimer;
|
|||
// Attributes:
|
||||
// windowid: The id of the destroyed window (uint16).
|
||||
|
||||
class nsWindowCollector : public nsIObserver
|
||||
class nsWindowCollector : public nsIMetricsCollector,
|
||||
public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMETRICSCOLLECTOR
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// Enables or disables the window collector.
|
||||
// The collector should be shut down with SetEnabled(PR_FALSE)
|
||||
// when the metrics service is shut down.
|
||||
static nsresult SetEnabled(PRBool enabled);
|
||||
|
||||
// Returns the singleton nsWindowCollector object, or NULL
|
||||
// if the window collector is not enabled.
|
||||
static nsWindowCollector *GetInstance() { return sInstance; }
|
||||
|
||||
protected:
|
||||
// Instances of this class should only be created by SetEnabled().
|
||||
nsWindowCollector() {}
|
||||
nsWindowCollector();
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
~nsWindowCollector();
|
||||
|
||||
// Object initialization, to be called by Startup()
|
||||
nsresult Init();
|
||||
|
||||
// timer callback
|
||||
static void WindowOpenCallback(nsITimer *timer, void *closure);
|
||||
void LogWindowOpen(nsITimer *timer, nsISupports *subject);
|
||||
|
||||
// the window collector singleton
|
||||
static nsWindowCollector *sInstance;
|
||||
|
||||
// timers that we're using for deferred window open events
|
||||
nsTArray< nsCOMPtr<nsITimer> > mWindowOpenTimers;
|
||||
};
|
||||
|
||||
#define NS_WINDOWCOLLECTOR_CLASSNAME "Window Collector"
|
||||
#define NS_WINDOWCOLLECTOR_CID \
|
||||
{ 0x56e37604, 0xd593, 0x47e4, {0x87, 0x1f, 0x76, 0x13, 0x64, 0x8e, 0x74, 0x2b}}
|
||||
|
||||
#endif // nsWindowCollector_h_
|
||||
|
|
Загрузка…
Ссылка в новой задаче