LCOV - code coverage report
Current view: directory - xpcom/threads - HangMonitor.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 66 47 71.2 %
Date: 2012-06-02 Functions: 7 6 85.7 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

       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 Firefox.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * the Mozilla Foundation <http://www.mozilla.org>.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "mozilla/HangMonitor.h"
      39                 : #include "mozilla/Monitor.h"
      40                 : #include "mozilla/Preferences.h"
      41                 : #include "nsXULAppAPI.h"
      42                 : #include "nsThreadUtils.h"
      43                 : 
      44                 : #ifdef MOZ_CRASHREPORTER
      45                 : #include "nsExceptionHandler.h"
      46                 : #endif
      47                 : 
      48                 : #ifdef XP_WIN
      49                 : #include <windows.h>
      50                 : #endif
      51                 : 
      52                 : namespace mozilla { namespace HangMonitor {
      53                 : 
      54                 : /**
      55                 :  * A flag which may be set from within a debugger to disable the hang
      56                 :  * monitor.
      57                 :  */
      58                 : volatile bool gDebugDisableHangMonitor = false;
      59                 : 
      60                 : const char kHangMonitorPrefName[] = "hangmonitor.timeout";
      61                 : 
      62                 : // Monitor protects gShutdown and gTimeout, but not gTimestamp which rely on
      63                 : // being atomically set by the processor; synchronization doesn't really matter
      64                 : // in this use case.
      65                 : Monitor* gMonitor;
      66                 : 
      67                 : // The timeout preference, in seconds.
      68                 : PRInt32 gTimeout;
      69                 : 
      70                 : PRThread* gThread;
      71                 : 
      72                 : // Set when shutdown begins to signal the thread to exit immediately.
      73                 : bool gShutdown;
      74                 : 
      75                 : // The timestamp of the last event notification, or PR_INTERVAL_NO_WAIT if
      76                 : // we're currently not processing events.
      77                 : volatile PRIntervalTime gTimestamp;
      78                 : 
      79                 : // PrefChangedFunc

      80                 : int
      81            1420 : PrefChanged(const char*, void*)

      82                 : {
      83            1420 :   PRInt32 newval = Preferences::GetInt(kHangMonitorPrefName);

      84            2840 :   MonitorAutoLock lock(*gMonitor);

      85            1420 :   if (newval != gTimeout) {

      86               0 :     gTimeout = newval;

      87               0 :     lock.Notify();

      88                 :   }
      89                 : 
      90            1420 :   return 0;

      91                 : }
      92                 : 

      93                 : void
      94               0 : Crash()

      95                 : {
      96               0 :   if (gDebugDisableHangMonitor) {

      97               0 :     return;

      98                 :   }
      99                 : 
     100                 : #ifdef XP_WIN
     101                 :   if (::IsDebuggerPresent()) {
     102                 :     return;
     103                 :   }
     104                 : #endif
     105                 : 
     106                 : #ifdef MOZ_CRASHREPORTER
     107               0 :   CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Hang"),

     108               0 :                                      NS_LITERAL_CSTRING("1"));

     109                 : #endif
     110                 : 
     111               0 :   NS_RUNTIMEABORT("HangMonitor triggered");

     112                 : }
     113                 : 

     114                 : void
     115            1419 : ThreadMain(void*)

     116                 : {
     117            2838 :   MonitorAutoLock lock(*gMonitor);

     118                 : 
     119                 :   // In order to avoid issues with the hang monitor incorrectly triggering
     120                 :   // during a general system stop such as sleeping, the monitor thread must
     121                 :   // run twice to trigger hang protection.
     122            1419 :   PRIntervalTime lastTimestamp = 0;

     123            1419 :   int waitCount = 0;

     124                 : 
     125            1419 :   while (true) {

     126            2838 :     if (gShutdown) {

     127                 :       return; // Exit the thread
     128                 :     }
     129                 : 
     130                 :     // avoid rereading the volatile value in this loop
     131            1419 :     PRIntervalTime timestamp = gTimestamp;

     132                 : 
     133            1419 :     PRIntervalTime now = PR_IntervalNow();

     134                 : 
     135            1419 :     if (timestamp != PR_INTERVAL_NO_WAIT &&

     136                 :         now < timestamp) {
     137                 :       // 32-bit overflow, reset for another waiting period
     138               0 :       timestamp = 1; // lowest legal PRInterval value

     139                 :     }
     140                 : 
     141            1419 :     if (timestamp != PR_INTERVAL_NO_WAIT &&

     142                 :         timestamp == lastTimestamp &&
     143                 :         gTimeout > 0) {
     144               0 :       ++waitCount;

     145               0 :       if (waitCount == 2) {

     146                 :         PRInt32 delay =
     147               0 :           PRInt32(PR_IntervalToSeconds(now - timestamp));

     148               0 :         if (delay > gTimeout) {

     149               0 :           MonitorAutoUnlock unlock(*gMonitor);

     150               0 :           Crash();

     151                 :         }
     152               0 :       }

     153                 :     }
     154                 :     else {
     155            1419 :       lastTimestamp = timestamp;

     156            1419 :       waitCount = 0;

     157                 :     }
     158                 : 
     159                 :     PRIntervalTime timeout;
     160            1419 :     if (gTimeout <= 0) {

     161            1419 :       timeout = PR_INTERVAL_NO_TIMEOUT;

     162                 :     }
     163                 :     else {
     164               0 :       timeout = PR_MillisecondsToInterval(gTimeout * 500);

     165                 :     }
     166            1419 :     lock.Wait(timeout);

     167                 :   }
     168                 : }
     169                 : 

     170                 : void
     171            1419 : Startup()

     172                 : {
     173                 :   // The hang detector only runs in chrome processes. If you change this,
     174                 :   // you must also deal with the threadsafety of AnnotateCrashReport in
     175                 :   // non-chrome processes!
     176            1419 :   if (GeckoProcessType_Default != XRE_GetProcessType())

     177               0 :     return;

     178                 : 
     179            1419 :   NS_ASSERTION(!gMonitor, "Hang monitor already initialized");

     180            1419 :   gMonitor = new Monitor("HangMonitor");

     181                 : 
     182            1419 :   Preferences::RegisterCallback(PrefChanged, kHangMonitorPrefName, NULL);

     183            1419 :   PrefChanged(NULL, NULL);

     184                 : 
     185                 :   // Don't actually start measuring hangs until we hit the main event loop.
     186                 :   // This potentially misses a small class of really early startup hangs,
     187                 :   // but avoids dealing with some xpcshell tests and other situations which
     188                 :   // start XPCOM but don't ever start the event loop.
     189            1419 :   Suspend();

     190                 : 
     191                 :   gThread = PR_CreateThread(PR_USER_THREAD,
     192                 :                             ThreadMain,
     193                 :                             NULL, PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
     194            1419 :                             PR_JOINABLE_THREAD, 0);

     195                 : }
     196                 : 

     197                 : void
     198            1419 : Shutdown()

     199                 : {
     200            1419 :   if (GeckoProcessType_Default != XRE_GetProcessType())

     201               0 :     return;

     202                 : 
     203            1419 :   NS_ASSERTION(gMonitor, "Hang monitor not started");

     204                 : 
     205                 :   { // Scope the lock we're going to delete later
     206            2838 :     MonitorAutoLock lock(*gMonitor);

     207            1419 :     gShutdown = true;

     208            1419 :     lock.Notify();

     209                 :   }
     210                 : 
     211                 :   // thread creation could theoretically fail
     212            1419 :   if (gThread) {

     213            1419 :     PR_JoinThread(gThread);

     214            1419 :     gThread = NULL;

     215                 :   }
     216                 : 
     217            1419 :   delete gMonitor;

     218            1419 :   gMonitor = NULL;

     219                 : }
     220                 : 

     221                 : void
     222          438268 : NotifyActivity()

     223                 : {
     224          438268 :   NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Notify called from off the main thread.");

     225                 : 
     226                 :   // This is not a locked activity because PRTimeStamp is a 32-bit quantity
     227                 :   // which can be read/written atomically, and we don't want to pay locking
     228                 :   // penalties here.
     229          438268 :   gTimestamp = PR_IntervalNow();

     230          438268 : }

     231                 : 

     232                 : void
     233          423366 : Suspend()

     234                 : {
     235          423366 :   NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Suspend called from off the main thread.");

     236                 : 
     237                 :   // Because gTimestamp changes this resets the wait count.
     238          423366 :   gTimestamp = PR_INTERVAL_NO_WAIT;

     239          423366 : }

     240                 : 
     241                 : } } // namespace mozilla::HangMonitor

Generated by: LCOV version 1.7