LCOV - code coverage report
Current view: top level - dom/base - nsWindowMemoryReporter.h (source / functions) Hit Total Coverage
Test: output.info Lines: 9 13 69.2 %
Date: 2017-07-14 16:53:18 Functions: 2 3 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef nsWindowMemoryReporter_h__
       8             : #define nsWindowMemoryReporter_h__
       9             : 
      10             : #include "nsGlobalWindow.h"
      11             : #include "nsIMemoryReporter.h"
      12             : #include "nsIObserver.h"
      13             : #include "nsITimer.h"
      14             : #include "nsDataHashtable.h"
      15             : #include "nsWeakReference.h"
      16             : #include "mozilla/Attributes.h"
      17             : #include "mozilla/Assertions.h"
      18             : #include "mozilla/MemoryReporting.h"
      19             : #include "mozilla/PodOperations.h"
      20             : #include "mozilla/TimeStamp.h"
      21             : #include "nsArenaMemoryStats.h"
      22             : 
      23             : class nsWindowSizes {
      24             : #define FOR_EACH_SIZE(macro) \
      25             :   macro(DOM,   mDOMElementNodesSize) \
      26             :   macro(DOM,   mDOMTextNodesSize) \
      27             :   macro(DOM,   mDOMCDATANodesSize) \
      28             :   macro(DOM,   mDOMCommentNodesSize) \
      29             :   macro(DOM,   mDOMEventTargetsSize) \
      30             :   macro(DOM,   mDOMOtherSize) \
      31             :   macro(Style, mStyleSheetsSize) \
      32             :   macro(Other, mLayoutPresShellSize) \
      33             :   macro(Style, mLayoutStyleSetsSize) \
      34             :   macro(Other, mLayoutTextRunsSize) \
      35             :   macro(Other, mLayoutPresContextSize) \
      36             :   macro(Other, mLayoutFramePropertiesSize) \
      37             :   macro(Other, mPropertyTablesSize) \
      38             : 
      39             : public:
      40          21 :   explicit nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf)
      41          21 :     :
      42             :       #define ZERO_SIZE(kind, mSize)  mSize(0),
      43             :       FOR_EACH_SIZE(ZERO_SIZE)
      44             :       #undef ZERO_SIZE
      45             :       mDOMEventTargetsCount(0),
      46             :       mDOMEventListenersCount(0),
      47             :       mArenaStats(),
      48          21 :       mMallocSizeOf(aMallocSizeOf)
      49          21 :   {}
      50             : 
      51           0 :   void addToTabSizes(nsTabSizes *sizes) const {
      52             :     #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
      53           0 :     FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
      54             :     #undef ADD_TO_TAB_SIZES
      55           0 :     mArenaStats.addToTabSizes(sizes);
      56           0 :   }
      57             : 
      58          42 :   size_t getTotalSize() const
      59             :   {
      60          42 :     size_t total = 0;
      61             :     #define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
      62          42 :     FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
      63             :     #undef ADD_TO_TOTAL_SIZE
      64          42 :     total += mArenaStats.getTotalSize();
      65          42 :     return total;
      66             :   }
      67             : 
      68             :   #define DECL_SIZE(kind, mSize) size_t mSize;
      69             :   FOR_EACH_SIZE(DECL_SIZE);
      70             :   #undef DECL_SIZE
      71             : 
      72             :   uint32_t mDOMEventTargetsCount;
      73             :   uint32_t mDOMEventListenersCount;
      74             : 
      75             :   nsArenaMemoryStats mArenaStats;
      76             :   mozilla::MallocSizeOf mMallocSizeOf;
      77             : 
      78             : #undef FOR_EACH_SIZE
      79             : };
      80             : 
      81             : /**
      82             :  * nsWindowMemoryReporter is responsible for the 'explicit/window-objects'
      83             :  * memory reporter.
      84             :  *
      85             :  * We classify DOM window objects into one of three categories:
      86             :  *
      87             :  *   - "active" windows, which are displayed in a tab (as the top-level window
      88             :  *     or an iframe),
      89             :  *
      90             :  *   - "cached" windows, which are in the fastback cache (aka the bfcache), and
      91             :  *
      92             :  *   - "detached" windows, which have a null docshell.  A window becomes
      93             :  *     detached when its <iframe> or tab containing the window is destroyed --
      94             :  *     i.e., when the window is no longer active or cached.
      95             :  *
      96             :  * Additionally, we classify a subset of detached windows as "ghost" windows.
      97             :  * Although ghost windows can happen legitimately (a page can hold a reference
      98             :  * to a cross-domain window and then close its container), the presence of
      99             :  * ghost windows is often indicative of a memory leak.
     100             :  *
     101             :  * A window is a ghost if it meets the following three criteria:
     102             :  *
     103             :  *   1) The window is detached.
     104             :  *
     105             :  *   2) There exist no non-detached windows with the same base domain as
     106             :  *      the window's principal.  (For example, the base domain of
     107             :  *      "wiki.mozilla.co.uk" is "mozilla.co.uk".)  This criterion makes us less
     108             :  *      likely to flag a legitimately held-alive detached window as a ghost.
     109             :  *
     110             :  *   3) The window has met criteria (1) and (2) above for at least
     111             :  *      memory.ghost_window_timeout_seconds.  This criterion is in place so we
     112             :  *      don't immediately declare a window a ghost before the GC/CC has had a
     113             :  *      chance to run.
     114             :  *
     115             :  * nsWindowMemoryReporter observes window detachment and uses mDetachedWindows
     116             :  * to remember when a window first met criteria (1) and (2).  When we generate
     117             :  * a memory report, we use this accounting to determine which windows are
     118             :  * ghosts.
     119             :  *
     120             :  *
     121             :  * We use the following memory reporter path for active and cached windows:
     122             :  *
     123             :  *   explicit/window-objects/top(<top-outer-uri>, id=<top-outer-id>)/<category>/window(<window-uri>)/...
     124             :  *
     125             :  * For detached and ghost windows, we use
     126             :  *
     127             :  *   explicit/window-objects/top(none)/<category>/window(<window-uri>)/...
     128             :  *
     129             :  * Where
     130             :  *
     131             :  * - <category> is "active", "cached", "detached", or "ghost", as described
     132             :  *   above.
     133             :  *
     134             :  * - <top-outer-id> is the window id of the top outer window (i.e. the tab, or
     135             :  *   the top level chrome window).  Exposing this ensures that each tab gets
     136             :  *   its own sub-tree, even if multiple tabs are showing the same URI.
     137             :  *
     138             :  * - <top-uri> is the URI of the top window.  Excepting special windows (such
     139             :  *   as browser.xul or hiddenWindow.html) it's what the address bar shows for
     140             :  *   the tab.
     141             :  *
     142             :  */
     143             : class nsWindowMemoryReporter final : public nsIMemoryReporter,
     144             :                                      public nsIObserver,
     145             :                                      public nsSupportsWeakReference
     146             : {
     147             : public:
     148             :   NS_DECL_ISUPPORTS
     149             :   NS_DECL_NSIMEMORYREPORTER
     150             :   NS_DECL_NSIOBSERVER
     151             : 
     152             :   static void Init();
     153             : 
     154             : #ifdef DEBUG
     155             :   /**
     156             :    * Unlink all known ghost windows, to enable investigating what caused them
     157             :    * to become ghost windows in the first place.
     158             :    */
     159             :   static void UnlinkGhostWindows();
     160             : #endif
     161             : 
     162             :   static nsWindowMemoryReporter* Get();
     163             :   void ObserveDOMWindowDetached(nsGlobalWindow* aWindow);
     164             : 
     165             :   static int64_t GhostWindowsDistinguishedAmount();
     166             : 
     167             : private:
     168             :   ~nsWindowMemoryReporter();
     169             : 
     170             :   // Protect ctor, use Init() instead.
     171             :   nsWindowMemoryReporter();
     172             : 
     173             :   /**
     174             :    * Get the number of seconds for which a window must satisfy ghost criteria
     175             :    * (1) and (2) before we deem that it satisfies criterion (3).
     176             :    */
     177             :   uint32_t GetGhostTimeout();
     178             : 
     179             :   void ObserveAfterMinimizeMemoryUsage();
     180             : 
     181             :   /**
     182             :    * Iterate over all weak window pointers in mDetachedWindows and update our
     183             :    * accounting of which windows meet ghost criterion (2).
     184             :    *
     185             :    * This method also cleans up mDetachedWindows, removing entries for windows
     186             :    * which have been destroyed or are no longer detached.
     187             :    *
     188             :    * If aOutGhostIDs is non-null, we populate it with the Window IDs of the
     189             :    * ghost windows.
     190             :    *
     191             :    * This is called asynchronously after we observe a DOM window being detached
     192             :    * from its docshell, and also right before we generate a memory report.
     193             :    */
     194             :   void CheckForGhostWindows(nsTHashtable<nsUint64HashKey> *aOutGhostIDs = nullptr);
     195             : 
     196             :   /**
     197             :    * Eventually do a check for ghost windows, if we haven't done one recently
     198             :    * and we aren't already planning to do one soon.
     199             :    */
     200             :   void AsyncCheckForGhostWindows();
     201             : 
     202             :   /**
     203             :    * Kill the check timer, if it exists.
     204             :    */
     205             :   void KillCheckTimer();
     206             : 
     207             :   static void CheckTimerFired(nsITimer* aTimer, void* aClosure);
     208             : 
     209             :   /**
     210             :    * Maps a weak reference to a detached window (nsIWeakReference) to the time
     211             :    * when we observed that the window met ghost criterion (2) above.
     212             :    *
     213             :    * If the window has not yet met criterion (2) it maps to the null timestamp.
     214             :    *
     215             :    * (Although windows are not added to this table until they're detached, it's
     216             :    * possible for a detached window to become non-detached, and we won't
     217             :    * remove it from the table until CheckForGhostWindows runs.)
     218             :    */
     219             :   nsDataHashtable<nsISupportsHashKey, mozilla::TimeStamp> mDetachedWindows;
     220             : 
     221             :   /**
     222             :    * Track the last time we ran CheckForGhostWindows(), to avoid running it
     223             :    * too often after a DOM window is detached.
     224             :    */
     225             :   mozilla::TimeStamp mLastCheckForGhostWindows;
     226             : 
     227             :   nsCOMPtr<nsITimer> mCheckTimer;
     228             : 
     229             :   bool mCycleCollectorIsRunning;
     230             : 
     231             :   bool mCheckTimerWaitingForCCEnd;
     232             : 
     233             :   int64_t mGhostWindowCount;
     234             : };
     235             : 
     236             : #endif // nsWindowMemoryReporter_h__
     237             : 

Generated by: LCOV version 1.13