LCOV - code coverage report
Current view: directory - xpcom/base - nsConsoleService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 114 88 77.2 %
Date: 2012-06-02 Functions: 19 19 100.0 %

       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           32331 : NS_IMPL_THREADSAFE_ADDREF(nsConsoleService)
      62           33721 : NS_IMPL_THREADSAFE_RELEASE(nsConsoleService)
      63                 : NS_IMPL_CLASSINFO(nsConsoleService, NULL, nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, NS_CONSOLESERVICE_CID)
      64            8738 : NS_IMPL_QUERY_INTERFACE1_CI(nsConsoleService, nsIConsoleService)
      65             371 : NS_IMPL_CI_INTERFACE_GETTER1(nsConsoleService, nsIConsoleService)
      66                 : 
      67            1390 : nsConsoleService::nsConsoleService()
      68                 :     : mMessages(nsnull)
      69                 :     , mCurrent(0)
      70                 :     , mFull(false)
      71                 :     , mDeliveringMessage(false)
      72            1390 :     , 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            1390 :     mBufferSize = 250;
      78            1390 : }
      79                 : 
      80            2780 : nsConsoleService::~nsConsoleService()
      81                 : {
      82            1390 :     PRUint32 i = 0;
      83           19604 :     while (i < mBufferSize && mMessages[i] != nsnull) {
      84           16824 :         NS_RELEASE(mMessages[i]);
      85           16824 :         i++;
      86                 :     }
      87                 : 
      88            1390 :     if (mMessages)
      89            1390 :         nsMemory::Free(mMessages);
      90            1390 : }
      91                 : 
      92                 : nsresult
      93            1390 : nsConsoleService::Init()
      94                 : {
      95                 :     mMessages = (nsIConsoleMessage **)
      96            1390 :         nsMemory::Alloc(mBufferSize * sizeof(nsIConsoleMessage *));
      97            1390 :     if (!mMessages)
      98               0 :         return NS_ERROR_OUT_OF_MEMORY;
      99                 : 
     100                 :     // Array elements should be 0 initially for circular buffer algorithm.
     101            1390 :     memset(mMessages, 0, mBufferSize * sizeof(nsIConsoleMessage *));
     102                 : 
     103            1390 :     mListeners.Init();
     104                 : 
     105            1390 :     return NS_OK;
     106                 : }
     107                 : 
     108                 : namespace {
     109                 : 
     110                 : class LogMessageRunnable : public nsRunnable
     111           81584 : {
     112                 : public:
     113           20396 :     LogMessageRunnable(nsIConsoleMessage* message, nsConsoleService* service)
     114                 :         : mMessage(message)
     115           20396 :         , mService(service)
     116           20396 :     { }
     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           20396 : LogMessageRunnable::Run()
     132                 : {
     133           20396 :     MOZ_ASSERT(NS_IsMainThread());
     134                 : 
     135           20396 :     mService->SetIsDelivering();
     136                 : 
     137           20404 :     for (PRInt32 i = 0; i < mListeners.Count(); ++i)
     138               8 :         mListeners[i]->Observe(mMessage);
     139                 : 
     140           20396 :     mService->SetDoneDelivering();
     141                 : 
     142           20396 :     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           20396 : nsConsoleService::LogMessage(nsIConsoleMessage *message)
     159                 : {
     160           20396 :     if (message == nsnull)
     161               0 :         return NS_ERROR_INVALID_ARG;
     162                 : 
     163           20396 :     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           40792 :     nsRefPtr<LogMessageRunnable> r = new LogMessageRunnable(message, this);
     169                 :     nsIConsoleMessage *retiredMessage;
     170                 : 
     171           20396 :     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           40792 :         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           20396 :         retiredMessage = mMessages[mCurrent];
     196                 :         
     197           20396 :         mMessages[mCurrent++] = message;
     198           20396 :         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           20396 :         mListeners.EnumerateRead(CollectCurrentListeners, r);
     208                 :     }
     209           20396 :     if (retiredMessage != nsnull)
     210            3472 :         NS_RELEASE(retiredMessage);
     211                 : 
     212           20396 :     NS_DispatchToMainThread(r);
     213                 : 
     214           20396 :     return NS_OK;
     215                 : }
     216                 : 
     217                 : NS_IMETHODIMP
     218           17337 : nsConsoleService::LogStringMessage(const PRUnichar *message)
     219                 : {
     220           17337 :     nsConsoleMessage *msg = new nsConsoleMessage(message);
     221           17337 :     return this->LogMessage(msg);
     222                 : }
     223                 : 
     224                 : NS_IMETHODIMP
     225               1 : 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               2 :     MutexAutoLock lock(mLock);
     234                 : 
     235               1 :     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               1 :             nsMemory::Alloc(sizeof (nsIConsoleMessage *));
     243               1 :         *messageArray = nsnull;
     244               1 :         *messages = messageArray;
     245               1 :         *count = 0;
     246                 :         
     247               1 :         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