xpcom/base/SystemMemoryReporter.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     7 #include "mozilla/SystemMemoryReporter.h"
     9 #include "mozilla/Attributes.h"
    10 #include "mozilla/Preferences.h"
    11 #include "mozilla/unused.h"
    13 #include "nsIMemoryReporter.h"
    14 #include "nsPrintfCString.h"
    15 #include "nsString.h"
    16 #include "nsTHashtable.h"
    17 #include "nsHashKeys.h"
    19 #include <dirent.h>
    20 #include <inttypes.h>
    21 #include <stdio.h>
    22 #include <sys/stat.h>
    23 #include <sys/types.h>
    24 #include <unistd.h>
    25 #include <errno.h>
    27 // This file implements a Linux-specific, system-wide memory reporter.  It
    28 // gathers all the useful memory measurements obtainable from the OS in a
    29 // single place, giving a high-level view of memory consumption for the entire
    30 // machine/device.
    31 //
    32 // Other memory reporters measure part of a single process's memory consumption.
    33 // This reporter is different in that it measures memory consumption of many
    34 // processes, and they end up in a single reports tree.  This is a slight abuse
    35 // of the memory reporting infrastructure, and therefore the results are given
    36 // their own "process" called "System", which means they show up in about:memory
    37 // in their own section, distinct from the per-process sections.
    39 namespace mozilla {
    40 namespace SystemMemoryReporter {
    42 #if !defined(XP_LINUX)
    43 #error "This won't work if we're not on Linux."
    44 #endif
    46 static bool
    47 EndsWithLiteral(const nsCString& aHaystack, const char* aNeedle)
    48 {
    49   int32_t idx = aHaystack.RFind(aNeedle);
    50   return idx != -1 && idx + strlen(aNeedle) == aHaystack.Length();
    51 }
    53 static void
    54 GetDirname(const nsCString& aPath, nsACString& aOut)
    55 {
    56   int32_t idx = aPath.RFind("/");
    57   if (idx == -1) {
    58     aOut.Truncate();
    59   } else {
    60     aOut.Assign(Substring(aPath, 0, idx));
    61   }
    62 }
    64 static void
    65 GetBasename(const nsCString& aPath, nsACString& aOut)
    66 {
    67   nsCString out;
    68   int32_t idx = aPath.RFind("/");
    69   if (idx == -1) {
    70     out.Assign(aPath);
    71   } else {
    72     out.Assign(Substring(aPath, idx + 1));
    73   }
    75   // On Android, some entries in /dev/ashmem end with "(deleted)" (e.g.
    76   // "/dev/ashmem/libxul.so(deleted)").  We don't care about this modifier, so
    77   // cut it off when getting the entry's basename.
    78   if (EndsWithLiteral(out, "(deleted)")) {
    79     out.Assign(Substring(out, 0, out.RFind("(deleted)")));
    80   }
    81   out.StripChars(" ");
    83   aOut.Assign(out);
    84 }
    86 static bool
    87 IsNumeric(const char* s)
    88 {
    89   MOZ_ASSERT(*s);   // shouldn't see empty strings
    90   while (*s) {
    91     if (!isdigit(*s)) {
    92       return false;
    93     }
    94     s++;
    95   }
    96   return true;
    97 }
    99 static bool
   100 IsAnonymous(const nsACString& aName)
   101 {
   102   // Recent kernels (e.g. 3.5) have multiple [stack:nnnn] entries, where |nnnn|
   103   // is a thread ID.  However, [stack:nnnn] entries count both stack memory
   104   // *and* anonymous memory because the kernel only knows about the start of
   105   // each thread stack, not its end.  So we treat such entries as anonymous
   106   // memory instead of stack.  This is consistent with older kernels that don't
   107   // even show [stack:nnnn] entries.
   108   return aName.IsEmpty() ||
   109          StringBeginsWith(aName, NS_LITERAL_CSTRING("[stack:"));
   110 }
   112 class SystemReporter MOZ_FINAL : public nsIMemoryReporter
   113 {
   114 public:
   115   NS_DECL_THREADSAFE_ISUPPORTS
   117 #define REPORT_WITH_CLEANUP(_path, _units, _amount, _desc, _cleanup)          \
   118   do {                                                                        \
   119     size_t amount = _amount;  /* evaluate _amount only once */                \
   120     if (amount > 0) {                                                         \
   121       nsresult rv;                                                            \
   122       rv = aHandleReport->Callback(NS_LITERAL_CSTRING("System"), _path,       \
   123                                    KIND_NONHEAP, _units, amount, _desc,       \
   124                                    aData);                                    \
   125       if (NS_WARN_IF(NS_FAILED(rv))) {                                        \
   126         _cleanup;                                                             \
   127         return rv;                                                            \
   128       }                                                                       \
   129     }                                                                         \
   130   } while (0)
   132 #define REPORT(_path, _amount, _desc) \
   133     REPORT_WITH_CLEANUP(_path, UNITS_BYTES, _amount, _desc, (void)0)
   135   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   136                             nsISupports* aData)
   137   {
   138     if (!Preferences::GetBool("memory.system_memory_reporter")) {
   139       return NS_OK;
   140     }
   142     // Read relevant fields from /proc/meminfo.
   143     int64_t memTotal = 0, memFree = 0;
   144     nsresult rv = ReadMemInfo(&memTotal, &memFree);
   146     // Collect per-process reports from /proc/<pid>/smaps.
   147     int64_t totalPss = 0;
   148     rv = CollectProcessReports(aHandleReport, aData, &totalPss);
   149     NS_ENSURE_SUCCESS(rv, rv);
   151     // Report the non-process numbers.
   152     int64_t other = memTotal - memFree - totalPss;
   153     REPORT(NS_LITERAL_CSTRING("mem/other"), other, NS_LITERAL_CSTRING(
   154 "Memory which is neither owned by any user-space process nor free. Note that "
   155 "this includes memory holding cached files from the disk which can be "
   156 "reclaimed by the OS at any time."));
   158     REPORT(NS_LITERAL_CSTRING("mem/free"), memFree, NS_LITERAL_CSTRING(
   159 "Memory which is free and not being used for any purpose."));
   161     // Report reserved memory not included in memTotal.
   162     rv = CollectPmemReports(aHandleReport, aData);
   163     NS_ENSURE_SUCCESS(rv, rv);
   165     // Report zram usage statistics.
   166     rv = CollectZramReports(aHandleReport, aData);
   167     NS_ENSURE_SUCCESS(rv, rv);
   169     return rv;
   170   }
   172 private:
   173   // Keep this in sync with SystemReporter::kindPathSuffixes!
   174   enum ProcessSizeKind {
   175     AnonymousOutsideBrk  = 0,
   176     AnonymousBrkHeap     = 1,
   177     SharedLibrariesRX    = 2,
   178     SharedLibrariesRW    = 3,
   179     SharedLibrariesR     = 4,
   180     SharedLibrariesOther = 5,
   181     OtherFiles           = 6,
   182     MainThreadStack      = 7,
   183     Vdso                 = 8,
   185     ProcessSizeKindLimit = 9  // must be last
   186   };
   188   static const char* kindPathSuffixes[ProcessSizeKindLimit];
   190   // These are the cross-cutting measurements across all processes.
   191   struct ProcessSizes
   192   {
   193     ProcessSizes() { memset(this, 0, sizeof(*this)); }
   195     size_t mSizes[ProcessSizeKindLimit];
   196   };
   198   nsresult ReadMemInfo(int64_t* aMemTotal, int64_t* aMemFree)
   199   {
   200     FILE* f = fopen("/proc/meminfo", "r");
   201     if (!f) {
   202       return NS_ERROR_FAILURE;
   203     }
   205     int n1 = fscanf(f, "MemTotal: %" SCNd64 " kB\n", aMemTotal);
   206     int n2 = fscanf(f, "MemFree: %"  SCNd64 " kB\n", aMemFree);
   208     fclose(f);
   210     if (n1 != 1 || n2 != 1) {
   211       return NS_ERROR_FAILURE;
   212     }
   214     // Convert from KB to B.
   215     *aMemTotal *= 1024;
   216     *aMemFree  *= 1024;
   218     return NS_OK;
   219   }
   221   nsresult CollectProcessReports(nsIHandleReportCallback* aHandleReport,
   222                                  nsISupports* aData,
   223                                  int64_t* aTotalPss)
   224   {
   225     *aTotalPss = 0;
   226     ProcessSizes processSizes;
   228     DIR* d = opendir("/proc");
   229     if (NS_WARN_IF(!d)) {
   230       return NS_ERROR_FAILURE;
   231     }
   232     struct dirent* ent;
   233     while ((ent = readdir(d))) {
   234       struct stat statbuf;
   235       const char* pidStr = ent->d_name;
   236       // Don't check the return value of stat() -- it can return -1 for these
   237       // directories even when it has succeeded, apparently.
   238       stat(pidStr, &statbuf);
   239       if (S_ISDIR(statbuf.st_mode) && IsNumeric(pidStr)) {
   240         nsCString processName("process(");
   242         // Get the command name from cmdline.  If that fails, the pid is still
   243         // shown.
   244         nsPrintfCString cmdlinePath("/proc/%s/cmdline", pidStr);
   245         FILE* f = fopen(cmdlinePath.get(), "r");
   246         if (f) {
   247           static const size_t len = 256;
   248           char buf[len];
   249           if (fgets(buf, len, f)) {
   250             processName.Append(buf);
   251             // A hack: replace forward slashes with '\\' so they aren't treated
   252             // as path separators.  Consumers of this reporter (such as
   253             // about:memory) have to undo this change.
   254             processName.ReplaceChar('/', '\\');
   255             processName.Append(", ");
   256           }
   257           fclose(f);
   258         }
   259         processName.Append("pid=");
   260         processName.Append(pidStr);
   261         processName.Append(")");
   263         // Read the PSS values from the smaps file.
   264         nsPrintfCString smapsPath("/proc/%s/smaps", pidStr);
   265         f = fopen(smapsPath.get(), "r");
   266         if (!f) {
   267           // Processes can terminate between the readdir() call above and now,
   268           // so just skip if we can't open the file.
   269           continue;
   270         }
   271         while (true) {
   272           nsresult rv = ParseMapping(f, processName, aHandleReport, aData,
   273                                      &processSizes, aTotalPss);
   274           if (NS_FAILED(rv))
   275             break;
   276         }
   277         fclose(f);
   278       }
   279     }
   280     closedir(d);
   282     // Report the "processes/" tree.
   284     for (size_t i = 0; i < ProcessSizeKindLimit; i++) {
   285       nsAutoCString path("processes/");
   286       path.Append(kindPathSuffixes[i]);
   288       nsAutoCString desc("This is the sum of all processes' '");
   289       desc.Append(kindPathSuffixes[i]);
   290       desc.Append("' numbers.");
   292       REPORT(path, processSizes.mSizes[i], desc);
   293     }
   295     return NS_OK;
   296   }
   298   nsresult ParseMapping(FILE* aFile,
   299                         const nsACString& aProcessName,
   300                         nsIHandleReportCallback* aHandleReport,
   301                         nsISupports* aData,
   302                         ProcessSizes* aProcessSizes,
   303                         int64_t* aTotalPss)
   304   {
   305     // The first line of an entry in /proc/<pid>/smaps looks just like an entry
   306     // in /proc/<pid>/maps:
   307     //
   308     //   address           perms offset  dev   inode  pathname
   309     //   02366000-025d8000 rw-p 00000000 00:00 0      [heap]
   311     const int argCount = 8;
   313     unsigned long long addrStart, addrEnd;
   314     char perms[5];
   315     unsigned long long offset;
   316     // The 2.6 and 3.0 kernels allocate 12 bits for the major device number and
   317     // 20 bits for the minor device number.  Future kernels might allocate more.
   318     // 64 bits ought to be enough for anybody.
   319     char devMajor[17];
   320     char devMinor[17];
   321     unsigned int inode;
   322     char path[1025];
   324     // A path might not be present on this line; set it to the empty string.
   325     path[0] = '\0';
   327     // This is a bit tricky.  Whitespace in a scanf pattern matches *any*
   328     // whitespace, including newlines.  We want this pattern to match a line
   329     // with or without a path, but we don't want to look to a new line for the
   330     // path.  Thus we have %u%1024[^\n] at the end of the pattern.  This will
   331     // capture into the path some leading whitespace, which we'll later trim
   332     // off.
   333     int n = fscanf(aFile,
   334                    "%llx-%llx %4s %llx "
   335                    "%16[0-9a-fA-F]:%16[0-9a-fA-F] %u%1024[^\n]",
   336                    &addrStart, &addrEnd, perms, &offset, devMajor,
   337                    devMinor, &inode, path);
   339     // Eat up any whitespace at the end of this line, including the newline.
   340     unused << fscanf(aFile, " ");
   342     // We might or might not have a path, but the rest of the arguments should
   343     // be there.
   344     if (n != argCount && n != argCount - 1) {
   345       return NS_ERROR_FAILURE;
   346     }
   348     nsAutoCString name, description;
   349     ProcessSizeKind kind;
   350     GetReporterNameAndDescription(path, perms, name, description, &kind);
   352     while (true) {
   353       size_t pss = 0;
   354       nsresult rv = ParseMapBody(aFile, aProcessName, name, description,
   355                                  aHandleReport, aData, &pss);
   356       if (NS_FAILED(rv))
   357         break;
   359       // Increment the appropriate aProcessSizes values, and the total.
   360       aProcessSizes->mSizes[kind] += pss;
   361       *aTotalPss += pss;
   362     }
   364     return NS_OK;
   365   }
   367   void GetReporterNameAndDescription(const char* aPath,
   368                                      const char* aPerms,
   369                                      nsACString& aName,
   370                                      nsACString& aDesc,
   371                                      ProcessSizeKind* aProcessSizeKind)
   372   {
   373     aName.Truncate();
   374     aDesc.Truncate();
   376     // If aPath points to a file, we have its absolute path, plus some
   377     // whitespace.  Truncate this to its basename, and put the absolute path in
   378     // the description.
   379     nsAutoCString absPath;
   380     absPath.Append(aPath);
   381     absPath.StripChars(" ");
   383     nsAutoCString basename;
   384     GetBasename(absPath, basename);
   386     if (basename.EqualsLiteral("[heap]")) {
   387       aName.Append("anonymous/brk-heap");
   388       aDesc.Append("Memory in anonymous mappings within the boundaries "
   389                    "defined by brk() / sbrk().  This is likely to be just "
   390                    "a portion of the application's heap; the remainder "
   391                    "lives in other anonymous mappings. This corresponds to "
   392                    "'[heap]' in /proc/<pid>/smaps.");
   393       *aProcessSizeKind = AnonymousBrkHeap;
   395     } else if (basename.EqualsLiteral("[stack]")) {
   396       aName.Append("main-thread-stack");
   397       aDesc.Append("The stack size of the process's main thread.  This "
   398                    "corresponds to '[stack]' in /proc/<pid>/smaps.");
   399       *aProcessSizeKind = MainThreadStack;
   401     } else if (basename.EqualsLiteral("[vdso]")) {
   402       aName.Append("vdso");
   403       aDesc.Append("The virtual dynamically-linked shared object, also known "
   404                    "as the 'vsyscall page'. This is a memory region mapped by "
   405                    "the operating system for the purpose of allowing processes "
   406                    "to perform some privileged actions without the overhead of "
   407                    "a syscall.");
   408       *aProcessSizeKind = Vdso;
   410     } else if (!IsAnonymous(basename)) {
   411       nsAutoCString dirname;
   412       GetDirname(absPath, dirname);
   414       // Hack: A file is a shared library if the basename contains ".so" and
   415       // its dirname contains "/lib", or if the basename ends with ".so".
   416       if (EndsWithLiteral(basename, ".so") ||
   417           (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
   418         aName.Append("shared-libraries/");
   420         if (strncmp(aPerms, "r-x", 3) == 0) {
   421           *aProcessSizeKind = SharedLibrariesRX;
   422         } else if (strncmp(aPerms, "rw-", 3) == 0) {
   423           *aProcessSizeKind = SharedLibrariesRW;
   424         } else if (strncmp(aPerms, "r--", 3) == 0) {
   425           *aProcessSizeKind = SharedLibrariesR;
   426         } else {
   427           *aProcessSizeKind = SharedLibrariesOther;
   428         }
   430       } else {
   431         aName.Append("other-files/");
   432         if (EndsWithLiteral(basename, ".xpi")) {
   433           aName.Append("extensions/");
   434         } else if (dirname.Find("/fontconfig") != -1) {
   435           aName.Append("fontconfig/");
   436         }
   437         *aProcessSizeKind = OtherFiles;
   438       }
   440       aName.Append(basename);
   441       aDesc.Append(absPath);
   443     } else {
   444       aName.Append("anonymous/outside-brk");
   445       aDesc.Append("Memory in anonymous mappings outside the boundaries "
   446                    "defined by brk() / sbrk().");
   447       *aProcessSizeKind = AnonymousOutsideBrk;
   448     }
   450     aName.Append("/[");
   451     aName.Append(aPerms);
   452     aName.Append("]");
   454     // Append the permissions.  This is useful for non-verbose mode in
   455     // about:memory when the filename is long and goes of the right side of the
   456     // window.
   457     aDesc.Append(" [");
   458     aDesc.Append(aPerms);
   459     aDesc.Append("]");
   460   }
   462   nsresult ParseMapBody(
   463     FILE* aFile,
   464     const nsACString& aProcessName,
   465     const nsACString& aName,
   466     const nsACString& aDescription,
   467     nsIHandleReportCallback* aHandleReport,
   468     nsISupports* aData,
   469     size_t* aPss)
   470   {
   471     // Most of the lines in the body look like this:
   472     //
   473     // Size:                132 kB
   474     // Rss:                  20 kB
   475     // Pss:                  20 kB
   476     //
   477     // We're only interested in Pss.  In newer kernels, the last line in the
   478     // body has a different form:
   479     //
   480     // VmFlags: rd wr mr mw me dw ac
   481     //
   482     // The strings after "VmFlags: " vary.
   484     char desc[1025];
   485     int64_t sizeKB;
   486     int n = fscanf(aFile, "%1024[a-zA-Z_]: %" SCNd64 " kB\n", desc, &sizeKB);
   487     if (n == EOF || n == 0) {
   488       return NS_ERROR_FAILURE;
   489     } else if (n == 1 && strcmp(desc, "VmFlags") == 0) {
   490       // This is the "VmFlags:" line.  Chew up the rest of it.
   491       fscanf(aFile, "%*1024[a-z ]\n");
   492       return NS_ERROR_FAILURE;
   493     }
   495     // Only report "Pss" values.
   496     if (strcmp(desc, "Pss") == 0) {
   497       *aPss = sizeKB * 1024;
   499       // Don't report zero values.
   500       if (*aPss == 0) {
   501         return NS_OK;
   502       }
   504       nsAutoCString path("mem/processes/");
   505       path.Append(aProcessName);
   506       path.Append("/");
   507       path.Append(aName);
   509       REPORT(path, *aPss, aDescription);
   510     } else {
   511       *aPss = 0;
   512     }
   514     return NS_OK;
   515   }
   517   nsresult CollectPmemReports(nsIHandleReportCallback* aHandleReport,
   518                               nsISupports* aData)
   519   {
   520     // The pmem subsystem allocates physically contiguous memory for
   521     // interfacing with hardware.  In order to ensure availability,
   522     // this memory is reserved during boot, and allocations are made
   523     // within these regions at runtime.
   524     //
   525     // There are typically several of these pools allocated at boot.
   526     // The /sys/kernel/pmem_regions directory contains a subdirectory
   527     // for each one.  Within each subdirectory, the files we care
   528     // about are "size" (the total amount of physical memory) and
   529     // "mapped_regions" (a list of the current allocations within that
   530     // area).
   531     DIR* d = opendir("/sys/kernel/pmem_regions");
   532     if (!d) {
   533       if (NS_WARN_IF(errno != ENOENT)) {
   534         return NS_ERROR_FAILURE;
   535       }
   536       // If ENOENT, system doesn't use pmem.
   537       return NS_OK;
   538     }
   540     struct dirent* ent;
   541     while ((ent = readdir(d))) {
   542       const char* name = ent->d_name;
   543       uint64_t size;
   544       int scanned;
   546       // Skip "." and ".." (and any other dotfiles).
   547       if (name[0] == '.') {
   548         continue;
   549       }
   551       // Read the total size.  The file gives the size in decimal and
   552       // hex, in the form "13631488(0xd00000)"; we parse the former.
   553       nsPrintfCString sizePath("/sys/kernel/pmem_regions/%s/size", name);
   554       FILE* sizeFile = fopen(sizePath.get(), "r");
   555       if (NS_WARN_IF(!sizeFile)) {
   556         continue;
   557       }
   558       scanned = fscanf(sizeFile, "%" SCNu64, &size);
   559       if (NS_WARN_IF(scanned != 1)) {
   560         continue;
   561       }
   562       fclose(sizeFile);
   564       // Read mapped regions; format described below.
   565       uint64_t freeSize = size;
   566       nsPrintfCString regionsPath("/sys/kernel/pmem_regions/%s/mapped_regions",
   567                                   name);
   568       FILE* regionsFile = fopen(regionsPath.get(), "r");
   569       if (regionsFile) {
   570         static const size_t bufLen = 4096;
   571         char buf[bufLen];
   572         while (fgets(buf, bufLen, regionsFile)) {
   573           int pid;
   575           // Skip header line.
   576           if (strncmp(buf, "pid #", 5) == 0) {
   577             continue;
   578           }
   579           // Line format: "pid N:" + zero or more "(Start,Len) ".
   580           // N is decimal; Start and Len are in hex.
   581           scanned = sscanf(buf, "pid %d", &pid);
   582           if (NS_WARN_IF(scanned != 1)) {
   583             continue;
   584           }
   585           for (const char* nextParen = strchr(buf, '(');
   586                nextParen != nullptr;
   587                nextParen = strchr(nextParen + 1, '(')) {
   588             uint64_t mapStart, mapLen;
   590             scanned = sscanf(nextParen + 1, "%" SCNx64 ",%" SCNx64,
   591                              &mapStart, &mapLen);
   592             if (NS_WARN_IF(scanned != 2)) {
   593               break;
   594             }
   596             nsPrintfCString path("mem/pmem/used/%s/segment(pid=%d, "
   597                                  "offset=0x%" PRIx64 ")", name, pid, mapStart);
   598             nsPrintfCString desc("Physical memory reserved for the \"%s\" pool "
   599                                  "and allocated to a buffer.", name);
   600             REPORT_WITH_CLEANUP(path, UNITS_BYTES, mapLen, desc,
   601                                 (fclose(regionsFile), closedir(d)));
   602             freeSize -= mapLen;
   603           }
   604         }
   605         fclose(regionsFile);
   606       }
   608       nsPrintfCString path("mem/pmem/free/%s", name);
   609       nsPrintfCString desc("Physical memory reserved for the \"%s\" pool and "
   610                            "unavailable to the rest of the system, but not "
   611                            "currently allocated.", name);
   612       REPORT_WITH_CLEANUP(path, UNITS_BYTES, freeSize, desc, closedir(d));
   613     }
   614     closedir(d);
   615     return NS_OK;
   616   }
   618   uint64_t
   619   ReadSizeFromFile(const char* aFilename)
   620   {
   621       FILE* sizeFile = fopen(aFilename, "r");
   622       if (NS_WARN_IF(!sizeFile)) {
   623         return 0;
   624       }
   626       uint64_t size = 0;
   627       fscanf(sizeFile, "%" SCNu64, &size);
   628       fclose(sizeFile);
   630       return size;
   631   }
   633   nsresult
   634   CollectZramReports(nsIHandleReportCallback* aHandleReport,
   635                      nsISupports* aData)
   636   {
   637     // zram usage stats files can be found under:
   638     //  /sys/block/zram<id>
   639     //  |--> disksize        - Maximum amount of uncompressed data that can be
   640     //                         stored on the disk (bytes)
   641     //  |--> orig_data_size  - Uncompressed size of data in the disk (bytes)
   642     //  |--> compr_data_size - Compressed size of the data in the disk (bytes)
   643     //  |--> num_reads       - Number of attempted reads to the disk (count)
   644     //  |--> num_writes      - Number of attempted writes to the disk (count)
   645     //
   646     // Each file contains a single integer value in decimal form.
   648     DIR* d = opendir("/sys/block");
   649     if (!d) {
   650       if (NS_WARN_IF(errno != ENOENT)) {
   651         return NS_ERROR_FAILURE;
   652       }
   654       return NS_OK;
   655     }
   657     struct dirent* ent;
   658     while ((ent = readdir(d))) {
   659       const char* name = ent->d_name;
   661       // Skip non-zram entries.
   662       if (strncmp("zram", name, 4) != 0) {
   663         continue;
   664       }
   666       // Report disk size statistics.
   667       nsPrintfCString diskSizeFile("/sys/block/%s/disksize", name);
   668       nsPrintfCString origSizeFile("/sys/block/%s/orig_data_size", name);
   670       uint64_t diskSize = ReadSizeFromFile(diskSizeFile.get());
   671       uint64_t origSize = ReadSizeFromFile(origSizeFile.get());
   672       uint64_t unusedSize = diskSize - origSize;
   674       nsPrintfCString diskUsedPath("zram-disksize/%s/used", name);
   675       nsPrintfCString diskUsedDesc(
   676                            "The uncompressed size of data stored in \"%s.\" "
   677                            "This excludes zero-filled pages since "
   678                            "no memory is allocated for them.", name);
   679       REPORT_WITH_CLEANUP(diskUsedPath, UNITS_BYTES, origSize,
   680                           diskUsedDesc, closedir(d));
   682       nsPrintfCString diskUnusedPath("zram-disksize/%s/unused", name);
   683       nsPrintfCString diskUnusedDesc(
   684                            "The amount of uncompressed data that can still be "
   685                            "be stored in \"%s\"", name);
   686       REPORT_WITH_CLEANUP(diskUnusedPath, UNITS_BYTES, unusedSize,
   687                           diskUnusedDesc, closedir(d));
   689       // Report disk accesses.
   690       nsPrintfCString readsFile("/sys/block/%s/num_reads", name);
   691       nsPrintfCString writesFile("/sys/block/%s/num_writes", name);
   693       uint64_t reads = ReadSizeFromFile(readsFile.get());
   694       uint64_t writes = ReadSizeFromFile(writesFile.get());
   696       nsPrintfCString readsDesc(
   697                            "The number of reads (failed or successful) done on "
   698                            "\"%s\"", name);
   699       nsPrintfCString readsPath("zram-accesses/%s/reads", name);
   700       REPORT_WITH_CLEANUP(readsPath, UNITS_COUNT_CUMULATIVE, reads,
   701                           readsDesc, closedir(d));
   703       nsPrintfCString writesDesc(
   704                            "The number of writes (failed or successful) done "
   705                            "on \"%s\"", name);
   706       nsPrintfCString writesPath("zram-accesses/%s/writes", name);
   707       REPORT_WITH_CLEANUP(writesPath, UNITS_COUNT_CUMULATIVE, writes,
   708                           writesDesc, closedir(d));
   710       // Report compressed data size.
   711       nsPrintfCString comprSizeFile("/sys/block/%s/compr_data_size", name);
   712       uint64_t comprSize = ReadSizeFromFile(comprSizeFile.get());
   714       nsPrintfCString comprSizeDesc(
   715                            "The compressed size of data stored in \"%s\"",
   716                             name);
   717       nsPrintfCString comprSizePath("zram-compr-data-size/%s", name);
   718       REPORT_WITH_CLEANUP(comprSizePath, UNITS_BYTES, comprSize,
   719                           comprSizeDesc, closedir(d));
   720     }
   722     closedir(d);
   723     return NS_OK;
   724   }
   726 #undef REPORT
   727 };
   729 NS_IMPL_ISUPPORTS(SystemReporter, nsIMemoryReporter)
   731 // Keep this in sync with SystemReporter::ProcessSizeKind!
   732 const char* SystemReporter::kindPathSuffixes[] = {
   733     "anonymous/outside-brk",
   734     "anonymous/brk-heap",
   735     "shared-libraries/read-executable",
   736     "shared-libraries/read-write",
   737     "shared-libraries/read-only",
   738     "shared-libraries/other",
   739     "other-files",
   740     "main-thread-stack",
   741     "vdso"
   742 };
   744 void Init()
   745 {
   746   RegisterStrongMemoryReporter(new SystemReporter());
   747 }
   749 } // namespace SystemMemoryReporter
   750 } // namespace mozilla

mercurial