diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 512e477b8de..cfff8e6c27f 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -273,8 +273,7 @@ function appendElementWithText(aP, aTagName, aClassName, aText) //--------------------------------------------------------------------------- -const kTreeDescriptions = { - 'explicit' : +const explicitTreeDescription = "This tree covers explicit memory allocations by the application. It includes \ \n\n\ * allocations made at the operating system level (via calls to functions such as \ @@ -291,69 +290,7 @@ and thread stacks. \ \n\n\ 'explicit' is not guaranteed to cover every explicit allocation, but it does cover \ most (including the entire heap), and therefore it is the single best number to \ -focus on when trying to reduce memory usage.", - - 'rss': -"This tree shows how much space in physical memory each of the process's \ -mappings is currently using (the mapping's 'resident set size', or 'RSS'). \ -This is a good measure of the 'cost' of the mapping, although it does not \ -take into account the fact that shared libraries may be mapped by multiple \ -processes but appear only once in physical memory. \ -\n\n\ -Note that the 'rss' value here might not equal the value for 'resident' \ -under 'Other Measurements' because the two measurements are not taken at \ -exactly the same time.", - - 'pss': -"This tree shows how much space in physical memory can be 'blamed' on this \ -process. For each mapping, its 'proportional set size' (PSS) is the \ -mapping's resident size divided by the number of processes which use the \ -mapping. So if a mapping is private to this process, its PSS should equal \ -its RSS. But if a mapping is shared between three processes, its PSS in each \ -of the processes would be 1/3 its RSS.", - - 'size': -"This tree shows how much virtual addres space each of the process's mappings \ -takes up (a.k.a. the mapping's 'vsize'). A mapping may have a large size but use \ -only a small amount of physical memory; the resident set size of a mapping is \ -a better measure of the mapping's 'cost'. \ -\n\n\ -Note that the 'size' value here might not equal the value for 'vsize' under \ -'Other Measurements' because the two measurements are not taken at exactly \ -the same time.", - - 'swap': -"This tree shows how much space in the swap file each of the process's \ -mappings is currently using. Mappings which are not in the swap file (i.e., \ -nodes which would have a value of 0 in this tree) are omitted." -}; - -const kSectionNames = { - 'explicit': 'Explicit Allocations', - 'rss': 'Resident Set Size (RSS) Breakdown', - 'pss': 'Proportional Set Size (PSS) Breakdown', - 'size': 'Virtual Size Breakdown', - 'swap': 'Swap Breakdown', - 'other': 'Other Measurements' -}; - -const kSmapsTreeNames = ['rss', 'pss', 'size', 'swap' ]; -const kSmapsTreePrefixes = ['rss/', 'pss/', 'size/', 'swap/']; - -function isExplicitPath(aUnsafePath) -{ - return aUnsafePath.startsWith("explicit/"); -} - -function isSmapsPath(aUnsafePath) -{ - for (let i = 0; i < kSmapsTreePrefixes.length; i++) { - if (aUnsafePath.startsWith(kSmapsTreePrefixes[i])) { - return true; - } - } - return false; -} +focus on when trying to reduce memory usage."; //--------------------------------------------------------------------------- @@ -546,8 +483,7 @@ function updateAboutMemoryFromReporters() try { // Process the reports from the memory reporters. - appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize, - /* forceShowSmaps = */ false); + appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize); } catch (ex) { handleException(ex); @@ -576,8 +512,7 @@ function updateAboutMemoryFromJSONObject(aObj) let process = function(aIgnoreReporter, aIgnoreReport, aHandleReport) { processMemoryReportsFromFile(aObj.reports, aIgnoreReport, aHandleReport); } - appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize, - /* forceShowSmaps = */ true); + appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize); } catch (ex) { handleException(ex); } @@ -765,6 +700,8 @@ DReport.prototype = { // // In those cases, we just use the description from the first-encountered // one, which is what about:memory also does. + // (Note: reports with those paths are no longer generated, but allowing + // the descriptions to differ seems reasonable.) }, merge: function(aJr) { @@ -930,14 +867,10 @@ function PColl() * file. * @param aHasMozMallocUsableSize * Boolean indicating if moz_malloc_usable_size works. - * @param aForceShowSmaps - * True if we should show the smaps memory reporters even if we're not - * in verbose mode. */ -function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize, - aForceShowSmaps) +function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize) { - let pcollsByProcess = getPCollsByProcess(aProcessReports, aForceShowSmaps); + let pcollsByProcess = getPCollsByProcess(aProcessReports); // Sort the processes. let processes = Object.keys(pcollsByProcess); @@ -998,12 +931,9 @@ function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize, * @param aProcessReports * Function that extracts the memory reports from the reporters or from * file. - * @param aForceShowSmaps - * True if we should show the smaps memory reporters even if we're not - * in verbose mode. * @return The table of PColls by process. */ -function getPCollsByProcess(aProcessReports, aForceShowSmaps) +function getPCollsByProcess(aProcessReports) { let pcollsByProcess = {}; @@ -1012,41 +942,29 @@ function getPCollsByProcess(aProcessReports, aForceShowSmaps) // be in parentheses, so a ')' might appear after the '.'.) const gSentenceRegExp = /^[A-Z].*\.\)?$/m; - // Ignore the "smaps" reporter in non-verbose mode unless we're reading from - // a file or the clipboard. (Note that reports from these reporters can - // reach here via a "content-child" reporter if they were in a child - // process.) - // // Ignore any "redundant/"-prefixed reporters and reports, which are only // used by telemetry. function ignoreReporter(aName) { - return (aName === "smaps" && !gVerbose.checked && !aForceShowSmaps) || - aName.startsWith("redundant/"); + return aName.startsWith("redundant/"); } function ignoreReport(aUnsafePath) { - return (isSmapsPath(aUnsafePath) && !gVerbose.checked && !aForceShowSmaps) || - aUnsafePath.startsWith("redundant/"); + return aUnsafePath.startsWith("redundant/"); } function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, aDescription, aPresence) { - if (isExplicitPath(aUnsafePath)) { + if (aUnsafePath.startsWith("explicit/")) { assertInput(aKind === KIND_HEAP || aKind === KIND_NONHEAP, "bad explicit kind"); assertInput(aUnits === UNITS_BYTES, "bad explicit units"); assertInput(gSentenceRegExp.test(aDescription), "non-sentence explicit description"); - } else if (isSmapsPath(aUnsafePath)) { - assertInput(aKind === KIND_NONHEAP, "bad smaps kind"); - assertInput(aUnits === UNITS_BYTES, "bad smaps units"); - assertInput(aDescription !== "", "empty smaps description"); - } else { assertInput(gSentenceRegExp.test(aDescription), "non-sentence other description"); @@ -1434,7 +1352,7 @@ function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates, let hasKnownHeapAllocated; { let treeName = "explicit"; - let pre = appendSectionHeader(aP, kSectionNames[treeName]); + let pre = appendSectionHeader(aP, "Explicit Allocations"); let t = aTrees[treeName]; if (t) { fillInTree(t); @@ -1442,31 +1360,13 @@ function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates, aDegenerates && addHeapUnclassifiedNode(t, aDegenerates["heap-allocated"], aHeapTotal); sortTreeAndInsertAggregateNodes(t._amount, t); - t._description = kTreeDescriptions[treeName]; + t._description = explicitTreeDescription; appendTreeElements(pre, t, aProcess, ""); delete aTrees[treeName]; } appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste } - // The smaps trees, which are only present in aTrees in verbose mode or when - // we're reading from a file or the clipboard. - kSmapsTreeNames.forEach(function(aTreeName) { - // |t| will be undefined if we don't have any reports for the given - // unsafePath. - let t = aTrees[aTreeName]; - if (t) { - let pre = appendSectionHeader(aP, kSectionNames[aTreeName]); - fillInTree(t); - sortTreeAndInsertAggregateNodes(t._amount, t); - t._description = kTreeDescriptions[aTreeName]; - t._hideKids = true; // smaps trees are always initially collapsed - appendTreeElements(pre, t, aProcess, ""); - delete aTrees[aTreeName]; - appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste - } - }); - // Fill in and sort all the non-degenerate other trees. let otherTrees = []; for (let unsafeName in aTrees) { @@ -1494,7 +1394,7 @@ function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates, otherDegenerates.sort(TreeNode.compareUnsafeNames); // Now generate the elements, putting non-degenerate trees first. - let pre = appendSectionHeader(aP, kSectionNames['other']); + let pre = appendSectionHeader(aP, "Other Measurements"); for (let i = 0; i < otherTrees.length; i++) { let t = otherTrees[i]; appendTreeElements(pre, t, aProcess, ""); diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index 646fb8ee8f4..0f7de2cea4e 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -116,21 +116,6 @@ f("compartments/user/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789", OTHER, COUNT, 1); } }, - { name: "smaps", - collectReports: function(aCbObj, aClosure) { - // The amounts are given in pages, so multiply here by 4kb. - function f(aP, aA) { - aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure); - } - f("size/a", 24); - f("swap/a", 1); - f("swap/a", 2); - f("size/a", 19); - f("swap/b/c", 10); - f("rss/a", 42); - f("pss/a", 43); - } - }, { name: "compartments", collectReports: function(aCbObj, aClosure) { function f(aP) { @@ -189,12 +174,6 @@ HEAP, BYTES,200 * MB); f("2nd", "other0", OTHER, BYTES,666 * MB); f("2nd", "other1", OTHER, BYTES,111 * MB); - // If the "smaps" reporter is in a child process it'll be passed to - // the main process under a different name. The fact that we skip - // the "smaps" reporter in the main process won't cause these - // to be skipped; the report-level skipping will be hit instead. - f("2nd", "size/e", NONHEAP, BYTES,24*4*KB); - f("2nd", "size/f", NONHEAP, BYTES,24*4*KB); f("2nd", "redundant/blah", NONHEAP, BYTES,24*4*KB); // ignored! // Check that we can handle "heap-allocated" not being present. @@ -437,22 +416,6 @@ Explicit Allocations\n\ ├──────510,976 B (00.08%) ── d\n\ └──────102,400 B (00.02%) ── e\n\ \n\ -Resident Set Size (RSS) Breakdown\n\ -\n\ -172,032 B (100.0%) ++ rss\n\ -\n\ -Proportional Set Size (PSS) Breakdown\n\ -\n\ -176,128 B (100.0%) ++ pss\n\ -\n\ -Virtual Size Breakdown\n\ -\n\ -176,128 B (100.0%) ++ size\n\ -\n\ -Swap Breakdown\n\ -\n\ -53,248 B (100.0%) ++ swap\n\ -\n\ Other Measurements\n\ \n\ 5 (100.0%) -- compartments\n\ @@ -549,10 +512,6 @@ Explicit Allocations\n\ ├────209,715,200 B (20.00%) ── flip/the/backslashes\n\ └────105,906,176 B (10.10%) ── heap-unclassified\n\ \n\ -Virtual Size Breakdown\n\ -\n\ -196,608 B (100.0%) ++ size\n\ -\n\ Other Measurements\n\ \n\ 1,048,576,000 B ── heap-allocated\n\ diff --git a/xpcom/base/MapsMemoryReporter.cpp b/xpcom/base/MapsMemoryReporter.cpp deleted file mode 100644 index 8815dda6402..00000000000 --- a/xpcom/base/MapsMemoryReporter.cpp +++ /dev/null @@ -1,578 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/Util.h" - -#include "mozilla/MapsMemoryReporter.h" -#include "nsIMemoryReporter.h" -#include "nsString.h" -#include "nsCOMPtr.h" -#include "nsTHashtable.h" -#include "nsHashKeys.h" -#include "mozilla/Attributes.h" -#include "mozilla/unused.h" -#include - -namespace mozilla { -namespace MapsMemoryReporter { - -#if !defined(XP_LINUX) -#error "This doesn't have a prayer of working if we're not on Linux." -#endif - -// mozillaLibraries is a list of all the shared libraries we build. This list -// is used for determining whether a library is a "Mozilla library" or a -// "third-party library". But even if this list is missing items, about:memory -// will identify a library in the same directory as libxul.so as a "Mozilla -// library". -const char* mozillaLibraries[] = -{ - "libfreebl3.so", - "libmozalloc.so", - "libmozsqlite3.so", - "libnspr4.so", - "libnss3.so", - "libnssckbi.so", - "libnssdbm3.so", - "libnssutil3.so", - "libplc4.so", - "libplds4.so", - "libsmime3.so", - "libsoftokn3.so", - "libssl3.so", - "libxpcom.so", - "libxul.so" -}; - -namespace { - -bool EndsWithLiteral(const nsCString &aHaystack, const char *aNeedle) -{ - int32_t idx = aHaystack.RFind(aNeedle); - if (idx == -1) { - return false; - } - - return idx + strlen(aNeedle) == aHaystack.Length(); -} - -void GetDirname(const nsCString &aPath, nsACString &aOut) -{ - int32_t idx = aPath.RFind("/"); - if (idx == -1) { - aOut.Truncate(); - } - else { - aOut.Assign(Substring(aPath, 0, idx)); - } -} - -void GetBasename(const nsCString &aPath, nsACString &aOut) -{ - nsCString out; - int32_t idx = aPath.RFind("/"); - if (idx == -1) { - out.Assign(aPath); - } - else { - out.Assign(Substring(aPath, idx + 1)); - } - - // On Android, some entries in /dev/ashmem end with "(deleted)" (e.g. - // "/dev/ashmem/libxul.so(deleted)"). We don't care about this modifier, so - // cut it off when getting the entry's basename. - if (EndsWithLiteral(out, "(deleted)")) { - out.Assign(Substring(out, 0, out.RFind("(deleted)"))); - } - out.StripChars(" "); - - aOut.Assign(out); -} - -// MapsReporter::CollectReports uses this stuct to keep track of whether it's -// seen a mapping under 'rss', 'pss', 'size', and 'swap'. -struct CategoriesSeen { - CategoriesSeen() : - mSeenRss(false), - mSeenPss(false), - mSeenSize(false), - mSeenSwap(false) - { - } - - bool mSeenRss; - bool mSeenPss; - bool mSeenSize; - bool mSeenSwap; -}; - -} // anonymous namespace - -class MapsReporter MOZ_FINAL : public nsIMemoryReporter -{ -public: - MapsReporter(); - - NS_DECL_THREADSAFE_ISUPPORTS - - NS_IMETHOD GetName(nsACString &aName) - { - aName.AssignLiteral("smaps"); - return NS_OK; - } - - NS_IMETHOD - CollectReports(nsIMemoryReporterCallback *aCb, - nsISupports *aClosure); - -private: - // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the - // the directory containing libxul. - nsresult FindLibxul(); - - nsresult - ParseMapping(FILE *aFile, - nsIMemoryReporterCallback *aCb, - nsISupports *aClosure, - CategoriesSeen *aCategoriesSeen); - - void - GetReporterNameAndDescription(const char *aPath, - const char *aPermissions, - nsACString &aName, - nsACString &aDesc); - - nsresult - ParseMapBody(FILE *aFile, - const nsACString &aName, - const nsACString &aDescription, - nsIMemoryReporterCallback *aCb, - nsISupports *aClosure, - CategoriesSeen *aCategoriesSeen); - - bool mSearchedForLibxul; - nsCString mLibxulDir; - nsTHashtable mMozillaLibraries; -}; - -NS_IMPL_ISUPPORTS1(MapsReporter, nsIMemoryReporter) - -MapsReporter::MapsReporter() - : mSearchedForLibxul(false) - , mMozillaLibraries(ArrayLength(mozillaLibraries)) -{ - const uint32_t len = ArrayLength(mozillaLibraries); - for (uint32_t i = 0; i < len; i++) { - nsAutoCString str; - str.Assign(mozillaLibraries[i]); - mMozillaLibraries.PutEntry(str); - } -} - -NS_IMETHODIMP -MapsReporter::CollectReports(nsIMemoryReporterCallback *aCb, - nsISupports *aClosure) -{ - CategoriesSeen categoriesSeen; - - FILE *f = fopen("/proc/self/smaps", "r"); - if (!f) - return NS_ERROR_FAILURE; - - while (true) { - nsresult rv = ParseMapping(f, aCb, aClosure, &categoriesSeen); - if (NS_FAILED(rv)) - break; - } - - fclose(f); - - // For sure we should have created some node under 'rss' and - // 'size'; otherwise we're probably not reading smaps correctly. If we - // didn't create a node under 'swap', create one here so about:memory - // knows to create an empty 'swap' tree; it needs a 'total' child because - // about:memory expects at least one report whose path begins with 'swap/'. - - NS_ASSERTION(categoriesSeen.mSeenSize, "Didn't create a size node?"); - NS_ASSERTION(categoriesSeen.mSeenRss, "Didn't create a rss node?"); - NS_ASSERTION(categoriesSeen.mSeenPss, "Didn't create a pss node?"); - if (!categoriesSeen.mSeenSwap) { - nsresult rv; - rv = aCb->Callback(NS_LITERAL_CSTRING(""), - NS_LITERAL_CSTRING("swap/total"), - nsIMemoryReporter::KIND_NONHEAP, - nsIMemoryReporter::UNITS_BYTES, - 0, - NS_LITERAL_CSTRING("This process uses no swap space."), - aClosure); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -MapsReporter::FindLibxul() -{ - if (mSearchedForLibxul) - return NS_OK; - - mSearchedForLibxul = true; - - mLibxulDir.Truncate(); - - // Note that we're scanning /proc/self/*maps*, not smaps, here. - FILE *f = fopen("/proc/self/maps", "r"); - if (!f) { - return NS_ERROR_FAILURE; - } - - while (true) { - // Skip any number of non-slash characters, then capture starting with the - // slash to the newline. This is the path part of /proc/self/maps. - char path[1025]; - int numRead = fscanf(f, "%*[^/]%1024[^\n]", path); - if (numRead != 1) { - break; - } - - nsAutoCString pathStr; - pathStr.Append(path); - - nsAutoCString basename; - GetBasename(pathStr, basename); - - if (basename.EqualsLiteral("libxul.so")) { - GetDirname(pathStr, mLibxulDir); - break; - } - } - - fclose(f); - return mLibxulDir.IsEmpty() ? NS_ERROR_FAILURE : NS_OK; -} - -nsresult -MapsReporter::ParseMapping( - FILE *aFile, - nsIMemoryReporterCallback *aCb, - nsISupports *aClosure, - CategoriesSeen *aCategoriesSeen) -{ - // We need to use native types in order to get good warnings from fscanf, so - // let's make sure that the native types have the sizes we expect. - static_assert(sizeof(long long) == sizeof(int64_t), - "size of (long long) is expected to match (int64_t)"); - static_assert(sizeof(int) == sizeof(int32_t), - "size of (int) is expected to match (int32_t)"); - - // Don't bail if FindLibxul fails. We can still gather meaningful stats - // here. - FindLibxul(); - - // The first line of an entry in /proc/self/smaps looks just like an entry - // in /proc/maps: - // - // address perms offset dev inode pathname - // 02366000-025d8000 rw-p 00000000 00:00 0 [heap] - - const int argCount = 8; - - unsigned long long addrStart, addrEnd; - char perms[5]; - unsigned long long offset; - // The 2.6 and 3.0 kernels allocate 12 bits for the major device number and - // 20 bits for the minor device number. Future kernels might allocate more. - // 64 bits ought to be enough for anybody. - char devMajor[17]; - char devMinor[17]; - unsigned int inode; - char path[1025]; - - // A path might not be present on this line; set it to the empty string. - path[0] = '\0'; - - // This is a bit tricky. Whitespace in a scanf pattern matches *any* - // whitespace, including newlines. We want this pattern to match a line - // with or without a path, but we don't want to look to a new line for the - // path. Thus we have %u%1024[^\n] at the end of the pattern. This will - // capture into the path some leading whitespace, which we'll later trim off. - int numRead = fscanf(aFile, - "%llx-%llx %4s %llx " - "%16[0-9a-fA-F]:%16[0-9a-fA-F] %u%1024[^\n]", - &addrStart, &addrEnd, perms, &offset, devMajor, - devMinor, &inode, path); - - // Eat up any whitespace at the end of this line, including the newline. - unused << fscanf(aFile, " "); - - // We might or might not have a path, but the rest of the arguments should be - // there. - if (numRead != argCount && numRead != argCount - 1) { - return NS_ERROR_FAILURE; - } - - nsAutoCString name, description; - GetReporterNameAndDescription(path, perms, name, description); - - while (true) { - nsresult rv = ParseMapBody(aFile, name, description, aCb, - aClosure, aCategoriesSeen); - if (NS_FAILED(rv)) - break; - } - - return NS_OK; -} - -static bool -IsAnonymous(const nsACString &aName) -{ - // Recent kernels (e.g. 3.5) have multiple [stack:nnnn] entries, where |nnnn| - // is a thread ID. However, [stack:nnnn] entries count both stack memory - // *and* anonymous memory because the kernel only knows about the start of - // each thread stack, not its end. So we treat such entries as anonymous - // memory instead of stack. This is consistent with older kernels that don't - // even show [stack:nnnn] entries. - return aName.IsEmpty() || - StringBeginsWith(aName, NS_LITERAL_CSTRING("[stack:")); -} - -void -MapsReporter::GetReporterNameAndDescription( - const char *aPath, - const char *aPerms, - nsACString &aName, - nsACString &aDesc) -{ - aName.Truncate(); - aDesc.Truncate(); - - // If aPath points to a file, we have its absolute path, plus some - // whitespace. Truncate this to its basename, and put the absolute path in - // the description. - nsAutoCString absPath; - absPath.Append(aPath); - absPath.StripChars(" "); - - nsAutoCString basename; - GetBasename(absPath, basename); - - if (basename.EqualsLiteral("[heap]")) { - aName.Append("anonymous/anonymous, within brk()"); - aDesc.Append("Memory in anonymous mappings within the boundaries " - "defined by brk() / sbrk(). This is likely to be just " - "a portion of the application's heap; the remainder " - "lives in other anonymous mappings. This node corresponds to " - "'[heap]' in /proc/self/smaps."); - } - else if (basename.EqualsLiteral("[stack]")) { - aName.Append("main thread's stack"); - aDesc.Append("The stack size of the process's main thread. This node " - "corresponds to '[stack]' in /proc/self/smaps."); - } - else if (basename.EqualsLiteral("[vdso]")) { - aName.Append("vdso"); - aDesc.Append("The virtual dynamically-linked shared object, also known as " - "the 'vsyscall page'. This is a memory region mapped by the " - "operating system for the purpose of allowing processes to " - "perform some privileged actions without the overhead of a " - "syscall."); - } - else if (!IsAnonymous(basename)) { - nsAutoCString dirname; - GetDirname(absPath, dirname); - - // Hack: A file is a shared library if the basename contains ".so" and its - // dirname contains "/lib", or if the basename ends with ".so". - if (EndsWithLiteral(basename, ".so") || - (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) { - aName.Append("shared-libraries/"); - if ((!mLibxulDir.IsEmpty() && dirname.Equals(mLibxulDir)) || - mMozillaLibraries.Contains(basename)) { - aName.Append("shared-libraries-mozilla/"); - } - else { - aName.Append("shared-libraries-other/"); - } - } - else { - aName.Append("other-files/"); - if (EndsWithLiteral(basename, ".xpi")) { - aName.Append("extensions/"); - } - else if (dirname.Find("/fontconfig") != -1) { - aName.Append("fontconfig/"); - } - } - - aName.Append(basename); - aDesc.Append(absPath); - } - else { - aName.Append("anonymous/anonymous, outside brk()"); - aDesc.Append("Memory in anonymous mappings outside the boundaries defined " - "by brk() / sbrk()."); - } - - aName.Append("/["); - aName.Append(aPerms); - aName.Append("]"); - - // Modify the description to include an explanation of the permissions. - aDesc.Append(" ("); - if (strstr(aPerms, "rw")) { - aDesc.Append("read/write, "); - } - else if (strchr(aPerms, 'r')) { - aDesc.Append("read-only, "); - } - else if (strchr(aPerms, 'w')) { - aDesc.Append("write-only, "); - } - else { - aDesc.Append("not readable, not writable, "); - } - - if (strchr(aPerms, 'x')) { - aDesc.Append("executable, "); - } - else { - aDesc.Append("not executable, "); - } - - if (strchr(aPerms, 's')) { - aDesc.Append("shared"); - } - else if (strchr(aPerms, 'p')) { - aDesc.Append("private"); - } - else { - aDesc.Append("not shared or private??"); - } - aDesc.Append(")"); -} - -nsresult -MapsReporter::ParseMapBody( - FILE *aFile, - const nsACString &aName, - const nsACString &aDescription, - nsIMemoryReporterCallback *aCb, - nsISupports *aClosure, - CategoriesSeen *aCategoriesSeen) -{ - static_assert(sizeof(long long) == sizeof(int64_t), - "size of (long long) is expected to match (int64_t)"); - - const int argCount = 2; - - char desc[1025]; - unsigned long long size; - if (fscanf(aFile, "%1024[a-zA-Z_]: %llu kB\n", - desc, &size) != argCount) { - return NS_ERROR_FAILURE; - } - - // Don't report nodes with size 0. - if (size == 0) - return NS_OK; - - const char* category; - if (strcmp(desc, "Size") == 0) { - category = "size"; - aCategoriesSeen->mSeenSize = true; - } - else if (strcmp(desc, "Rss") == 0) { - category = "rss"; - aCategoriesSeen->mSeenRss = true; - } - else if (strcmp(desc, "Pss") == 0) { - category = "pss"; - aCategoriesSeen->mSeenPss = true; - } - else if (strcmp(desc, "Swap") == 0) { - category = "swap"; - aCategoriesSeen->mSeenSwap = true; - } - else { - // Don't report this category. - return NS_OK; - } - - nsAutoCString path; - path.Append(category); - path.Append("/"); - path.Append(aName); - - nsresult rv; - rv = aCb->Callback(NS_LITERAL_CSTRING(""), - path, - nsIMemoryReporter::KIND_NONHEAP, - nsIMemoryReporter::UNITS_BYTES, - int64_t(size) * 1024, // convert from kB to bytes - aDescription, aClosure); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -class ResidentUniqueReporter MOZ_FINAL : public MemoryUniReporter -{ -public: - ResidentUniqueReporter() - : MemoryUniReporter("resident-unique", KIND_OTHER, UNITS_BYTES, -"Memory mapped by the process that is present in physical memory and not " -"shared with any other processes. This is also known as the process's unique " -"set size (USS). This is the amount of RAM we'd expect to be freed if we " -"closed this process.") - {} - -private: - NS_IMETHOD GetAmount(int64_t *aAmount) - { - // You might be tempted to calculate USS by subtracting the "shared" value - // from the "resident" value in /proc//statm. But at least on Linux, - // statm's "shared" value actually counts pages backed by files, which has - // little to do with whether the pages are actually shared. smaps on the - // other hand appears to give us the correct information. - // - // We could calculate this data within the smaps reporter, but the overhead - // of the smaps reporter is considerable (we don't even run the smaps - // reporter in normal about:memory operation). Hopefully this - // implementation is fast enough not to matter. - - *aAmount = 0; - - FILE *f = fopen("/proc/self/smaps", "r"); - NS_ENSURE_STATE(f); - - int64_t total = 0; - char line[256]; - while (fgets(line, sizeof(line), f)) { - long long val = 0; - if (sscanf(line, "Private_Dirty: %lld kB", &val) == 1 || - sscanf(line, "Private_Clean: %lld kB", &val) == 1) { - total += val * 1024; // convert from kB to bytes - } - } - *aAmount = total; - - fclose(f); - return NS_OK; - } -}; - -void Init() -{ - nsCOMPtr reporter = new MapsReporter(); - NS_RegisterMemoryReporter(reporter); - - NS_RegisterMemoryReporter(new ResidentUniqueReporter()); -} - -} // namespace MapsMemoryReporter -} // namespace mozilla diff --git a/xpcom/base/MapsMemoryReporter.h b/xpcom/base/MapsMemoryReporter.h deleted file mode 100644 index 423385074c8..00000000000 --- a/xpcom/base/MapsMemoryReporter.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 ci et: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_MapsMemoryReporter_h_ -#define mozilla_MapsMemoryReporter_h_ - -namespace mozilla { -namespace MapsMemoryReporter { - -// This only works on Linux, but to make callers' lives easier, we stub out -// empty functions on other platforms. - -#if defined(XP_LINUX) - void Init(); -#else - void Init() {} -#endif - -} // namespace MapsMemoryReporter -} // namespace mozilla - -#endif diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index 21a18ed4929..f6ee2ad7d4e 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -75,7 +75,6 @@ EXPORTS.mozilla += [ 'AvailableMemoryTracker.h', 'ClearOnShutdown.h', 'CycleCollectedJSRuntime.h', - 'MapsMemoryReporter.h', 'StackWalk.h', 'StaticMutex.h', 'StaticPtr.h', @@ -108,11 +107,6 @@ CPP_SOURCES += [ 'nsVersionComparatorImpl.cpp', ] -if CONFIG['OS_ARCH'] == 'Linux': - CPP_SOURCES += [ - 'MapsMemoryReporter.cpp', - ] - if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': CPP_SOURCES += [ 'nsMacUtilsImpl.cpp', diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 4b3c7afceec..cceff3d25c4 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -143,11 +143,6 @@ interface nsIMemoryReporterCallback : nsISupports * description that is a sentence (i.e. starts with a capital letter and * ends with a period, or similar). * - * - The "size", "rss", "pss" and "swap" trees are optional. They - * represent regions of virtual memory that the process has mapped. - * Reporters in this category must have kind NONHEAP, units BYTES, and a - * non-empty description. - * * - The "redundant" tree is optional, and can be used for reports that are * redundant w.r.t. other reports. These are useful for telemetry, and are * not shown in about:memory. Reports in this tree are entirely diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index ff7790a1852..bc1074fc6ff 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -73,6 +73,49 @@ static nsresult GetResidentFast(int64_t* aN) return GetResident(aN); } +#define HAVE_RESIDENT_UNIQUE_REPORTER +class ResidentUniqueReporter MOZ_FINAL : public MemoryUniReporter +{ +public: + ResidentUniqueReporter() + : MemoryUniReporter("resident-unique", KIND_OTHER, UNITS_BYTES, +"Memory mapped by the process that is present in physical memory and not " +"shared with any other processes. This is also known as the process's unique " +"set size (USS). This is the amount of RAM we'd expect to be freed if we " +"closed this process.") + {} + +private: + NS_IMETHOD GetAmount(int64_t *aAmount) + { + // You might be tempted to calculate USS by subtracting the "shared" value + // from the "resident" value in /proc//statm. But at least on Linux, + // statm's "shared" value actually counts pages backed by files, which has + // little to do with whether the pages are actually shared. + // /proc/self/smaps on the other hand appears to give us the correct + // information. + + *aAmount = 0; + + FILE *f = fopen("/proc/self/smaps", "r"); + NS_ENSURE_STATE(f); + + int64_t total = 0; + char line[256]; + while (fgets(line, sizeof(line), f)) { + long long val = 0; + if (sscanf(line, "Private_Dirty: %lld kB", &val) == 1 || + sscanf(line, "Private_Clean: %lld kB", &val) == 1) { + total += val * 1024; // convert from kB to bytes + } + } + *aAmount = total; + + fclose(f); + return NS_OK; + } +}; + #elif defined(__DragonFly__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) @@ -718,6 +761,10 @@ nsMemoryReporterManager::Init() RegisterReporter(new ResidentFastReporter); #endif +#ifdef HAVE_RESIDENT_UNIQUE_REPORTER + RegisterReporter(new ResidentUniqueReporter); +#endif + #ifdef HAVE_PAGE_FAULT_REPORTERS RegisterReporter(new PageFaultsSoftReporter); RegisterReporter(new PageFaultsHardReporter); diff --git a/xpcom/build/nsXPComInit.cpp b/xpcom/build/nsXPComInit.cpp index 9fb878c3d8c..e5c9d2f033a 100644 --- a/xpcom/build/nsXPComInit.cpp +++ b/xpcom/build/nsXPComInit.cpp @@ -118,7 +118,6 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **) #include "base/message_loop.h" #include "mozilla/ipc/BrowserProcessSubThread.h" -#include "mozilla/MapsMemoryReporter.h" #include "mozilla/AvailableMemoryTracker.h" #include "mozilla/ClearOnShutdown.h" @@ -574,8 +573,6 @@ NS_InitXPCOM2(nsIServiceManager* *result, CreateAnonTempFileRemover(); #endif - mozilla::MapsMemoryReporter::Init(); - // The memory reporter manager is up and running -- register a reporter for // ICU's memory usage. NS_RegisterMemoryReporter(new ICUReporter());