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:
bryner%brianryner.com 2006-04-25 04:00:40 +00:00
Родитель cf3c10880d
Коммит 589c17408d
14 изменённых файлов: 438 добавлений и 184 удалений

Просмотреть файл

@ -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");
@ -788,11 +877,6 @@ nsMetricsService::ProfileStartup()
// Start up the collectors
rv = EnableCollectors();
NS_ENSURE_SUCCESS(rv, rv);
nsProfileCollector *pc = nsProfileCollector::GetInstance();
if (pc) {
pc->LogProfile();
}
RegisterUploadTimer();
@ -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_