Bug 1038888 - Extend system memory reporter to report ion memory. r=njn
authorEric Rahm <erahm@mozilla.com>
Wed, 23 Jul 2014 16:36:27 -0700
changeset 195809 eeccb95d2e83d362001413b1ac442399746f0673
parent 195808 6712067462a977b77962fba6be7f259578c28efb
child 195810 9c3d8f8b46f7cddc778420daa47002afd708a7c1
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersnjn
bugs1038888
milestone34.0a1
Bug 1038888 - Extend system memory reporter to report ion memory. r=njn
xpcom/base/SystemMemoryReporter.cpp
--- a/xpcom/base/SystemMemoryReporter.cpp
+++ b/xpcom/base/SystemMemoryReporter.cpp
@@ -41,16 +41,36 @@
 
 namespace mozilla {
 namespace SystemMemoryReporter {
 
 #if !defined(XP_LINUX)
 #error "This won't work if we're not on Linux."
 #endif
 
+/**
+ * RAII helper that will close an open DIR handle.
+ */
+struct MOZ_STACK_CLASS AutoDir
+{
+  AutoDir(DIR* aDir) : mDir(aDir) {}
+  ~AutoDir() { if (mDir) closedir(mDir); };
+  DIR* mDir;
+};
+
+/**
+ * RAII helper that will close an open FILE handle.
+ */
+struct MOZ_STACK_CLASS AutoFile
+{
+  AutoFile(FILE* aFile) : mFile(aFile) {}
+  ~AutoFile() { if (mFile) fclose(mFile); }
+  FILE* mFile;
+};
+
 static bool
 EndsWithLiteral(const nsCString& aHaystack, const char* aNeedle)
 {
   int32_t idx = aHaystack.RFind(aNeedle);
   return idx != -1 && idx + strlen(aNeedle) == aHaystack.Length();
 }
 
 static void
@@ -175,16 +195,20 @@ public:
     // Report zram usage statistics.
     rv = CollectZramReports(aHandleReport, aData);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Report kgsl graphics memory usage.
     rv = CollectKgslReports(aHandleReport, aData);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // Report ION memory usage.
+    rv = CollectIonReports(aHandleReport, aData);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     return rv;
   }
 
 private:
   // These are the cross-cutting measurements across all processes.
   class ProcessSizes
   {
   public:
@@ -675,16 +699,116 @@ private:
                            "unavailable to the rest of the system, but not "
                            "currently allocated.", name);
       REPORT_WITH_CLEANUP(path, UNITS_BYTES, freeSize, desc, closedir(d));
     }
     closedir(d);
     return NS_OK;
   }
 
+  nsresult
+  CollectIonReports(nsIHandleReportCallback* aHandleReport,
+                    nsISupports* aData)
+  {
+    // ION is a replacement for PMEM (and other similar allocators).
+    //
+    // More details from http://lwn.net/Articles/480055/
+    //  "Like its PMEM-like predecessors, ION manages one or more memory pools,
+    //   some of which are set aside at boot time to combat fragmentation or to
+    //   serve special hardware needs. GPUs, display controllers, and cameras
+    //   are some of the hardware blocks that may have special memory
+    //   requirements."
+    //
+    // The file format starts as follows:
+    //     client              pid             size
+    //     ----------------------------------------------------
+    //     adsprpc-smd                1             4096
+    //     fd900000.qcom,mdss_mdp     1          1658880
+    //     ----------------------------------------------------
+    //     orphaned allocations (info is from last known client):
+    //     Homescreen            24100           294912 0 1
+    //     b2g                   23987          1658880 0 1
+    //     mdss_fb0                401          1658880 0 1
+    //     b2g                   23987             4096 0 1
+    //     Built-in Keyboa       24205            61440 0 1
+    //     ----------------------------------------------------
+    //     <other stuff>
+    //
+    // For our purposes we only care about the first portion of the file noted
+    // above which contains memory alloations (both sections). The term
+    // "orphaned" is misleading, it appears that every allocation not by the
+    // first process is considered orphaned on FxOS devices.
+
+    // The first three fields of each entry interest us:
+    //   1) client - Essentially the process name. We limit client names to 63
+    //               characters, in theory they should never be greater than 15
+    //               due to thread name length limitations.
+    //   2) pid    - The ID of the allocating process, read as a uint32_t.
+    //   3) size   - The size of the allocation in bytes, read as as a uint64_t.
+    const char* const kFormatString = "%63s %" SCNu32 " %" SCNu64;
+    const size_t kNumFields = 3;
+    const size_t kStringSize = 64;
+    const char* const kIonIommuPath = "/sys/kernel/debug/ion/iommu";
+
+    FILE* iommu = fopen(kIonIommuPath, "r");
+    if (!iommu) {
+      if (NS_WARN_IF(errno != ENOENT)) {
+        return NS_ERROR_FAILURE;
+      }
+      // If ENOENT, system doesn't use ION.
+      return NS_OK;
+    }
+
+    AutoFile iommuGuard(iommu);
+
+    const size_t kBufferLen = 256;
+    char buffer[kBufferLen];
+    char client[kStringSize];
+    uint32_t pid;
+    uint64_t size;
+
+    // Ignore the header line.
+    fgets(buffer, kBufferLen, iommu);
+
+    // Ignore the separator line.
+    fgets(buffer, kBufferLen, iommu);
+
+    const char* const kSep = "----";
+    const size_t kSepLen = 4;
+
+    // Read non-orphaned entries.
+    while (fgets(buffer, kBufferLen, iommu) &&
+           strncmp(kSep, buffer, kSepLen) != 0) {
+      if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
+        nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
+        REPORT(entryPath,
+               size,
+               NS_LITERAL_CSTRING("An ION kernel memory allocation."));
+      }
+    }
+
+    // Ignore the orphaned header.
+    fgets(buffer, kBufferLen, iommu);
+
+    // Read orphaned entries.
+    while (fgets(buffer, kBufferLen, iommu) &&
+           strncmp(kSep, buffer, kSepLen) != 0) {
+      if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
+        nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
+        REPORT(entryPath,
+               size,
+               NS_LITERAL_CSTRING("An ION kernel memory allocation."));
+      }
+    }
+
+    // Ignore the rest of the file.
+
+    return NS_OK;
+  }
+
   uint64_t
   ReadSizeFromFile(const char* aFilename)
   {
     FILE* sizeFile = fopen(aFilename, "r");
     if (NS_WARN_IF(!sizeFile)) {
       return 0;
     }
 
@@ -861,30 +985,16 @@ private:
             entryPath, UNITS_COUNT, 1, entryDescription, closedir(d));
       }
     }
 
     closedir(d);
     return NS_OK;
   }
 
-  struct AutoDir
-  {
-    AutoDir(DIR* aDir) : mDir(aDir) {}
-    ~AutoDir() { closedir(mDir); };
-    DIR* mDir;
-  };
-
-  struct AutoFile
-  {
-    AutoFile(FILE* aFile) : mFile(aFile) {}
-    ~AutoFile() { fclose(mFile); }
-    FILE* mFile;
-  };
-
   nsresult
   CollectKgslReports(nsIHandleReportCallback* aHandleReport,
                      nsISupports* aData)
   {
     // Each process that uses kgsl memory will have an entry under
     // /sys/kernel/debug/kgsl/proc/<pid>/mem. This file format includes a
     // header and then entries with types as follows:
     //   gpuaddr useraddr size id  flags type usage sglen