xpcom/base/SystemMemoryReporter.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:dc984433c936
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/. */
6
7 #include "mozilla/SystemMemoryReporter.h"
8
9 #include "mozilla/Attributes.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/unused.h"
12
13 #include "nsIMemoryReporter.h"
14 #include "nsPrintfCString.h"
15 #include "nsString.h"
16 #include "nsTHashtable.h"
17 #include "nsHashKeys.h"
18
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>
26
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.
38
39 namespace mozilla {
40 namespace SystemMemoryReporter {
41
42 #if !defined(XP_LINUX)
43 #error "This won't work if we're not on Linux."
44 #endif
45
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 }
52
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 }
63
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 }
74
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(" ");
82
83 aOut.Assign(out);
84 }
85
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 }
98
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 }
111
112 class SystemReporter MOZ_FINAL : public nsIMemoryReporter
113 {
114 public:
115 NS_DECL_THREADSAFE_ISUPPORTS
116
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)
131
132 #define REPORT(_path, _amount, _desc) \
133 REPORT_WITH_CLEANUP(_path, UNITS_BYTES, _amount, _desc, (void)0)
134
135 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
136 nsISupports* aData)
137 {
138 if (!Preferences::GetBool("memory.system_memory_reporter")) {
139 return NS_OK;
140 }
141
142 // Read relevant fields from /proc/meminfo.
143 int64_t memTotal = 0, memFree = 0;
144 nsresult rv = ReadMemInfo(&memTotal, &memFree);
145
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);
150
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."));
157
158 REPORT(NS_LITERAL_CSTRING("mem/free"), memFree, NS_LITERAL_CSTRING(
159 "Memory which is free and not being used for any purpose."));
160
161 // Report reserved memory not included in memTotal.
162 rv = CollectPmemReports(aHandleReport, aData);
163 NS_ENSURE_SUCCESS(rv, rv);
164
165 // Report zram usage statistics.
166 rv = CollectZramReports(aHandleReport, aData);
167 NS_ENSURE_SUCCESS(rv, rv);
168
169 return rv;
170 }
171
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,
184
185 ProcessSizeKindLimit = 9 // must be last
186 };
187
188 static const char* kindPathSuffixes[ProcessSizeKindLimit];
189
190 // These are the cross-cutting measurements across all processes.
191 struct ProcessSizes
192 {
193 ProcessSizes() { memset(this, 0, sizeof(*this)); }
194
195 size_t mSizes[ProcessSizeKindLimit];
196 };
197
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 }
204
205 int n1 = fscanf(f, "MemTotal: %" SCNd64 " kB\n", aMemTotal);
206 int n2 = fscanf(f, "MemFree: %" SCNd64 " kB\n", aMemFree);
207
208 fclose(f);
209
210 if (n1 != 1 || n2 != 1) {
211 return NS_ERROR_FAILURE;
212 }
213
214 // Convert from KB to B.
215 *aMemTotal *= 1024;
216 *aMemFree *= 1024;
217
218 return NS_OK;
219 }
220
221 nsresult CollectProcessReports(nsIHandleReportCallback* aHandleReport,
222 nsISupports* aData,
223 int64_t* aTotalPss)
224 {
225 *aTotalPss = 0;
226 ProcessSizes processSizes;
227
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(");
241
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(")");
262
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);
281
282 // Report the "processes/" tree.
283
284 for (size_t i = 0; i < ProcessSizeKindLimit; i++) {
285 nsAutoCString path("processes/");
286 path.Append(kindPathSuffixes[i]);
287
288 nsAutoCString desc("This is the sum of all processes' '");
289 desc.Append(kindPathSuffixes[i]);
290 desc.Append("' numbers.");
291
292 REPORT(path, processSizes.mSizes[i], desc);
293 }
294
295 return NS_OK;
296 }
297
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]
310
311 const int argCount = 8;
312
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];
323
324 // A path might not be present on this line; set it to the empty string.
325 path[0] = '\0';
326
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);
338
339 // Eat up any whitespace at the end of this line, including the newline.
340 unused << fscanf(aFile, " ");
341
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 }
347
348 nsAutoCString name, description;
349 ProcessSizeKind kind;
350 GetReporterNameAndDescription(path, perms, name, description, &kind);
351
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;
358
359 // Increment the appropriate aProcessSizes values, and the total.
360 aProcessSizes->mSizes[kind] += pss;
361 *aTotalPss += pss;
362 }
363
364 return NS_OK;
365 }
366
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();
375
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(" ");
382
383 nsAutoCString basename;
384 GetBasename(absPath, basename);
385
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;
394
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;
400
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;
409
410 } else if (!IsAnonymous(basename)) {
411 nsAutoCString dirname;
412 GetDirname(absPath, dirname);
413
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/");
419
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 }
429
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 }
439
440 aName.Append(basename);
441 aDesc.Append(absPath);
442
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 }
449
450 aName.Append("/[");
451 aName.Append(aPerms);
452 aName.Append("]");
453
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 }
461
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.
483
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 }
494
495 // Only report "Pss" values.
496 if (strcmp(desc, "Pss") == 0) {
497 *aPss = sizeKB * 1024;
498
499 // Don't report zero values.
500 if (*aPss == 0) {
501 return NS_OK;
502 }
503
504 nsAutoCString path("mem/processes/");
505 path.Append(aProcessName);
506 path.Append("/");
507 path.Append(aName);
508
509 REPORT(path, *aPss, aDescription);
510 } else {
511 *aPss = 0;
512 }
513
514 return NS_OK;
515 }
516
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 }
539
540 struct dirent* ent;
541 while ((ent = readdir(d))) {
542 const char* name = ent->d_name;
543 uint64_t size;
544 int scanned;
545
546 // Skip "." and ".." (and any other dotfiles).
547 if (name[0] == '.') {
548 continue;
549 }
550
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);
563
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;
574
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;
589
590 scanned = sscanf(nextParen + 1, "%" SCNx64 ",%" SCNx64,
591 &mapStart, &mapLen);
592 if (NS_WARN_IF(scanned != 2)) {
593 break;
594 }
595
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 }
607
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 }
617
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 }
625
626 uint64_t size = 0;
627 fscanf(sizeFile, "%" SCNu64, &size);
628 fclose(sizeFile);
629
630 return size;
631 }
632
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.
647
648 DIR* d = opendir("/sys/block");
649 if (!d) {
650 if (NS_WARN_IF(errno != ENOENT)) {
651 return NS_ERROR_FAILURE;
652 }
653
654 return NS_OK;
655 }
656
657 struct dirent* ent;
658 while ((ent = readdir(d))) {
659 const char* name = ent->d_name;
660
661 // Skip non-zram entries.
662 if (strncmp("zram", name, 4) != 0) {
663 continue;
664 }
665
666 // Report disk size statistics.
667 nsPrintfCString diskSizeFile("/sys/block/%s/disksize", name);
668 nsPrintfCString origSizeFile("/sys/block/%s/orig_data_size", name);
669
670 uint64_t diskSize = ReadSizeFromFile(diskSizeFile.get());
671 uint64_t origSize = ReadSizeFromFile(origSizeFile.get());
672 uint64_t unusedSize = diskSize - origSize;
673
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));
681
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));
688
689 // Report disk accesses.
690 nsPrintfCString readsFile("/sys/block/%s/num_reads", name);
691 nsPrintfCString writesFile("/sys/block/%s/num_writes", name);
692
693 uint64_t reads = ReadSizeFromFile(readsFile.get());
694 uint64_t writes = ReadSizeFromFile(writesFile.get());
695
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));
702
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));
709
710 // Report compressed data size.
711 nsPrintfCString comprSizeFile("/sys/block/%s/compr_data_size", name);
712 uint64_t comprSize = ReadSizeFromFile(comprSizeFile.get());
713
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 }
721
722 closedir(d);
723 return NS_OK;
724 }
725
726 #undef REPORT
727 };
728
729 NS_IMPL_ISUPPORTS(SystemReporter, nsIMemoryReporter)
730
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 };
743
744 void Init()
745 {
746 RegisterStrongMemoryReporter(new SystemReporter());
747 }
748
749 } // namespace SystemMemoryReporter
750 } // namespace mozilla

mercurial