LCOV - app.info - xpcom/base/nsConsoleService.cpp

LCOV - code coverage report
| Current view: | directory - xpcom/base - nsConsoleService.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 114 | 80 | 70.2 %
| Date: | 2012-04-21 | Functions: | 19 | 18 | 94.7 %

           1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
           2                 : /* ***** BEGIN LICENSE BLOCK *****
           3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           4                 :  *
           5                 :  * The contents of this file are subject to the Mozilla Public License Version
           6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           7                 :  * the License. You may obtain a copy of the License at
           8                 :  * http://www.mozilla.org/MPL/
           9                 :  *
          10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          12                 :  * for the specific language governing rights and limitations under the
          13                 :  * License.
          14                 :  *
          15                 :  * The Original Code is mozilla.org code.
          16                 :  *
          17                 :  * The Initial Developer of the Original Code is
          18                 :  * Netscape Communications Corporation.
          19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
          20                 :  * the Initial Developer. All Rights Reserved.
          21                 :  *
          22                 :  * Contributor(s):
          23                 :  *   Simon Bünzli <zeniko@gmail.com>
          24                 :  *
          25                 :  * Alternatively, the contents of this file may be used under the terms of
          26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          27                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          29                 :  * of those above. If you wish to allow use of your version of this file only
          30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          31                 :  * use your version of this file under the terms of the MPL, indicate your
          32                 :  * decision by deleting the provisions above and replace them with the notice
          33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          34                 :  * the provisions above, a recipient may use your version of this file under
          35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          36                 :  *
          37                 :  * ***** END LICENSE BLOCK ***** */
          38                 :
          39                 : /*
          40                 :  * Maintains a circular buffer of recent messages, and notifies
          41                 :  * listeners when new messages are logged.
          42                 :  */
          43                 :
          44                 : /* Threadsafe. */
          45                 :
          46                 : #include "nsMemory.h"
          47                 : #include "nsIServiceManager.h"
          48                 : #include "nsCOMArray.h"
          49                 : #include "nsThreadUtils.h"
          50                 :
          51                 : #include "nsConsoleService.h"
          52                 : #include "nsConsoleMessage.h"
          53                 : #include "nsIClassInfoImpl.h"
          54                 :
          55                 : #if defined(ANDROID)
          56                 : #include <android/log.h>
          57                 : #endif
          58                 :
          59                 : using namespace mozilla;
          60                 :
          61           32215 : NS_IMPL_THREADSAFE_ADDREF(nsConsoleService)
          62           33580 : NS_IMPL_THREADSAFE_RELEASE(nsConsoleService)
          63                 : NS_IMPL_CLASSINFO(nsConsoleService, NULL, nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, NS_CONSOLESERVICE_CID)
          64            8728 : NS_IMPL_QUERY_INTERFACE1_CI(nsConsoleService, nsIConsoleService)
          65             370 : NS_IMPL_CI_INTERFACE_GETTER1(nsConsoleService, nsIConsoleService)
          66                 :
          67            1365 : nsConsoleService::nsConsoleService()
          68                 :     : mMessages(nsnull)
          69                 :     , mCurrent(0)
          70                 :     , mFull(false)
          71                 :     , mDeliveringMessage(false)
          72            1365 :     , mLock("nsConsoleService.mLock")
          73                 : {
          74                 :     // XXX grab this from a pref!
          75                 :     // hm, but worry about circularity, bc we want to be able to report
          76                 :     // prefs errs...
          77            1365 :     mBufferSize = 250;
          78            1365 : }
          79                 :
          80            2730 : nsConsoleService::~nsConsoleService()
          81                 : {
          82            1365 :     PRUint32 i = 0;
          83           19529 :     while (i < mBufferSize && mMessages[i] != nsnull) {
          84           16799 :         NS_RELEASE(mMessages[i]);
          85           16799 :         i++;
          86                 :     }
          87                 :
          88            1365 :     if (mMessages)
          89            1365 :         nsMemory::Free(mMessages);
          90            1365 : }
          91                 :
          92                 : nsresult
          93            1365 : nsConsoleService::Init()
          94                 : {
          95                 :     mMessages = (nsIConsoleMessage **)
          96            1365 :         nsMemory::Alloc(mBufferSize * sizeof(nsIConsoleMessage *));
          97            1365 :     if (!mMessages)
          98               0 :         return NS_ERROR_OUT_OF_MEMORY;
          99                 :
         100                 :     // Array elements should be 0 initially for circular buffer algorithm.
         101            1365 :     memset(mMessages, 0, mBufferSize * sizeof(nsIConsoleMessage *));
         102                 :
         103            1365 :     mListeners.Init();
         104                 :
         105            1365 :     return NS_OK;
         106                 : }
         107                 :
         108                 : namespace {
         109                 :
         110                 : class LogMessageRunnable : public nsRunnable
         111           81484 : {
         112                 : public:
         113           20371 :     LogMessageRunnable(nsIConsoleMessage* message, nsConsoleService* service)
         114                 :         : mMessage(message)
         115           20371 :         , mService(service)
         116           20371 :     { }
         117                 :
         118               8 :     void AddListener(nsIConsoleListener* listener) {
         119               8 :         mListeners.AppendObject(listener);
         120               8 :     }
         121                 :
         122                 :     NS_DECL_NSIRUNNABLE
         123                 :
         124                 : private:
         125                 :     nsCOMPtr<nsIConsoleMessage> mMessage;
         126                 :     nsRefPtr<nsConsoleService> mService;
         127                 :     nsCOMArray<nsIConsoleListener> mListeners;
         128                 : };
         129                 :
         130                 : NS_IMETHODIMP
         131           20371 : LogMessageRunnable::Run()
         132                 : {
         133           20371 :     MOZ_ASSERT(NS_IsMainThread());
         134                 :
         135           20371 :     mService->SetIsDelivering();
         136                 :
         137           20379 :     for (PRInt32 i = 0; i < mListeners.Count(); ++i)
         138               8 :         mListeners[i]->Observe(mMessage);
         139                 :
         140           20371 :     mService->SetDoneDelivering();
         141                 :
         142           20371 :     return NS_OK;
         143                 : }
         144                 :
         145                 : PLDHashOperator
         146               8 : CollectCurrentListeners(nsISupports* aKey, nsIConsoleListener* aValue,
         147                 :                         void* closure)
         148                 : {
         149               8 :     LogMessageRunnable* r = static_cast<LogMessageRunnable*>(closure);
         150               8 :     r->AddListener(aValue);
         151               8 :     return PL_DHASH_NEXT;
         152                 : }
         153                 :
         154                 : } // anonymous namespace
         155                 :
         156                 : // nsIConsoleService methods
         157                 : NS_IMETHODIMP
         158           20371 : nsConsoleService::LogMessage(nsIConsoleMessage *message)
         159                 : {
         160           20371 :     if (message == nsnull)
         161               0 :         return NS_ERROR_INVALID_ARG;
         162                 :
         163           20371 :     if (NS_IsMainThread() && mDeliveringMessage) {
         164               0 :         NS_WARNING("Some console listener threw an error while inside itself. Discarding this message");
         165               0 :         return NS_ERROR_FAILURE;
         166                 :     }
         167                 :
         168           40742 :     nsRefPtr<LogMessageRunnable> r = new LogMessageRunnable(message, this);
         169                 :     nsIConsoleMessage *retiredMessage;
         170                 :
         171           20371 :     NS_ADDREF(message); // early, in case it's same as replaced below.
         172                 :
         173                 :     /*
         174                 :      * Lock while updating buffer, and while taking snapshot of
         175                 :      * listeners array.
         176                 :      */
         177                 :     {
         178           40742 :         MutexAutoLock lock(mLock);
         179                 :
         180                 : #if defined(ANDROID)
         181                 :         {
         182                 :             nsXPIDLString msg;
         183                 :             message->GetMessageMoz(getter_Copies(msg));
         184                 :             __android_log_print(ANDROID_LOG_ERROR, "GeckoConsole",
         185                 :                         "%s",
         186                 :                         NS_LossyConvertUTF16toASCII(msg).get());
         187                 :         }
         188                 : #endif
         189                 :
         190                 :         /*
         191                 :          * If there's already a message in the slot we're about to replace,
         192                 :          * we've wrapped around, and we need to release the old message.  We
         193                 :          * save a pointer to it, so we can release below outside the lock.
         194                 :          */
         195           20371 :         retiredMessage = mMessages[mCurrent];
         196                 :
         197           20371 :         mMessages[mCurrent++] = message;
         198           20371 :         if (mCurrent == mBufferSize) {
         199              24 :             mCurrent = 0; // wrap around.
         200              24 :             mFull = true;
         201                 :         }
         202                 :
         203                 :         /*
         204                 :          * Copy the listeners into the snapshot array - in case a listener
         205                 :          * is removed during an Observe(...) notification...
         206                 :          */
         207           20371 :         mListeners.EnumerateRead(CollectCurrentListeners, r);
         208                 :     }
         209           20371 :     if (retiredMessage != nsnull)
         210            3472 :         NS_RELEASE(retiredMessage);
         211                 :
         212           20371 :     NS_DispatchToMainThread(r);
         213                 :
         214           20371 :     return NS_OK;
         215                 : }
         216                 :
         217                 : NS_IMETHODIMP
         218           17329 : nsConsoleService::LogStringMessage(const PRUnichar *message)
         219                 : {
         220           17329 :     nsConsoleMessage *msg = new nsConsoleMessage(message);
         221           17329 :     return this->LogMessage(msg);
         222                 : }
         223                 :
         224                 : NS_IMETHODIMP
         225               0 : nsConsoleService::GetMessageArray(nsIConsoleMessage ***messages, PRUint32 *count)
         226                 : {
         227                 :     nsIConsoleMessage **messageArray;
         228                 :
         229                 :     /*
         230                 :      * Lock the whole method, as we don't want anyone mucking with mCurrent or
         231                 :      * mFull while we're copying out the buffer.
         232                 :      */
         233               0 :     MutexAutoLock lock(mLock);
         234                 :
         235               0 :     if (mCurrent == 0 && !mFull) {
         236                 :         /*
         237                 :          * Make a 1-length output array so that nobody gets confused,
         238                 :          * and return a count of 0.  This should result in a 0-length
         239                 :          * array object when called from script.
         240                 :          */
         241                 :         messageArray = (nsIConsoleMessage **)
         242               0 :             nsMemory::Alloc(sizeof (nsIConsoleMessage *));
         243               0 :         *messageArray = nsnull;
         244               0 :         *messages = messageArray;
         245               0 :         *count = 0;
         246                 :
         247               0 :         return NS_OK;
         248                 :     }
         249                 :
         250               0 :     PRUint32 resultSize = mFull ? mBufferSize : mCurrent;
         251                 :     messageArray =
         252                 :         (nsIConsoleMessage **)nsMemory::Alloc((sizeof (nsIConsoleMessage *))
         253               0 :                                               * resultSize);
         254                 :
         255               0 :     if (messageArray == nsnull) {
         256               0 :         *messages = nsnull;
         257               0 :         *count = 0;
         258               0 :         return NS_ERROR_FAILURE;
         259                 :     }
         260                 :
         261                 :     PRUint32 i;
         262               0 :     if (mFull) {
         263               0 :         for (i = 0; i < mBufferSize; i++) {
         264                 :             // if full, fill the buffer starting from mCurrent (which'll be
         265                 :             // oldest) wrapping around the buffer to the most recent.
         266               0 :             messageArray[i] = mMessages[(mCurrent + i) % mBufferSize];
         267               0 :             NS_ADDREF(messageArray[i]);
         268                 :         }
         269                 :     } else {
         270               0 :         for (i = 0; i < mCurrent; i++) {
         271               0 :             messageArray[i] = mMessages[i];
         272               0 :             NS_ADDREF(messageArray[i]);
         273                 :         }
         274                 :     }
         275               0 :     *count = resultSize;
         276               0 :     *messages = messageArray;
         277                 :
         278               0 :     return NS_OK;
         279                 : }
         280                 :
         281                 : NS_IMETHODIMP
         282              10 : nsConsoleService::RegisterListener(nsIConsoleListener *listener)
         283                 : {
         284              10 :     if (!NS_IsMainThread()) {
         285               0 :         NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
         286               0 :         return NS_ERROR_NOT_SAME_THREAD;
         287                 :     }
         288                 :
         289              20 :     nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
         290                 :
         291              20 :     MutexAutoLock lock(mLock);
         292              10 :     if (mListeners.GetWeak(canonical)) {
         293                 :         // Reregistering a listener isn't good
         294               0 :         return NS_ERROR_FAILURE;
         295                 :     }
         296              10 :     mListeners.Put(canonical, listener);
         297              10 :     return NS_OK;
         298                 : }
         299                 :
         300                 : NS_IMETHODIMP
         301              10 : nsConsoleService::UnregisterListener(nsIConsoleListener *listener)
         302                 : {
         303              10 :     if (!NS_IsMainThread()) {
         304               0 :         NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
         305               0 :         return NS_ERROR_NOT_SAME_THREAD;
         306                 :     }
         307                 :
         308              20 :     nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
         309                 :
         310              20 :     MutexAutoLock lock(mLock);
         311                 :
         312              10 :     if (!mListeners.GetWeak(canonical)) {
         313                 :         // Unregistering a listener that was never registered?
         314               0 :         return NS_ERROR_FAILURE;
         315                 :     }
         316              10 :     mListeners.Remove(canonical);
         317              10 :     return NS_OK;
         318                 : }
         319                 :
         320                 : NS_IMETHODIMP
         321              75 : nsConsoleService::Reset()
         322                 : {
         323                 :     /*
         324                 :      * Make sure nobody trips into the buffer while it's being reset
         325                 :      */
         326             150 :     MutexAutoLock lock(mLock);
         327                 :
         328              75 :     mCurrent = 0;
         329              75 :     mFull = false;
         330                 :
         331                 :     /*
         332                 :      * Free all messages stored so far (cf. destructor)
         333                 :      */
         334             175 :     for (PRUint32 i = 0; i < mBufferSize && mMessages[i] != nsnull; i++)
         335             100 :         NS_RELEASE(mMessages[i]);
         336                 :
         337              75 :     return NS_OK;
         338                 : }

Generated by: LCOV version 1.7