LCOV - code coverage report
Current view: directory - xpcom/base - MapsMemoryReporter.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 184 166 90.2 %
Date: 2012-06-02 Functions: 17 16 94.1 %

       1                 : /* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim:set ts=2 sw=2 sts=2 ci et: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
      19                 :  *
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Justin Lebar <justin.lebar@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/Util.h"
      41                 : 
      42                 : #include "mozilla/MapsMemoryReporter.h"
      43                 : #include "nsIMemoryReporter.h"
      44                 : #include "nsString.h"
      45                 : #include "nsCOMPtr.h"
      46                 : #include "nsTHashtable.h"
      47                 : #include "nsHashKeys.h"
      48                 : #include <stdio.h>
      49                 : 
      50                 : namespace mozilla {
      51                 : namespace MapsMemoryReporter {
      52                 : 
      53                 : #if !defined(XP_LINUX)
      54                 : #error "This doesn't have a prayer of working if we're not on Linux."
      55                 : #endif
      56                 : 
      57                 : // mozillaLibraries is a list of all the shared libraries we build.  This list
      58                 : // is used for determining whether a library is a "Mozilla library" or a
      59                 : // "third-party library".  But even if this list is missing items, about:memory
      60                 : // will identify a library in the same directory as libxul.so as a "Mozilla
      61                 : // library".
      62                 : const char* mozillaLibraries[] =
      63                 : {
      64                 :   "libfreebl3.so",
      65                 :   "libmozalloc.so",
      66                 :   "libmozsqlite3.so",
      67                 :   "libnspr4.so",
      68                 :   "libnss3.so",
      69                 :   "libnssckbi.so",
      70                 :   "libnssdbm3.so",
      71                 :   "libnssutil3.so",
      72                 :   "libplc4.so",
      73                 :   "libplds4.so",
      74                 :   "libsmime3.so",
      75                 :   "libsoftokn3.so",
      76                 :   "libssl3.so",
      77                 :   "libxpcom.so",
      78                 :   "libxul.so"
      79                 : };
      80                 : 
      81                 : namespace {
      82                 : 
      83            1467 : bool EndsWithLiteral(const nsCString &aHaystack, const char *aNeedle)
      84                 : {
      85            1467 :   PRInt32 idx = aHaystack.RFind(aNeedle);
      86            1467 :   if (idx == -1) {
      87             912 :     return false;
      88                 :   }
      89                 : 
      90             555 :   return idx + strlen(aNeedle) == aHaystack.Length();
      91                 : }
      92                 : 
      93             568 : void GetDirname(const nsCString &aPath, nsACString &aOut)
      94                 : {
      95             568 :   PRInt32 idx = aPath.RFind("/");
      96             568 :   if (idx == -1) {
      97               0 :     aOut.Truncate();
      98                 :   }
      99                 :   else {
     100             568 :     aOut.Assign(Substring(aPath, 0, idx));
     101                 :   }
     102             568 : }
     103                 : 
     104             888 : void GetBasename(const nsCString &aPath, nsACString &aOut)
     105                 : {
     106            1776 :   nsCString out;
     107             888 :   PRInt32 idx = aPath.RFind("/");
     108             888 :   if (idx == -1) {
     109             185 :     out.Assign(aPath);
     110                 :   }
     111                 :   else {
     112             703 :     out.Assign(Substring(aPath, idx + 1));
     113                 :   }
     114                 : 
     115                 :   // On Android, some entries in /dev/ashmem end with "(deleted)" (e.g.
     116                 :   // "/dev/ashmem/libxul.so(deleted)").  We don't care about this modifier, so
     117                 :   // cut it off when getting the entry's basename.
     118             888 :   if (EndsWithLiteral(out, "(deleted)")) {
     119               0 :     out.Assign(Substring(out, 0, out.RFind("(deleted)")));
     120                 :   }
     121             888 :   out.StripChars(" ");
     122                 : 
     123             888 :   aOut.Assign(out);
     124             888 : }
     125                 : 
     126                 : // MapsReporter::CollectReports uses this stuct to keep track of whether it's
     127                 : // seen a mapping under 'smaps/resident', 'smaps/pss', 'smaps/vsize', and
     128                 : // 'smaps/swap'.
     129                 : struct CategoriesSeen {
     130               3 :   CategoriesSeen() :
     131                 :     mSeenResident(false),
     132                 :     mSeenPss(false),
     133                 :     mSeenVsize(false),
     134               3 :     mSeenSwap(false)
     135                 :   {
     136               3 :   }
     137                 : 
     138                 :   bool mSeenResident;
     139                 :   bool mSeenPss;
     140                 :   bool mSeenVsize;
     141                 :   bool mSeenSwap;
     142                 : };
     143                 : 
     144                 : } // anonymous namespace
     145                 : 
     146                 : class MapsReporter : public nsIMemoryMultiReporter
     147            1419 : {
     148                 : public:
     149                 :   MapsReporter();
     150                 : 
     151                 :   NS_DECL_ISUPPORTS
     152                 : 
     153               0 :   NS_IMETHOD GetName(nsACString &aName)
     154                 :   {
     155               0 :       aName.AssignLiteral("smaps");
     156               0 :       return NS_OK;
     157                 :   }
     158                 : 
     159                 :   NS_IMETHOD
     160                 :   CollectReports(nsIMemoryMultiReporterCallback *aCb,
     161                 :                  nsISupports *aClosure);
     162                 : 
     163                 :   NS_IMETHOD
     164               3 :   GetExplicitNonHeap(PRInt64 *aAmount) {
     165                 :     // This reporter doesn't do any "explicit" measurements.
     166               3 :     *aAmount = 0;
     167               3 :     return NS_OK;
     168                 :   }
     169                 : 
     170                 : private:
     171                 :   // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the
     172                 :   // the directory containing libxul.
     173                 :   nsresult FindLibxul();
     174                 : 
     175                 :   nsresult
     176                 :   ParseMapping(FILE *aFile,
     177                 :                nsIMemoryMultiReporterCallback *aCb,
     178                 :                nsISupports *aClosure,
     179                 :                CategoriesSeen *aCategoriesSeen);
     180                 : 
     181                 :   void
     182                 :   GetReporterNameAndDescription(const char *aPath,
     183                 :                                 const char *aPermissions,
     184                 :                                 nsACString &aName,
     185                 :                                 nsACString &aDesc);
     186                 : 
     187                 :   nsresult
     188                 :   ParseMapBody(FILE *aFile,
     189                 :                const nsACString &aName,
     190                 :                const nsACString &aDescription,
     191                 :                nsIMemoryMultiReporterCallback *aCb,
     192                 :                nsISupports *aClosure,
     193                 :                CategoriesSeen *aCategoriesSeen);
     194                 : 
     195                 :   bool mSearchedForLibxul;
     196                 :   nsCString mLibxulDir;
     197                 :   nsTHashtable<nsCStringHashKey> mMozillaLibraries;
     198                 : };
     199                 : 
     200           11382 : NS_IMPL_THREADSAFE_ISUPPORTS1(MapsReporter, nsIMemoryMultiReporter)
     201                 : 
     202            1419 : MapsReporter::MapsReporter()
     203            1419 :   : mSearchedForLibxul(false)
     204                 : {
     205            1419 :   const PRUint32 len = ArrayLength(mozillaLibraries);
     206            1419 :   mMozillaLibraries.Init(len);
     207           22704 :   for (PRUint32 i = 0; i < len; i++) {
     208           42570 :     nsCAutoString str;
     209           21285 :     str.Assign(mozillaLibraries[i]);
     210           21285 :     mMozillaLibraries.PutEntry(str);
     211                 :   }
     212            1419 : }
     213                 : 
     214                 : NS_IMETHODIMP
     215               3 : MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCb,
     216                 :                              nsISupports *aClosure)
     217                 : {
     218               3 :   CategoriesSeen categoriesSeen;
     219                 : 
     220               3 :   FILE *f = fopen("/proc/self/smaps", "r");
     221               3 :   if (!f)
     222               0 :     return NS_ERROR_FAILURE;
     223                 : 
     224             752 :   while (true) {
     225             755 :     nsresult rv = ParseMapping(f, aCb, aClosure, &categoriesSeen);
     226             755 :     if (NS_FAILED(rv))
     227                 :       break;
     228                 :   }
     229                 : 
     230               3 :   fclose(f);
     231                 : 
     232                 :   // For sure we should have created some node under 'smaps/resident' and
     233                 :   // 'smaps/vsize'; otherwise we're probably not reading smaps correctly.  If we
     234                 :   // didn't create a node under 'smaps/swap', create one here so about:memory
     235                 :   // knows to create an empty 'smaps/swap' tree.  See also bug 682735.
     236                 : 
     237               3 :   NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a vsize node?");
     238               3 :   NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?");
     239               3 :   if (!categoriesSeen.mSeenSwap) {
     240                 :     nsresult rv;
     241               3 :     rv = aCb->Callback(NS_LITERAL_CSTRING(""),
     242               3 :                        NS_LITERAL_CSTRING("smaps/swap/total"),
     243                 :                        nsIMemoryReporter::KIND_NONHEAP,
     244                 :                        nsIMemoryReporter::UNITS_BYTES,
     245                 :                        0,
     246               3 :                        NS_LITERAL_CSTRING("This process uses no swap space."),
     247               3 :                        aClosure);
     248               3 :     NS_ENSURE_SUCCESS(rv, rv);
     249                 :   }
     250                 : 
     251               3 :   return NS_OK;
     252                 : }
     253                 : 
     254                 : nsresult
     255             755 : MapsReporter::FindLibxul()
     256                 : {
     257             755 :   if (mSearchedForLibxul)
     258             754 :     return NS_OK;
     259                 : 
     260               1 :   mSearchedForLibxul = true;
     261                 : 
     262               1 :   mLibxulDir.Truncate();
     263                 : 
     264                 :   // Note that we're scanning /proc/self/*maps*, not smaps, here.
     265               1 :   FILE *f = fopen("/proc/self/maps", "r");
     266               1 :   if (!f) {
     267               0 :     return NS_ERROR_FAILURE;
     268                 :   }
     269                 : 
     270             135 :   while (true) {
     271                 :     // Skip any number of non-slash characters, then capture starting with the
     272                 :     // slash to the newline.  This is the path part of /proc/self/maps.
     273                 :     char path[1025];
     274             136 :     int numRead = fscanf(f, "%*[^/]%1024[^\n]", path);
     275             136 :     if (numRead != 1) {
     276               0 :       break;
     277                 :     }
     278                 : 
     279             272 :     nsCAutoString pathStr;
     280             136 :     pathStr.Append(path);
     281                 : 
     282             272 :     nsCAutoString basename;
     283             136 :     GetBasename(pathStr, basename);
     284                 : 
     285             136 :     if (basename.EqualsLiteral("libxul.so")) {
     286               1 :       GetDirname(pathStr, mLibxulDir);
     287                 :       break;
     288                 :     }
     289                 :   }
     290                 : 
     291               1 :   fclose(f);
     292               1 :   return mLibxulDir.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
     293                 : }
     294                 : 
     295                 : nsresult
     296             755 : MapsReporter::ParseMapping(
     297                 :   FILE *aFile,
     298                 :   nsIMemoryMultiReporterCallback *aCb,
     299                 :   nsISupports *aClosure,
     300                 :   CategoriesSeen *aCategoriesSeen)
     301                 : {
     302                 :   // We need to use native types in order to get good warnings from fscanf, so
     303                 :   // let's make sure that the native types have the sizes we expect.
     304                 :   PR_STATIC_ASSERT(sizeof(long long) == sizeof(PRInt64));
     305                 :   PR_STATIC_ASSERT(sizeof(int) == sizeof(PRInt32));
     306                 : 
     307                 :   // Don't bail if FindLibxul fails.  We can still gather meaningful stats
     308                 :   // here.
     309             755 :   FindLibxul();
     310                 : 
     311                 :   // The first line of an entry in /proc/self/smaps looks just like an entry
     312                 :   // in /proc/maps:
     313                 :   //
     314                 :   //   address           perms offset  dev   inode  pathname
     315                 :   //   02366000-025d8000 rw-p 00000000 00:00 0      [heap]
     316                 : 
     317             755 :   const int argCount = 8;
     318                 : 
     319                 :   unsigned long long addrStart, addrEnd;
     320                 :   char perms[5];
     321                 :   unsigned long long offset;
     322                 :   char devMajor[3];
     323                 :   char devMinor[3];
     324                 :   unsigned int inode;
     325                 :   char path[1025];
     326                 : 
     327                 :   // A path might not be present on this line; set it to the empty string.
     328             755 :   path[0] = '\0';
     329                 : 
     330                 :   // This is a bit tricky.  Whitespace in a scanf pattern matches *any*
     331                 :   // whitespace, including newlines.  We want this pattern to match a line
     332                 :   // with or without a path, but we don't want to look to a new line for the
     333                 :   // path.  Thus we have %u%1024[^\n] at the end of the pattern.  This will
     334                 :   // capture into the path some leading whitespace, which we'll later trim off.
     335                 :   int numRead = fscanf(aFile, "%llx-%llx %4s %llx %2s:%2s %u%1024[^\n]",
     336                 :                        &addrStart, &addrEnd, perms, &offset, devMajor,
     337             755 :                        devMinor, &inode, path);
     338                 : 
     339                 :   // Eat up any whitespace at the end of this line, including the newline.
     340             755 :   fscanf(aFile, " ");
     341                 : 
     342                 :   // We might or might not have a path, but the rest of the arguments should be
     343                 :   // there.
     344             755 :   if (numRead != argCount && numRead != argCount - 1) {
     345               3 :     return NS_ERROR_FAILURE;
     346                 :   }
     347                 : 
     348            1504 :   nsCAutoString name, description;
     349             752 :   GetReporterNameAndDescription(path, perms, name, description);
     350                 : 
     351            4512 :   while (true) {
     352                 :     nsresult rv = ParseMapBody(aFile, name, description, aCb,
     353            5264 :                                aClosure, aCategoriesSeen);
     354            5264 :     if (NS_FAILED(rv))
     355                 :       break;
     356                 :   }
     357                 : 
     358             752 :   return NS_OK;
     359                 : }
     360                 : 
     361                 : void
     362             752 : MapsReporter::GetReporterNameAndDescription(
     363                 :   const char *aPath,
     364                 :   const char *aPerms,
     365                 :   nsACString &aName,
     366                 :   nsACString &aDesc)
     367                 : {
     368             752 :   aName.Truncate();
     369             752 :   aDesc.Truncate();
     370                 : 
     371                 :   // If aPath points to a file, we have its absolute path, plus some
     372                 :   // whitespace.  Truncate this to its basename, and put the absolute path in
     373                 :   // the description.
     374            1504 :   nsCAutoString absPath;
     375             752 :   absPath.Append(aPath);
     376             752 :   absPath.StripChars(" ");
     377                 : 
     378            1504 :   nsCAutoString basename;
     379             752 :   GetBasename(absPath, basename);
     380                 : 
     381             752 :   if (basename.EqualsLiteral("[heap]")) {
     382               0 :     aName.Append("anonymous/anonymous, within brk()");
     383                 :     aDesc.Append("Memory in anonymous mappings within the boundaries "
     384                 :                  "defined by brk() / sbrk().  This is likely to be just "
     385                 :                  "a portion of the application's heap; the remainder "
     386                 :                  "lives in other anonymous mappings. This node corresponds to "
     387               0 :                  "'[heap]' in /proc/self/smaps.");
     388                 :   }
     389             752 :   else if (basename.EqualsLiteral("[stack]")) {
     390               3 :     aName.Append("main thread's stack");
     391                 :     aDesc.Append("The stack size of the process's main thread.  This node "
     392               3 :                  "corresponds to '[stack]' in /proc/self/smaps.");
     393                 :   }
     394             749 :   else if (basename.EqualsLiteral("[vdso]")) {
     395               3 :     aName.Append("vdso");
     396                 :     aDesc.Append("The virtual dynamically-linked shared object, also known as "
     397                 :                  "the 'vsyscall page'. This is a memory region mapped by the "
     398                 :                  "operating system for the purpose of allowing processes to "
     399                 :                  "perform some privileged actions without the overhead of a "
     400               3 :                  "syscall.");
     401                 :   }
     402             746 :   else if (!basename.IsEmpty()) {
     403            1134 :     nsCAutoString dirname;
     404             567 :     GetDirname(absPath, dirname);
     405                 : 
     406                 :     // Hack: A file is a shared library if the basename contains ".so" and its
     407                 :     // dirname contains "/lib", or if the basename ends with ".so".
     408            1221 :     if (EndsWithLiteral(basename, ".so") ||
     409             654 :         (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
     410             555 :       aName.Append("shared-libraries/");
     411            1005 :       if ((!mLibxulDir.IsEmpty() && dirname.Equals(mLibxulDir)) ||
     412             450 :           mMozillaLibraries.Contains(basename)) {
     413             105 :         aName.Append("shared-libraries-mozilla/");
     414                 :       }
     415                 :       else {
     416             450 :         aName.Append("shared-libraries-other/");
     417                 :       }
     418                 :     }
     419                 :     else {
     420              12 :       aName.Append("other-files/");
     421              12 :       if (EndsWithLiteral(basename, ".xpi")) {
     422               0 :         aName.Append("extensions/");
     423                 :       }
     424              12 :       else if (dirname.Find("/fontconfig") != -1) {
     425               0 :         aName.Append("fontconfig/");
     426                 :       }
     427                 :     }
     428                 : 
     429             567 :     aName.Append(basename);
     430             567 :     aDesc.Append(absPath);
     431                 :   }
     432                 :   else {
     433             179 :     aName.Append("anonymous/anonymous, outside brk()");
     434                 :     aDesc.Append("Memory in anonymous mappings outside the boundaries defined "
     435             179 :                  "by brk() / sbrk().");
     436                 :   }
     437                 : 
     438             752 :   aName.Append(" [");
     439             752 :   aName.Append(aPerms);
     440             752 :   aName.Append("]");
     441                 : 
     442                 :   // Modify the description to include an explanation of the permissions.
     443             752 :   aDesc.Append(" (");
     444             752 :   if (strstr(aPerms, "rw")) {
     445             405 :     aDesc.Append("read/write, ");
     446                 :   }
     447             347 :   else if (strchr(aPerms, 'r')) {
     448             309 :     aDesc.Append("read-only, ");
     449                 :   }
     450              38 :   else if (strchr(aPerms, 'w')) {
     451               0 :     aDesc.Append("write-only, ");
     452                 :   }
     453                 :   else {
     454              38 :     aDesc.Append("not readable, not writable, ");
     455                 :   }
     456                 : 
     457             752 :   if (strchr(aPerms, 'x')) {
     458             707 :     aDesc.Append("executable, ");
     459                 :   }
     460                 :   else {
     461              45 :     aDesc.Append("not executable, ");
     462                 :   }
     463                 : 
     464             752 :   if (strchr(aPerms, 's')) {
     465               3 :     aDesc.Append("shared");
     466                 :   }
     467             749 :   else if (strchr(aPerms, 'p')) {
     468             749 :     aDesc.Append("private");
     469                 :   }
     470                 :   else {
     471               0 :     aDesc.Append("not shared or private??");
     472                 :   }
     473             752 :   aDesc.Append(")");
     474             752 : }
     475                 : 
     476                 : nsresult
     477            5264 : MapsReporter::ParseMapBody(
     478                 :   FILE *aFile,
     479                 :   const nsACString &aName,
     480                 :   const nsACString &aDescription,
     481                 :   nsIMemoryMultiReporterCallback *aCb,
     482                 :   nsISupports *aClosure,
     483                 :   CategoriesSeen *aCategoriesSeen)
     484                 : {
     485                 :   PR_STATIC_ASSERT(sizeof(long long) == sizeof(PRInt64));
     486                 : 
     487            5264 :   const int argCount = 2;
     488                 : 
     489                 :   char desc[1025];
     490                 :   unsigned long long size;
     491            5264 :   if (fscanf(aFile, "%1024[a-zA-Z_]: %llu kB\n",
     492            5264 :              desc, &size) != argCount) {
     493             752 :     return NS_ERROR_FAILURE;
     494                 :   }
     495                 : 
     496                 :   // Don't report nodes with size 0.
     497            4512 :   if (size == 0)
     498            2326 :     return NS_OK;
     499                 : 
     500                 :   const char* category;
     501            2186 :   if (strcmp(desc, "Size") == 0) {
     502             752 :     category = "vsize";
     503             752 :     aCategoriesSeen->mSeenVsize = true;
     504                 :   }
     505            1434 :   else if (strcmp(desc, "Rss") == 0) {
     506             684 :     category = "resident";
     507             684 :     aCategoriesSeen->mSeenResident = true;
     508                 :   }
     509             750 :   else if (strcmp(desc, "Pss") == 0) {
     510               0 :     category = "pss";
     511               0 :     aCategoriesSeen->mSeenPss = true;
     512                 :   }
     513             750 :   else if (strcmp(desc, "Swap") == 0) {
     514               0 :     category = "swap";
     515               0 :     aCategoriesSeen->mSeenSwap = true;
     516                 :   }
     517                 :   else {
     518                 :     // Don't report this category.
     519             750 :     return NS_OK;
     520                 :   }
     521                 : 
     522            2872 :   nsCAutoString path;
     523            1436 :   path.Append("smaps/");
     524            1436 :   path.Append(category);
     525            1436 :   path.Append("/");
     526            1436 :   path.Append(aName);
     527                 : 
     528                 :   nsresult rv;
     529            1436 :   rv = aCb->Callback(NS_LITERAL_CSTRING(""),
     530                 :                      path,
     531                 :                      nsIMemoryReporter::KIND_NONHEAP,
     532                 :                      nsIMemoryReporter::UNITS_BYTES,
     533                 :                      PRInt64(size) * 1024, // convert from kB to bytes
     534            1436 :                      aDescription, aClosure);
     535            1436 :   NS_ENSURE_SUCCESS(rv, rv);
     536                 : 
     537            1436 :   return NS_OK;
     538                 : }
     539                 : 
     540            1419 : void Init()
     541                 : {
     542            2838 :   nsCOMPtr<nsIMemoryMultiReporter> reporter = new MapsReporter();
     543            1419 :   NS_RegisterMemoryMultiReporter(reporter);
     544            1419 : }
     545                 : 
     546                 : } // namespace MapsMemoryReporter
     547                 : } // namespace mozilla

Generated by: LCOV version 1.7