Commits

Brian Burg committed 65dbdc4

UPDATE: xpcom-nsIProbeService
toolkit/components/probes/ProbeService.cpp | 438 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 438 insertions(+), 0 deletions(-)

  • Participants
  • Parent commits 87fd3be

Comments (0)

Files changed (1)

File xpcom-nsIProbeService

 # Parent 8bc1f38580b72db139376d50b8b62fe62d7ba34a
 [mq]: xpcom-probes
 
-diff --git a/js/src/jsprobemgr.cpp b/js/src/jsprobemgr.cpp
---- a/js/src/jsprobemgr.cpp
-+++ b/js/src/jsprobemgr.cpp
-@@ -246,7 +246,6 @@
- {
-     JS_ASSERT(item->isSealed());
-     items.push_back(item);
--    totalBytes += ProbeTypeData[item->leafType].bytes;
- }
- 
- void
-@@ -1313,6 +1312,12 @@
-         jsval curObj = argv[offset];
-         uintN transformCount = (*it)->transforms.size();
- 
-+        /* corner case: there are no transforms (i.e., we are unpacking an actual arg to the probe) */
-+        if (transformCount == 0) {
-+            argv[offset] = unpackedArgs[(*it)->payloadOffset];
-+            continue;
-+        }
-+
-         for (uintN i = 0; i < transformCount; i++) {
-             ProbeTransform curTransform = (*it)->transforms[i];
-             const char *nextPropName = ProbeFunctionData[curTransform].api_name;
-@@ -1579,8 +1584,12 @@
-         /* if need new argument, pull it off the va_list */
-         else if (item->argn != lastArgn) {
-             JS_ASSERT(item->argn > lastArgn); /* should catch misorderings */
--            lastArgn = item->argn;
--            void *curArgp = va_arg(argp, void*);
-+            void *curArgp;
-+
-+            while (item->argn != lastArgn) {
-+                curArgp = va_arg(argp, void*);
-+                lastArgn++;
-+            }
-             curArg = ProbeValue(ty, &curArgp);
-         }
- 
 diff --git a/toolkit/components/Makefile.in b/toolkit/components/Makefile.in
 --- a/toolkit/components/Makefile.in
 +++ b/toolkit/components/Makefile.in
 +include $(topsrcdir)/config/config.mk
 +include $(topsrcdir)/config/rules.mk
 +include $(topsrcdir)/ipc/chromium/chromium-config.mk
+diff --git a/toolkit/components/probes/ProbeService.cpp b/toolkit/components/probes/ProbeService.cpp
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/probes/ProbeService.cpp
+@@ -0,0 +1,438 @@
++/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
++ * vim: set ts=8 sw=4 et tw=99 ft=cpp:
++ *
++ * ***** 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 mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ *   the Mozilla Corporation.
++ *
++ * Contributor(s):
++ *   Brian J. Burg <burg@cs.washington.edu> (Original Author)
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either of 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 "ProbeService.h"
++
++#include "nsIProbeService.h"
++#include "nsIObserver.h"
++#include "nsIObserverService.h"
++#include "nsIScriptContext.h"
++#include "nsITimer.h"
++
++#include "jsprobemgr.h"
++#include "mozilla/ModuleUtils.h"
++#include "nsCOMPtr.h"
++#include "nsContentUtils.h"
++#include "nsDOMJSUtils.h"
++#include "nsInterfaceHashtable.h"
++#include "nsServiceManagerUtils.h"
++#include "nsThreadUtils.h"
++#include "prlog.h"
++
++
++#if PR_LOGGING
++PRLogModuleInfo *gProbeServiceLog = nsnull;
++#endif
++#define LOG(_args) PR_LOG(gProbeServiceLog, PR_LOG_DEBUG, _args)
++
++namespace mozilla {
++
++class ProbeServiceImpl : public nsIProbeService
++                       , public nsIObserver
++{
++    NS_DECL_ISUPPORTS
++    NS_DECL_NSIPROBESERVICE
++    NS_DECL_NSIOBSERVER
++
++public:
++    ProbeServiceImpl();
++    ~ProbeServiceImpl();
++
++    nsresult Init();
++    /* called to actually reclaim members. */
++    nsresult Cleanup();
++
++    static already_AddRefed<nsIProbeService> CreateInstance();
++    /* called by the service manager to remove refs, trigger destructor */
++    static void Shutdown();
++
++private:
++    typedef nsInterfaceHashtable<nsUint32HashKey, nsIProbeMessageListener> ListenerMap;
++    typedef nsInterfaceHashtable<nsUint32HashKey, nsIScriptContext> ContextMap;
++
++    bool mHasShutdown;
++    nsCOMPtr<nsITimer> mDrainTimer;
++    js::probes::ScriptCookie mPreviousScript;
++    ListenerMap mListeners;
++    ContextMap mListenerContexts;
++    js::probes::ProbeManager *mManager;
++
++    static ProbeServiceImpl *sProbeService;
++
++    nsresult DispatchMessage(js::probes::Message *msg);
++    nsresult DrainMessages();
++    nsresult StartDrainTimer(PRInt32 inverval);
++
++};
++
++ProbeServiceImpl* ProbeServiceImpl::sProbeService = NULL;
++
++ProbeServiceImpl::ProbeServiceImpl()
++    : mHasShutdown(false)
++    , mPreviousScript(-1)
++    , mManager(NULL)
++{
++
++#if PR_LOGGING
++    if (!gProbeServiceLog) {
++        gProbeServiceLog = PR_NewLogModule("ProbeServiceImpl");
++    }
++#endif
++
++    LOG(("ProbeServiceImpl::ProbeServiceImpl"));
++}
++
++nsresult
++ProbeServiceImpl::Init()
++{
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++
++    LOG(("ProbeServiceImpl::Init -- initializing service..."));
++
++    nsresult rv;
++    nsCOMPtr<nsIObserverService> obs =
++        do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
++    NS_ENSURE_SUCCESS(rv, rv);
++
++    rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
++    NS_ENSURE_SUCCESS(rv, rv);
++
++    LOG(("ProbeServiceImpl -- Creating timer instance"));
++    mDrainTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
++    if (NS_FAILED(rv))
++        return rv;
++
++    mListeners.Init(5);
++    mListenerContexts.Init(5);
++    mManager = js::probes::ProbeManager::create();
++    mManager->start();
++
++    LOG(("ProbeServiceImpl::Init -- initialized!"));
++
++    return NS_OK;
++}
++
++ProbeServiceImpl::~ProbeServiceImpl()
++{
++    Cleanup();
++    LOG(("ProbeServiceImpl::~ProbeServiceImpl"));
++}
++
++already_AddRefed<nsIProbeService>
++ProbeServiceImpl::CreateInstance()
++{
++    NS_ABORT_IF_FALSE(sProbeService == NULL, "ProbeServiceImpl::CreateInstance may only be called once, via GetService()");
++    sProbeService = new ProbeServiceImpl();
++    sProbeService->Init();
++    /* AddRef each for static reference, and the caller. */
++    NS_ADDREF(sProbeService);
++    NS_ADDREF(sProbeService);
++
++    LOG(("ProbeServiceImpl::CreateInstance"));
++
++    return sProbeService;
++}
++
++nsresult
++ProbeServiceImpl::Cleanup()
++{
++    LOG(("ProbeServiceImpl::Cleanup -- starting cleanup..."));
++
++    /* reference for this section: dom/src/threads/nsIDOMThreadService.cpp */
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++
++/* TODO: kill timer */
++    LOG(("ProbeServiceImpl -- Cancelling Timer"));
++    mDrainTimer->Cancel();
++/* TODO: drain any remaining Messages synchronously */
++    mListeners.Clear();
++    mListenerContexts.Clear();
++    
++    nsresult rv;
++    nsCOMPtr<nsIObserverService> obs =
++        do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
++    NS_ENSURE_SUCCESS(rv, rv);
++    rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
++    NS_ENSURE_SUCCESS(rv, rv);
++
++    LOG(("ProbeServiceImpl::Cleanup -- stopping ProbeManager..."));
++    /* disables probes and joins handler thread */
++    mManager->shutdown();
++    mManager = NULL;
++
++    LOG(("ProbeServiceImpl::Cleanup -- done!"));
++    return NS_OK;
++}
++
++void
++ProbeServiceImpl::Shutdown()
++{
++    LOG(("ProbeServiceImpl::Shutdown"));
++    NS_IF_RELEASE(sProbeService);
++}
++
++nsresult
++ProbeServiceImpl::DrainMessages()
++{
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++
++    LOG(("ProbeServiceImpl -- Draining messages..."));
++
++    nsresult rv;
++
++    while (mManager->hasMessages()) {
++        js::probes::Message *msg = mManager->popMessage();
++        rv = DispatchMessage(msg);
++        NS_ENSURE_SUCCESS(rv, rv);
++    }
++    /* check timer termination condition */
++    if (!mManager->hasPendingScripts()) {
++        LOG(("ProbeServiceImpl -- Cancelling Timer"));
++        mDrainTimer->Cancel();
++    }
++
++    return NS_OK;
++}
++
++nsresult
++ProbeServiceImpl::DispatchMessage(js::probes::Message *msg)
++{
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++    NS_ENSURE_ARG(msg);
++
++    LOG(("ProbeServiceImpl::DispatchMessage -- dispatching one message"));
++    using namespace js::probes;
++
++    ScriptCookie cookie = msg->getCookie();
++
++    /* lookup listener for this scriptcookie */
++    nsCOMPtr<nsIProbeMessageListener> listener = mListeners.Get(cookie);
++    nsCOMPtr<nsIScriptContext> scriptCx = mListenerContexts.Get(cookie);
++    JSContext *cx = (JSContext *) scriptCx->GetNativeContext();
++
++    /* unpack message, using cx */
++    ScriptCookie outCookie;
++    jsval outVal;
++
++    JSAutoRequest req(cx);
++    /* put the JSContext onto the context stack. */
++    nsCxPusher cxPusher;
++    /* is this right, or should it push a null context? */
++    cxPusher.Push(cx);
++
++    JS_AddValueRoot(cx, &outVal);
++    msg->unpack(cx, &outVal, &outCookie);
++
++    LOG(("ProbeServiceImpl::DispatchMessage -- calling listener"));
++    /* call nsIProbeMessageListener.onMessage(jsval) */
++    listener->OnMessage(outVal);
++
++    JS_RemoveValueRoot(cx, &outVal);
++    cxPusher.Pop();
++
++    /* if script is different from last (and not first), then
++       it's time to remove the old listener from the listener map */
++
++    if (mListeners.Count() > 1 && mPreviousScript != cookie) {
++        mListeners.Remove(mPreviousScript);
++        mListenerContexts.Remove(mPreviousScript);
++    }
++
++    mPreviousScript = cookie;
++
++    return NS_OK;
++}
++
++/* note that interval specifies milliseconds */
++nsresult
++ProbeServiceImpl::StartDrainTimer(PRInt32 interval)
++{
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++    NS_ASSERTION(mDrainTimer, "cannot use uninitialized timer");
++
++    LOG(("ProbeServiceImpl -- initialized/started timer"));
++    return mDrainTimer->Init(this, interval, nsITimer::TYPE_REPEATING_SLACK);
++}
++
++// nsIObserver
++
++NS_IMETHODIMP
++ProbeServiceImpl::Observe(nsISupports *subject, const char *topic,
++                          const PRUnichar *data)
++{
++  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++
++  if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
++      Cleanup();
++      return NS_OK;
++  }
++  else if (strcmp(topic, NS_TIMER_CALLBACK_TOPIC) == 0) {
++    nsresult rv = DrainMessages();
++    if (NS_FAILED(rv)) {
++        LOG(("ProbeServiceImpl -- Cancelling Timer"));
++        mDrainTimer->Cancel();
++    }
++  }
++  return NS_OK;
++}
++
++
++/*  PRInt32 addHandler(in PRInt32 probe, in AString dataSpec, in AString handler); */
++
++NS_IMETHODIMP
++ProbeServiceImpl::AddHandler(PRInt32 aProbe, const nsAString &aDataSpec, const nsAString &aHandler, PRInt32 *_rval)
++{
++    using namespace js::probes;
++
++    LOG(("ProbeServiceImpl::AddHandler"));
++
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++    NS_ASSERTION(mManager, "Manager is missing!");
++   
++    if (aDataSpec.IsEmpty())
++        return NS_ERROR_INVALID_ARG;
++    if (aHandler.IsEmpty())
++        return NS_ERROR_INVALID_ARG;
++
++    /* get pointer to buffer contained in |code| */
++    nsAString::const_iterator specIt;
++    nsAString::const_iterator handlerIt;
++    aDataSpec.BeginReading(specIt);
++    aHandler.BeginReading(handlerIt);
++
++    ScriptDescriptor specDesc = { reinterpret_cast<const jschar *>(specIt.get()),
++                                  aDataSpec.Length(),
++                                  "dummy-spec-file", 1 };
++    ScriptDescriptor handlerDesc = { reinterpret_cast<const jschar *>(handlerIt.get()),
++                                     aHandler.Length(),
++                                     "dummy-handler-file", 1};
++    
++    HandlerCookie cookie;
++
++    if (!mManager->addProbeHandler((ProbePoint)aProbe, handlerDesc, specDesc, &cookie))
++      return NS_ERROR_FAILURE;
++
++    *_rval = cookie;
++    return NS_OK;
++}
++
++/* void removeHandler(in PRInt32 cookie); */
++
++NS_IMETHODIMP
++ProbeServiceImpl::RemoveHandler(PRInt32 cookie)
++{
++    LOG(("ProbeServiceImpl::RemoveHandler"));
++
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++    NS_ASSERTION(mManager, "Manager is missing!");
++
++    if (!mManager->removeProbeHandler(cookie))
++        return NS_ERROR_FAILURE;
++
++    return NS_OK;
++}
++
++/* void asyncQuery(in AString code, in nsIProbeMessageListener listener); */
++
++NS_IMETHODIMP
++ProbeServiceImpl::AsyncQuery(const nsAString &code, nsIProbeMessageListener *listener, JSContext *cx)
++{
++    using namespace js::probes;
++
++    LOG(("ProbeServiceImpl::AsyncQuery"));
++
++    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
++    NS_ENSURE_ARG(listener);
++    NS_ENSURE_ARG(cx);
++
++    if (code.IsEmpty())
++        return NS_ERROR_INVALID_ARG;
++
++    // get pointer to buffer contained in |code|
++    nsAString::const_iterator codeIter;
++    code.BeginReading(codeIter);
++
++    ScriptDescriptor codeDesc = { reinterpret_cast<const jschar *>(codeIter.get()),
++                                  code.Length(),
++                                  "dummy-async-query-file", 1 };
++    ScriptCookie codeCookie;
++
++    if (!mManager->runScriptOnce(codeDesc, &codeCookie))
++        return NS_ERROR_FAILURE;
++
++    nsIScriptContext *scriptCx = GetScriptContextFromJSContext(cx);
++    if (!scriptCx)
++        return NS_ERROR_FAILURE;
++
++    mListeners.Put(codeCookie, listener);
++    mListenerContexts.Put(codeCookie, scriptCx);
++    StartDrainTimer(1000);
++
++    return NS_OK;
++}
++
++NS_IMPL_THREADSAFE_ISUPPORTS1(ProbeServiceImpl, nsIProbeService)
++NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIProbeService, ProbeServiceImpl::CreateInstance)
++
++#define NS_PROBESERVICE_CID \
++{0x44ac0450, 0xeb4c, 0x4a4d, {0xa1, 0x9c, 0x93, 0x38, 0x91, 0x59, 0xa6, 0x27}}
++NS_DEFINE_NAMED_CID(NS_PROBESERVICE_CID);
++
++const Module::CIDEntry kProbeServiceCIDs[] = {
++  { &kNS_PROBESERVICE_CID, false, NULL, nsIProbeServiceConstructor },
++  { NULL }
++};
++
++const Module::ContractIDEntry kProbeServiceContracts[] = {
++  { "@mozilla.org/base/probes;1", &kNS_PROBESERVICE_CID },
++  { NULL }
++};
++
++const Module kProbeServiceModule = {
++  Module::kVersion,
++  kProbeServiceCIDs,
++  kProbeServiceContracts,
++  NULL,
++  NULL,
++  NULL,
++  ProbeServiceImpl::Shutdown
++};
++
++} // namespace mozilla
++
++NSMODULE_DEFN(nsProbesModule) = &mozilla::kProbeServiceModule;
 diff --git a/toolkit/components/probes/ProbeService.h b/toolkit/components/probes/ProbeService.h
 new file mode 100644
 --- /dev/null