Bug 999473 - report number of open file descriptions (fd) in about:memory. r=njn
authorEric Rahm <erahm@mozilla.com>
Fri, 25 Apr 2014 13:00:15 -0700
changeset 181707 5fec1b48b68d08f8eb1cf379ad62863a58bac82f
parent 181706 660cc013d558742a47c47d2f8ef596fc38820aab
child 181708 4774bb540acc1c680c501072306879538838b4d1
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersnjn
bugs999473
milestone32.0a1
Bug 999473 - report number of open file descriptions (fd) in about:memory. r=njn
xpcom/base/SystemMemoryReporter.cpp
--- a/xpcom/base/SystemMemoryReporter.cpp
+++ b/xpcom/base/SystemMemoryReporter.cpp
@@ -270,16 +270,24 @@ private:
         }
         while (true) {
           nsresult rv = ParseMapping(f, processName, aHandleReport, aData,
                                      &processSizes, aTotalPss);
           if (NS_FAILED(rv))
             break;
         }
         fclose(f);
+
+        // Report the open file descriptors for this process.
+        nsPrintfCString procFdPath("/proc/%s/fd", pidStr);
+        nsresult rv = CollectOpenFileReports(
+                  aHandleReport, aData, procFdPath, processName);
+        if (NS_FAILED(rv)) {
+          break;
+        }
       }
     }
     closedir(d);
 
     // Report the "processes/" tree.
 
     for (size_t i = 0; i < ProcessSizeKindLimit; i++) {
       nsAutoCString path("processes/");
@@ -718,16 +726,94 @@ private:
       REPORT_WITH_CLEANUP(comprSizePath, UNITS_BYTES, comprSize,
                           comprSizeDesc, closedir(d));
     }
 
     closedir(d);
     return NS_OK;
   }
 
+  nsresult
+  CollectOpenFileReports(nsIHandleReportCallback* aHandleReport,
+                         nsISupports* aData,
+                         const nsACString& aProcPath,
+                         const nsACString& aProcessName)
+  {
+    // All file descriptors opened by a process are listed under
+    // /proc/<pid>/fd/<numerical_fd>. Each entry is a symlink that points to the
+    // path that was opened. This can be an actual file, a socket, a pipe, an
+    // anon_inode, or possibly an uncategorized device.
+    const char kFilePrefix[] = "/";
+    const char kSocketPrefix[] = "socket:";
+    const char kPipePrefix[] = "pipe:";
+    const char kAnonInodePrefix[] = "anon_inode:";
+
+    const nsCString procPath(aProcPath);
+    DIR* d = opendir(procPath.get());
+    if (!d) {
+      if (NS_WARN_IF(errno != ENOENT && errno != EACCES)) {
+        return NS_ERROR_FAILURE;
+      }
+      return NS_OK;
+    }
+
+    char linkPath[PATH_MAX + 1];
+    struct dirent* ent;
+    while ((ent = readdir(d))) {
+      const char* fd = ent->d_name;
+
+      // Skip "." and ".." (and any other dotfiles).
+      if (fd[0] == '.') {
+        continue;
+      }
+
+      nsPrintfCString fullPath("%s/%s", procPath.get(), fd);
+      ssize_t linkPathSize = readlink(fullPath.get(), linkPath, PATH_MAX);
+      if (linkPathSize > 0) {
+        linkPath[linkPathSize] = '\0';
+
+#define CHECK_PREFIX(prefix) \
+  (strncmp(linkPath, prefix, sizeof(prefix) - 1) == 0)
+
+        const char* category = nullptr;
+        const char* descriptionPrefix = nullptr;
+
+        if (CHECK_PREFIX(kFilePrefix)) {
+          category = "files"; // No trailing slash, the file path will have one
+          descriptionPrefix = "An open";
+        } else if (CHECK_PREFIX(kSocketPrefix)) {
+          category = "sockets/";
+          descriptionPrefix = "A socket";
+        } else if (CHECK_PREFIX(kPipePrefix)) {
+          category = "pipes/";
+          descriptionPrefix = "A pipe";
+        } else if (CHECK_PREFIX(kAnonInodePrefix)) {
+          category = "anon_inodes/";
+          descriptionPrefix = "An anon_inode";
+        } else {
+          category = "";
+          descriptionPrefix = "An uncategorized";
+        }
+
+#undef CHECK_PREFIX
+
+        const nsCString processName(aProcessName);
+        nsPrintfCString entryPath(
+            "open-fds/%s/%s%s/%s", processName.get(), category, linkPath, fd);
+        nsPrintfCString entryDescription(
+            "%s file descriptor opened by the process", descriptionPrefix);
+        REPORT_WITH_CLEANUP(
+            entryPath, UNITS_COUNT, 1, entryDescription, closedir(d));
+      }
+    }
+
+    closedir(d);
+    return NS_OK;
+  }
+
 #undef REPORT
 };
 
 NS_IMPL_ISUPPORTS(SystemReporter, nsIMemoryReporter)
 
 // Keep this in sync with SystemReporter::ProcessSizeKind!
 const char* SystemReporter::kindPathSuffixes[] = {
     "anonymous/outside-brk",