summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutmemory/tests
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/aboutmemory/tests')
-rw-r--r--toolkit/components/aboutmemory/tests/chrome.ini31
-rw-r--r--toolkit/components/aboutmemory/tests/crash-dump-diff1.json18
-rw-r--r--toolkit/components/aboutmemory/tests/crash-dump-diff2.json18
-rw-r--r--toolkit/components/aboutmemory/tests/crash-dump-good.json42
-rw-r--r--toolkit/components/aboutmemory/tests/fiss-diff1.json59
-rw-r--r--toolkit/components/aboutmemory/tests/fiss-diff2.json59
-rw-r--r--toolkit/components/aboutmemory/tests/memory-reports-bad.json3
-rw-r--r--toolkit/components/aboutmemory/tests/memory-reports-diff1.json272
-rw-r--r--toolkit/components/aboutmemory/tests/memory-reports-diff2.json265
-rw-r--r--toolkit/components/aboutmemory/tests/memory-reports-good.json146
-rw-r--r--toolkit/components/aboutmemory/tests/remote.xhtml12
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml599
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml420
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml558
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml176
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory5.xhtml169
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory6.xhtml85
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml215
-rw-r--r--toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xhtml90
-rw-r--r--toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml433
-rw-r--r--toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml116
-rw-r--r--toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xhtml46
-rw-r--r--toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js39
-rw-r--r--toolkit/components/aboutmemory/tests/xpcshell/xpcshell.ini5
24 files changed, 3876 insertions, 0 deletions
diff --git a/toolkit/components/aboutmemory/tests/chrome.ini b/toolkit/components/aboutmemory/tests/chrome.ini
new file mode 100644
index 0000000000..4e5071e4a0
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/chrome.ini
@@ -0,0 +1,31 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ crash-dump-diff1.json
+ crash-dump-diff2.json
+ crash-dump-good.json
+ fiss-diff1.json
+ fiss-diff2.json
+ memory-reports-bad.json
+ memory-reports-diff1.json
+ memory-reports-diff2.json
+ memory-reports-good.json
+ remote.xhtml
+
+[test_aboutmemory.xhtml]
+[test_aboutmemory2.xhtml]
+[test_aboutmemory3.xhtml]
+[test_aboutmemory4.xhtml]
+skip-if =
+ os == "linux" && bits == 64 && debug # Bug 1683002
+[test_aboutmemory5.xhtml]
+skip-if =
+ asan # Bug 1116230
+ os == "linux" && bits == 64 && debug # Bug 1683002
+[test_aboutmemory6.xhtml]
+[test_aboutmemory7.xhtml]
+[test_memoryReporters.xhtml]
+[test_memoryReporters2.xhtml]
+[test_sqliteMultiReporter.xhtml]
+[test_dumpGCAndCCLogsToFile.xhtml]
+skip-if = (verify && debug && (os == 'mac'))
diff --git a/toolkit/components/aboutmemory/tests/crash-dump-diff1.json b/toolkit/components/aboutmemory/tests/crash-dump-diff1.json
new file mode 100644
index 0000000000..2b9c6921a8
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/crash-dump-diff1.json
@@ -0,0 +1,18 @@
+{
+ "foo": 1,
+ "blah": 2,
+ "memory_report": {
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 262144000,
+ "description": "Heap allocated."
+ }
+ ]
+ }
+}
diff --git a/toolkit/components/aboutmemory/tests/crash-dump-diff2.json b/toolkit/components/aboutmemory/tests/crash-dump-diff2.json
new file mode 100644
index 0000000000..15ddf2831c
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/crash-dump-diff2.json
@@ -0,0 +1,18 @@
+{
+ "foo": 3,
+ "blah": 4,
+ "memory_report": {
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 262144001,
+ "description": "Heap allocated."
+ }
+ ]
+ }
+}
diff --git a/toolkit/components/aboutmemory/tests/crash-dump-good.json b/toolkit/components/aboutmemory/tests/crash-dump-good.json
new file mode 100644
index 0000000000..7865369dc3
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/crash-dump-good.json
@@ -0,0 +1,42 @@
+{
+ "foo": 1,
+ "blah": 2,
+ "memory_report": {
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 262144000,
+ "description": "Heap allocated."
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "other/b",
+ "kind": 2,
+ "units": 0,
+ "amount": 104857,
+ "description": "Other b."
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "other/a",
+ "kind": 2,
+ "units": 0,
+ "amount": 209715,
+ "description": "Other a."
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "explicit/a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 52428800,
+ "description": "A b."
+ }
+ ]
+ }
+}
diff --git a/toolkit/components/aboutmemory/tests/fiss-diff1.json b/toolkit/components/aboutmemory/tests/fiss-diff1.json
new file mode 100644
index 0000000000..abd06bb508
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/fiss-diff1.json
@@ -0,0 +1,59 @@
+{
+ "foo": 1,
+ "blah": 2,
+ "memory_report": {
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "P (pid 12345)",
+ "path": "explicit/foobar",
+ "kind": 1,
+ "units": 0,
+ "amount": 100,
+ "description": "Desc."
+ },
+ {
+ "process": "P (pid 12345)",
+ "path": "explicit/zero1",
+ "kind": 1,
+ "units": 0,
+ "amount": 0,
+ "description": "Desc."
+ },
+ {
+ "process": "P (pid 12345)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 10000000,
+ "description": "Heap allocated."
+ },
+
+ {
+ "process": "web (pid 12345)",
+ "path": "explicit/a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 2000000,
+ "description": "Desc."
+ },
+ {
+ "process": "web (pid 12345)",
+ "path": "explicit/a/c/d",
+ "kind": 1,
+ "units": 0,
+ "amount": 2000000,
+ "description": "Desc."
+ },
+ {
+ "process": "web (pid 12345)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 10000000,
+ "description": "Heap allocated."
+ }
+ ]
+ }
+}
diff --git a/toolkit/components/aboutmemory/tests/fiss-diff2.json b/toolkit/components/aboutmemory/tests/fiss-diff2.json
new file mode 100644
index 0000000000..286d2dd8ec
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/fiss-diff2.json
@@ -0,0 +1,59 @@
+{
+ "foo": 1,
+ "blah": 2,
+ "memory_report": {
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "P (pid 12345)",
+ "path": "explicit/foobar",
+ "kind": 1,
+ "units": 0,
+ "amount": 400,
+ "description": "Desc."
+ },
+ {
+ "process": "P (pid 12345)",
+ "path": "explicit/zero1",
+ "kind": 1,
+ "units": 0,
+ "amount": 0,
+ "description": "Desc."
+ },
+ {
+ "process": "P (pid 12345)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 13000000,
+ "description": "Heap allocated."
+ },
+
+ {
+ "process": "webIsolated=https://example.com (pid 12345)",
+ "path": "explicit/a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 2000000,
+ "description": "Desc."
+ },
+ {
+ "process": "webIsolated=https://example.com (pid 12345)",
+ "path": "explicit/a/c/d",
+ "kind": 1,
+ "units": 0,
+ "amount": 3000000,
+ "description": "Desc."
+ },
+ {
+ "process": "webIsolated=https://example.com (pid 12345)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 12000000,
+ "description": "Heap allocated."
+ }
+ ]
+ }
+}
diff --git a/toolkit/components/aboutmemory/tests/memory-reports-bad.json b/toolkit/components/aboutmemory/tests/memory-reports-bad.json
new file mode 100644
index 0000000000..61a2092b1b
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/memory-reports-bad.json
@@ -0,0 +1,3 @@
+{
+ "version": 1
+}
diff --git a/toolkit/components/aboutmemory/tests/memory-reports-diff1.json b/toolkit/components/aboutmemory/tests/memory-reports-diff1.json
new file mode 100644
index 0000000000..bdc0eda14b
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/memory-reports-diff1.json
@@ -0,0 +1,272 @@
+{
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "P",
+ "path": "explicit/xpcom/category-manager",
+ "kind": 1,
+ "units": 0,
+ "amount": 56848,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "explicit/storage/prefixset/goog-phish-shavar",
+ "kind": 1,
+ "units": 0,
+ "amount": 680000,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "explicit/spell-check",
+ "kind": 1,
+ "units": 0,
+ "amount": 4,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "explicit/spell-check",
+ "kind": 1,
+ "units": 0,
+ "amount": 5,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "page-faults-soft",
+ "kind": 2,
+ "units": 2,
+ "amount": 61013,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "foobar",
+ "kind": 2,
+ "units": 0,
+ "amount": 100,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "zero1",
+ "kind": 2,
+ "units": 0,
+ "amount": 0,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "a/b",
+ "kind": 2,
+ "units": 0,
+ "amount": 1000000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/d",
+ "kind": 2,
+ "units": 0,
+ "amount": 2000000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/e",
+ "kind": 2,
+ "units": 0,
+ "amount": 2000000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/f",
+ "kind": 2,
+ "units": 0,
+ "amount": 3000000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/g",
+ "kind": 2,
+ "units": 0,
+ "amount": 3000000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/h",
+ "kind": 2,
+ "units": 0,
+ "amount": 1000,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P2 (pid 22)",
+ "path": "p1 (pid 123)",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p2 (blah, pid=123)",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p3/zone(0x1234)/p3",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p4/js-zone(0x1234)/p4",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p5/worker(foo.com, 0x1234)/p5",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "explicit/window-objects/top(bar.com, id=123)/...",
+ "kind": 0,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p6/z-moz-nullprincipal:{85e250f3-57ae-46c4-a11e-4176dd39d9c5}/p6",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p7/js-main-runtime-compartments/system/jar:file:\\\\\\temp_xyz\\firefox\\omni.ja!/p7",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "explicit/js-non-window/runtime/script-sources/source(scripts=1011, <non-notable files>)/misc",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P3",
+ "path": "p3",
+ "kind": 2,
+ "units": 0,
+ "amount": 55,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P5",
+ "path": "p5",
+ "kind": 2,
+ "units": 0,
+ "amount": 0,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P7",
+ "path": "p7",
+ "kind": 2,
+ "units": 0,
+ "amount": 5,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P8",
+ "path": "p8/a/b/c/d",
+ "kind": 2,
+ "units": 0,
+ "amount": 3,
+ "description": "Desc."
+ },
+ {
+ "process": "P8",
+ "path": "p8/a/b/c/e",
+ "kind": 2,
+ "units": 0,
+ "amount": 4,
+ "description": "Desc."
+ },
+ {
+ "process": "P8",
+ "path": "p8/a/b/f",
+ "kind": 2,
+ "units": 0,
+ "amount": 5,
+ "description": "Desc."
+ },
+ {
+ "process": "P8",
+ "path": "p8/a/g/h",
+ "kind": 2,
+ "units": 0,
+ "amount": 6,
+ "description": "Desc."
+ },
+ {
+ "process": "P8",
+ "path": "p8/a/g/i",
+ "kind": 2,
+ "units": 0,
+ "amount": 7,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P9",
+ "path": "explicit/threads/stacks/DNS Resolver #1 (tid=11)",
+ "kind": 0,
+ "units": 0,
+ "amount": 2000,
+ "description": "Desc."
+ },
+ {
+ "process": "P9",
+ "path": "explicit/threads/stacks/DNS Resolver #2 (tid=22)",
+ "kind": 0,
+ "units": 0,
+ "amount": 4000,
+ "description": "Desc."
+ }
+ ]
+}
diff --git a/toolkit/components/aboutmemory/tests/memory-reports-diff2.json b/toolkit/components/aboutmemory/tests/memory-reports-diff2.json
new file mode 100644
index 0000000000..d6cd988988
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/memory-reports-diff2.json
@@ -0,0 +1,265 @@
+{
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "P",
+ "path": "explicit/xpcom/category-manager",
+ "kind": 1,
+ "units": 0,
+ "amount": 56849,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "explicit/storage/prefixset/goog-phish-shavar",
+ "kind": 1,
+ "units": 0,
+ "amount": 670000,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "explicit/spell-check",
+ "kind": 1,
+ "units": 0,
+ "amount": 3,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "page-faults-soft",
+ "kind": 2,
+ "units": 2,
+ "amount": 61013,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "canvas-2d-pixel-bytes",
+ "kind": 2,
+ "units": 0,
+ "amount": 1000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "canvas-2d-pixel-bytes",
+ "kind": 2,
+ "units": 0,
+ "amount": 2000,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "foobaz",
+ "kind": 2,
+ "units": 0,
+ "amount": 0,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P",
+ "path": "a/b",
+ "kind": 2,
+ "units": 0,
+ "amount": 2000000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/d",
+ "kind": 2,
+ "units": 0,
+ "amount": 2998000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/e",
+ "kind": 2,
+ "units": 0,
+ "amount": 1001000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/f",
+ "kind": 2,
+ "units": 0,
+ "amount": 3001000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/c/g",
+ "kind": 2,
+ "units": 0,
+ "amount": 3001000,
+ "description": "Desc."
+ },
+ {
+ "process": "P",
+ "path": "a/h",
+ "kind": 2,
+ "units": 0,
+ "amount": 2000,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P2 (pid 22)",
+ "path": "p1 (pid 456)",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p2 (blah, pid=456)",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p3/zone(0x5678)/p3",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p4/js-zone(0x5678)/p4",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p5/worker(foo.com, 0x5678)/p5",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "explicit/window-objects/top(bar.com, id=456)/...",
+ "kind": 0,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p6/z-moz-nullprincipal:{161effaa-c1f7-4010-a08e-e7c9aea01aed}/p6",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "p7/js-main-runtime-compartments/system/jar:file:\\\\\\temp_abc\\firefox\\omni.ja!/p7",
+ "kind": 2,
+ "units": 0,
+ "amount": 44,
+ "description": "Desc."
+ },
+ {
+ "process": "P2 (pid 22)",
+ "path": "explicit/js-non-window/runtime/script-sources/source(scripts=1, <non-notable files>)/misc",
+ "kind": 2,
+ "units": 0,
+ "amount": 33,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P4",
+ "path": "p4",
+ "kind": 2,
+ "units": 0,
+ "amount": 66,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P6",
+ "path": "p6",
+ "kind": 2,
+ "units": 0,
+ "amount": 0,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P7",
+ "path": "p7/b",
+ "kind": 2,
+ "units": 0,
+ "amount": 3,
+ "description": "Desc."
+ },
+ {
+ "process": "P7",
+ "path": "p7/c",
+ "kind": 2,
+ "units": 0,
+ "amount": 4,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P8",
+ "path": "p8/a/b",
+ "kind": 2,
+ "units": 0,
+ "amount": 1,
+ "description": "Desc."
+ },
+ {
+ "process": "P8",
+ "path": "p8/a/g",
+ "kind": 2,
+ "units": 0,
+ "amount": 2,
+ "description": "Desc."
+ },
+
+ {
+ "process": "P9",
+ "path": "explicit/threads/stacks/DNS Resolver #1 (tid=33)",
+ "kind": 0,
+ "units": 0,
+ "amount": 2000,
+ "description": "Desc."
+ },
+ {
+ "process": "P9",
+ "path": "explicit/threads/stacks/DNS Resolver #2 (tid=44)",
+ "kind": 0,
+ "units": 0,
+ "amount": 4000,
+ "description": "Desc."
+ },
+ {
+ "process": "P9",
+ "path": "explicit/threads/stacks/DNS Resolver #3 (tid=45)",
+ "kind": 0,
+ "units": 0,
+ "amount": 5000,
+ "description": "Desc."
+ }
+ ]
+}
diff --git a/toolkit/components/aboutmemory/tests/memory-reports-good.json b/toolkit/components/aboutmemory/tests/memory-reports-good.json
new file mode 100644
index 0000000000..21b6d5b0aa
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/memory-reports-good.json
@@ -0,0 +1,146 @@
+{
+ "version": 1,
+ "hasMozMallocUsableSize": true,
+ "reports": [
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 262144000,
+ "description": "Heap allocated."
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "other/b",
+ "kind": 2,
+ "units": 0,
+ "amount": 104857,
+ "description": "Other b."
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "other/a",
+ "kind": 2,
+ "units": 0,
+ "amount": 209715,
+ "description": "Other a."
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "explicit/a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 52428800,
+ "description": "A b."
+ },
+
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "size/a",
+ "kind": 1,
+ "units": 0,
+ "amount": 1024,
+ "description": "non-sentence"
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "rss/a",
+ "kind": 1,
+ "units": 0,
+ "amount": 1024,
+ "description": "non-sentence"
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "pss/a",
+ "kind": 1,
+ "units": 0,
+ "amount": 1024,
+ "description": "non-sentence"
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "swap/a",
+ "kind": 1,
+ "units": 0,
+ "amount": 1024,
+ "description": "non-sentence"
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "compartments/system/a",
+ "kind": 1,
+ "units": 0,
+ "amount": 1024,
+ "description": ""
+ },
+ {
+ "process": "Main Process (pid NNN)",
+ "path": "ghost-windows/a",
+ "kind": 1,
+ "units": 0,
+ "amount": 1024,
+ "description": ""
+ },
+
+ {
+ "process": "Heap-unclassified process",
+ "path": "heap-allocated",
+ "kind": 2,
+ "units": 0,
+ "amount": 262144000,
+ "description": "Heap allocated."
+ },
+ {
+ "process": "Heap-unclassified process",
+ "path": "explicit/a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 52428800,
+ "description": "A b."
+ },
+ {
+ "process": "Heap-unclassified process",
+ "path": "explicit/heap-unclassified",
+ "kind": 1,
+ "units": 0,
+ "amount": 209715200,
+ "description": "Heap unclassified"
+ },
+
+ {
+ "process": "Explicit-only process",
+ "path": "explicit/a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 100000,
+ "description": "A b."
+ },
+
+ {
+ "process": "Other-only process",
+ "path": "a/b",
+ "kind": 1,
+ "units": 0,
+ "amount": 100000,
+ "description": "A b."
+ },
+ {
+ "process": "Other-only process",
+ "path": "a/c",
+ "kind": 1,
+ "units": 0,
+ "amount": 100000,
+ "description": "A c."
+ },
+ {
+ "process": "Other-only process",
+ "path": "heap-allocated",
+ "kind": 1,
+ "units": 0,
+ "amount": 500000,
+ "description": "D."
+ }
+ ]
+}
diff --git a/toolkit/components/aboutmemory/tests/remote.xhtml b/toolkit/components/aboutmemory/tests/remote.xhtml
new file mode 100644
index 0000000000..7d69101305
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/remote.xhtml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Remote browser"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- test results are displayed in the html:body -->
+ <p>Remote browser</p>
+
+ <browser type="content" src="about:blank" id="remote" remote="true"/>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml
new file mode 100644
index 0000000000..4617a019ad
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml
@@ -0,0 +1,599 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file uses fake memory reporters to test the presentation of memory
+ reports in about:memory. test_memoryReporters.xhtml uses the real
+ memory reporters to test whether the memory reporters are producing
+ sensible results. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ SimpleTest.expectAssertions(27);
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ // Hide all the real reporters; we'll restore them at the end.
+ mgr.blockRegistrationAndHideExistingReporters();
+
+ // Setup various fake-but-deterministic reporters.
+ const KB = 1024;
+ const MB = KB * KB;
+ const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
+ const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
+ const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
+
+ const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+ const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
+ const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
+ const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
+
+ let fakeReporters = [
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP, aK, aU, aA) {
+ aCbObj.callback("Main Process", aP, aK, aU, aA, "Desc.", aClosure);
+ }
+ f("heap-allocated", OTHER, BYTES, 500 * MB);
+ f("heap-unallocated", OTHER, BYTES, 100 * MB);
+ f("explicit/a", HEAP, BYTES, 222 * MB);
+ f("explicit/b/a", HEAP, BYTES, 85 * MB);
+ f("explicit/b/b", HEAP, BYTES, 75 * MB);
+ f("explicit/b/c/a", HEAP, BYTES, 70 * MB);
+ f("explicit/b/c/b", HEAP, BYTES, 2 * MB); // omitted
+ f("explicit/g/a", HEAP, BYTES, 6 * MB);
+ f("explicit/g/b", HEAP, BYTES, 5 * MB);
+ f("explicit/g/other", HEAP, BYTES, 4 * MB);
+ // A degenerate tree with the same name as a non-degenerate tree should
+ // work ok.
+ f("explicit", OTHER, BYTES, 888 * MB);
+ f("other1/a/b", OTHER, BYTES, 111 * MB);
+ f("other1/c/d", OTHER, BYTES, 22 * MB);
+ f("other1/c/e", OTHER, BYTES, 33 * MB);
+ f("other4", OTHER, COUNT_CUMULATIVE, 777);
+ f("other4", OTHER, COUNT_CUMULATIVE, 111);
+ f("other3/a/b/c/d/e", OTHER, PERCENTAGE, 2000);
+ f("other3/a/b/c/d/f", OTHER, PERCENTAGE, 10);
+ f("other3/a/b/c/d/g", OTHER, PERCENTAGE, 5);
+ f("other3/a/b/c/d/g", OTHER, PERCENTAGE, 5);
+ // Check that a rounded-up-to-100.00% value is shown as "100.0%" (i.e. one
+ // decimal point).
+ f("other6/big", OTHER, COUNT, 99999);
+ f("other6/small", OTHER, COUNT, 1);
+ // Check that a 0 / 0 is handled correctly.
+ f("other7/zero", OTHER, BYTES, 0);
+ // These compartments ones shouldn't be displayed.
+ f("compartments/user/foo", OTHER, COUNT, 1);
+ f("compartments/system/foo", OTHER, COUNT, 1);
+ }
+ },
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP, aK, aU, aA) {
+ aCbObj.callback("Main Process", aP, aK, aU, aA, "Desc.", aClosure);
+ }
+ f("explicit/c/d", NONHEAP, BYTES, 13 * MB);
+ f("explicit/c/d", NONHEAP, BYTES, 10 * MB); // dup
+ f("explicit/c/other", NONHEAP, BYTES, 77 * MB);
+ f("explicit/cc", NONHEAP, BYTES, 13 * MB);
+ f("explicit/cc", NONHEAP, BYTES, 10 * MB); // dup
+ f("explicit/d", NONHEAP, BYTES, 499 * KB); // omitted
+ f("explicit/e", NONHEAP, BYTES, 100 * KB); // omitted
+ f("explicit/f/g/h/i", HEAP, BYTES, 10 * MB);
+ f("explicit/f/g/h/j", HEAP, BYTES, 10 * MB);
+ }
+ },
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP, aK, aU, aA) {
+ aCbObj.callback("Main Process", aP, aK, aU, aA, "Desc.", aClosure);
+ }
+ f("other3", OTHER, COUNT, 777);
+ f("other2", OTHER, BYTES, 222 * MB);
+ f("perc2", OTHER, PERCENTAGE, 10000);
+ f("perc1", OTHER, PERCENTAGE, 4567);
+ 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);
+ }
+ },
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP) {
+ aCbObj.callback("Main Process", aP, OTHER, COUNT, 1, "Desc.", aClosure);
+ }
+ f("compartments/user/bar");
+ f("compartments/system/bar");
+ }
+ }
+ ];
+ for (let i = 0; i < fakeReporters.length; i++) {
+ mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]);
+ }
+
+ // The main process always comes first when we display about:memory. The
+ // remaining processes are sorted by their |resident| values (starting with
+ // the largest). Processes without a |resident| memory reporter are saved
+ // for the end.
+ let fakeReporters2 = [
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP1, aP2, aK, aU, aA) {
+ aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure);
+ }
+ f("2nd", "heap-allocated", OTHER, BYTES,1000* MB);
+ f("2nd", "heap-unallocated",OTHER, BYTES,100 * MB);
+ f("2nd", "explicit/a/b/c", HEAP, BYTES,497 * MB);
+ f("2nd", "explicit/a/b/c", HEAP, BYTES, 1 * MB); // dup: merge
+ f("2nd", "explicit/a/b/c", HEAP, BYTES, 1 * MB); // dup: merge
+ f("2nd", "explicit/flip\\the\\backslashes",
+ HEAP, BYTES,200 * MB);
+ f("2nd", "explicit/compartment(compartment-url)",
+ HEAP, BYTES,200 * MB);
+ f("2nd", "other0", OTHER, BYTES,666 * MB);
+ f("2nd", "other1", OTHER, BYTES,111 * MB);
+
+ // Check that we can handle "heap-allocated" not being present.
+ f("3rd", "explicit/a/b", HEAP, BYTES,333 * MB);
+ f("3rd", "explicit/a/c", HEAP, BYTES,444 * MB);
+ f("3rd", "other1", OTHER, BYTES, 1 * MB);
+ f("3rd", "resident", OTHER, BYTES,100 * MB);
+
+ // Invalid values (negative, too-big) should be identified.
+ f("4th", "heap-allocated", OTHER, BYTES,100 * MB);
+ f("4th", "resident", OTHER, BYTES,200 * MB);
+ f("4th", "explicit/js/compartment(http:\\\\too-big.com\\)/stuff",
+ HEAP, BYTES,150 * MB);
+ f("4th", "explicit/ok", HEAP, BYTES, 5 * MB);
+ f("4th", "explicit/neg1", NONHEAP, BYTES, -2 * MB);
+ // -111 becomes "-0.00MB" in non-verbose mode, and getting the negative
+ // sign in there correctly is non-trivial.
+ f("4th", "other1", OTHER, BYTES,-111);
+ f("4th", "other2", OTHER, BYTES,-222 * MB);
+ f("4th", "other3", OTHER, COUNT, -333);
+ f("4th", "other4", OTHER, COUNT_CUMULATIVE, -444);
+ f("4th", "other5", OTHER, PERCENTAGE, -555);
+ f("4th", "other6", OTHER, PERCENTAGE, 66666);
+
+ // If a negative value is within a collapsed sub-tree in non-verbose mode,
+ // we should get the warning at the top and the relevant sub-trees should
+ // be expanded, even in non-verbose mode.
+ f("5th", "heap-allocated", OTHER, BYTES,100 * MB);
+ f("5th", "explicit/big", HEAP, BYTES, 99 * MB);
+ f("5th", "explicit/a/pos", HEAP, BYTES, 40 * KB);
+ f("5th", "explicit/a/neg1", NONHEAP, BYTES,-20 * KB);
+ f("5th", "explicit/a/neg2", NONHEAP, BYTES,-10 * KB);
+ f("5th", "explicit/b/c/d/e", NONHEAP, BYTES, 20 * KB);
+ f("5th", "explicit/b/c/d/f", NONHEAP, BYTES,-60 * KB);
+ f("5th", "explicit/b/c/g/h", NONHEAP, BYTES, 10 * KB);
+ f("5th", "explicit/b/c/i/j", NONHEAP, BYTES, 5 * KB);
+ }
+ }
+ ];
+ for (let i = 0; i < fakeReporters2.length; i++) {
+ mgr.registerStrongReporterEvenIfBlocked(fakeReporters2[i]);
+ }
+ fakeReporters = fakeReporters.concat(fakeReporters2);
+ ]]>
+ </script>
+
+ <iframe id="amFrame" height="300" src="about:memory"></iframe>
+ <!-- vary the capitalization to make sure that works -->
+ <iframe id="amvFrame" height="300" src="About:Memory"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+ let amExpectedText =
+"\
+Main Process\n\
+Explicit Allocations\n\
+\n\
+623.58 MB (100.0%) -- explicit\n\
+├──232.00 MB (37.20%) -- b\n\
+│ ├───85.00 MB (13.63%) ── a\n\
+│ ├───75.00 MB (12.03%) ── b\n\
+│ └───72.00 MB (11.55%) -- c\n\
+│ ├──70.00 MB (11.23%) ── a\n\
+│ └───2.00 MB (00.32%) ── b\n\
+├──222.00 MB (35.60%) ── a\n\
+├──100.00 MB (16.04%) -- c\n\
+│ ├───77.00 MB (12.35%) ── other\n\
+│ └───23.00 MB (03.69%) ── d [2]\n\
+├───23.00 MB (03.69%) ── cc [2]\n\
+├───20.00 MB (03.21%) -- f/g/h\n\
+│ ├──10.00 MB (01.60%) ── i\n\
+│ └──10.00 MB (01.60%) ── j\n\
+├───15.00 MB (02.41%) ++ g\n\
+├───11.00 MB (01.76%) ── heap-unclassified\n\
+└────0.58 MB (00.09%) ++ (2 tiny)\n\
+\n\
+Other Measurements\n\
+\n\
+5 (100.0%) -- compartments\n\
+├──3 (60.00%) -- user\n\
+│ ├──1 (20.00%) ── bar\n\
+│ ├──1 (20.00%) ── foo\n\
+│ └──1 (20.00%) ── 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\n\
+└──2 (40.00%) -- system\n\
+ ├──1 (20.00%) ── bar\n\
+ └──1 (20.00%) ── foo\n\
+\n\
+166.00 MB (100.0%) -- other1\n\
+├──111.00 MB (66.87%) ── a/b\n\
+└───55.00 MB (33.13%) -- c\n\
+ ├──33.00 MB (19.88%) ── e\n\
+ └──22.00 MB (13.25%) ── d\n\
+\n\
+20.20% (100.0%) -- other3\n\
+└──20.20% (100.0%) -- a/b/c/d\n\
+ ├──20.00% (99.01%) ── e\n\
+ └───0.20% (00.99%) ++ (2 tiny)\n\
+\n\
+100,000 (100.0%) -- other6\n\
+├───99,999 (100.0%) ── big\n\
+└────────1 (00.00%) ── small\n\
+\n\
+0.00 MB (100.0%) -- other7\n\
+└──0.00 MB (100.0%) ── zero\n\
+\n\
+888.00 MB ── explicit\n\
+500.00 MB ── heap-allocated\n\
+100.00 MB ── heap-unallocated\n\
+222.00 MB ── other2\n\
+ 777 ── other3\n\
+ 888 ── other4 [2]\n\
+ 45.67% ── perc1\n\
+ 100.00% ── perc2\n\
+\n\
+End of Main Process\n\
+4th\n\
+\n\
+WARNING: the following values are negative or unreasonably large.\n\
+\n\
+ explicit/js/compartment(http://too-big.com/)/stuff\n\
+ explicit/(2 tiny)\n\
+ explicit/(2 tiny)/neg1\n\
+ explicit/(2 tiny)/heap-unclassified\n\
+ other1\n\
+ other2\n\
+ other3\n\
+ other4\n\
+ other5 \n\
+\n\
+This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\
+Explicit Allocations\n\
+\n\
+98.00 MB (100.0%) -- explicit\n\
+├──150.00 MB (153.06%) ── js/compartment(http://too-big.com/)/stuff [?!]\n\
+├───5.00 MB (05.10%) ── ok\n\
+└──-57.00 MB (-58.16%) -- (2 tiny) [?!]\n\
+ ├───-2.00 MB (-02.04%) ── neg1 [?!]\n\
+ └──-55.00 MB (-56.12%) ── heap-unclassified [?!]\n\
+\n\
+Other Measurements\n\
+\n\
+ 100.00 MB ── heap-allocated\n\
+ -0.00 MB ── other1 [?!]\n\
+-222.00 MB ── other2 [?!]\n\
+ -333 ── other3 [?!]\n\
+ -444 ── other4 [?!]\n\
+ -5.55% ── other5 [?!]\n\
+ 666.66% ── other6\n\
+ 200.00 MB ── resident\n\
+\n\
+End of 4th\n\
+3rd\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+777.00 MB (100.0%) -- explicit\n\
+└──777.00 MB (100.0%) -- a\n\
+ ├──444.00 MB (57.14%) ── c\n\
+ └──333.00 MB (42.86%) ── b\n\
+\n\
+Other Measurements\n\
+\n\
+ 1.00 MB ── other1\n\
+100.00 MB ── resident\n\
+\n\
+End of 3rd\n\
+2nd\n\
+Explicit Allocations\n\
+\n\
+1,000.00 MB (100.0%) -- explicit\n\
+├────499.00 MB (49.90%) ── a/b/c [3]\n\
+├────200.00 MB (20.00%) ── compartment(compartment-url)\n\
+├────200.00 MB (20.00%) ── flip/the/backslashes\n\
+└────101.00 MB (10.10%) ── heap-unclassified\n\
+\n\
+Other Measurements\n\
+\n\
+1,000.00 MB ── heap-allocated\n\
+ 100.00 MB ── heap-unallocated\n\
+ 666.00 MB ── other0\n\
+ 111.00 MB ── other1\n\
+\n\
+End of 2nd\n\
+5th\n\
+\n\
+WARNING: the following values are negative or unreasonably large.\n\
+\n\
+ explicit/(3 tiny)/a/neg2\n\
+ explicit/(3 tiny)/a/neg1\n\
+ explicit/(3 tiny)/b/c\n\
+ explicit/(3 tiny)/b/c/d\n\
+ explicit/(3 tiny)/b/c/d/f \n\
+\n\
+This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\
+Explicit Allocations\n\
+\n\
+99.95 MB (100.0%) -- explicit\n\
+├──99.00 MB (99.05%) ── big\n\
+└───0.95 MB (00.95%) -- (3 tiny)\n\
+ ├──0.96 MB (00.96%) ── heap-unclassified\n\
+ ├──0.01 MB (00.01%) -- a\n\
+ │ ├──0.04 MB (00.04%) ── pos\n\
+ │ ├──-0.01 MB (-00.01%) ── neg2 [?!]\n\
+ │ └──-0.02 MB (-00.02%) ── neg1 [?!]\n\
+ └──-0.02 MB (-00.02%) -- b/c [?!]\n\
+ ├───0.01 MB (00.01%) ── g/h\n\
+ ├───0.00 MB (00.00%) ── i/j\n\
+ └──-0.04 MB (-00.04%) -- d [?!]\n\
+ ├───0.02 MB (00.02%) ── e\n\
+ └──-0.06 MB (-00.06%) ── f [?!]\n\
+\n\
+Other Measurements\n\
+\n\
+100.00 MB ── heap-allocated\n\
+\n\
+End of 5th\n\
+";
+
+ let amvExpectedText =
+"\
+Main Process\n\
+Explicit Allocations\n\
+\n\
+653,876,224 B (100.0%) -- explicit\n\
+├──243,269,632 B (37.20%) -- b\n\
+│ ├───89,128,960 B (13.63%) ── a\n\
+│ ├───78,643,200 B (12.03%) ── b\n\
+│ └───75,497,472 B (11.55%) -- c\n\
+│ ├──73,400,320 B (11.23%) ── a\n\
+│ └───2,097,152 B (00.32%) ── b\n\
+├──232,783,872 B (35.60%) ── a\n\
+├──104,857,600 B (16.04%) -- c\n\
+│ ├───80,740,352 B (12.35%) ── other\n\
+│ └───24,117,248 B (03.69%) ── d [2]\n\
+├───24,117,248 B (03.69%) ── cc [2]\n\
+├───20,971,520 B (03.21%) -- f/g/h\n\
+│ ├──10,485,760 B (01.60%) ── i\n\
+│ └──10,485,760 B (01.60%) ── j\n\
+├───15,728,640 B (02.41%) -- g\n\
+│ ├───6,291,456 B (00.96%) ── a\n\
+│ ├───5,242,880 B (00.80%) ── b\n\
+│ └───4,194,304 B (00.64%) ── other\n\
+├───11,534,336 B (01.76%) ── heap-unclassified\n\
+├──────510,976 B (00.08%) ── d\n\
+└──────102,400 B (00.02%) ── e\n\
+\n\
+Other Measurements\n\
+\n\
+5 (100.0%) -- compartments\n\
+├──3 (60.00%) -- user\n\
+│ ├──1 (20.00%) ── bar\n\
+│ ├──1 (20.00%) ── foo\n\
+│ └──1 (20.00%) ── 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\n\
+└──2 (40.00%) -- system\n\
+ ├──1 (20.00%) ── bar\n\
+ └──1 (20.00%) ── foo\n\
+\n\
+174,063,616 B (100.0%) -- other1\n\
+├──116,391,936 B (66.87%) ── a/b\n\
+└───57,671,680 B (33.13%) -- c\n\
+ ├──34,603,008 B (19.88%) ── e\n\
+ └──23,068,672 B (13.25%) ── d\n\
+\n\
+20.20% (100.0%) -- other3\n\
+└──20.20% (100.0%) -- a/b/c/d\n\
+ ├──20.00% (99.01%) ── e\n\
+ ├───0.10% (00.50%) ── f\n\
+ └───0.10% (00.50%) ── g [2]\n\
+\n\
+100,000 (100.0%) -- other6\n\
+├───99,999 (100.0%) ── big\n\
+└────────1 (00.00%) ── small\n\
+\n\
+0 B (100.0%) -- other7\n\
+└──0 B (100.0%) ── zero\n\
+\n\
+931,135,488 B ── explicit\n\
+524,288,000 B ── heap-allocated\n\
+104,857,600 B ── heap-unallocated\n\
+232,783,872 B ── other2\n\
+ 777 ── other3\n\
+ 888 ── other4 [2]\n\
+ 45.67% ── perc1\n\
+ 100.00% ── perc2\n\
+\n\
+End of Main Process\n\
+4th\n\
+\n\
+WARNING: the following values are negative or unreasonably large.\n\
+\n\
+ explicit/js/compartment(http://too-big.com/)/stuff\n\
+ explicit/neg1\n\
+ explicit/heap-unclassified\n\
+ other1\n\
+ other2\n\
+ other3\n\
+ other4\n\
+ other5 \n\
+\n\
+This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\
+Explicit Allocations\n\
+\n\
+102,760,448 B (100.0%) -- explicit\n\
+├──157,286,400 B (153.06%) ── js/compartment(http://too-big.com/)/stuff [?!]\n\
+├────5,242,880 B (05.10%) ── ok\n\
+├───-2,097,152 B (-02.04%) ── neg1 [?!]\n\
+└──-57,671,680 B (-56.12%) ── heap-unclassified [?!]\n\
+\n\
+Other Measurements\n\
+\n\
+ 104,857,600 B ── heap-allocated\n\
+ -111 B ── other1 [?!]\n\
+-232,783,872 B ── other2 [?!]\n\
+ -333 ── other3 [?!]\n\
+ -444 ── other4 [?!]\n\
+ -5.55% ── other5 [?!]\n\
+ 666.66% ── other6\n\
+ 209,715,200 B ── resident\n\
+\n\
+End of 4th\n\
+3rd\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+814,743,552 B (100.0%) -- explicit\n\
+└──814,743,552 B (100.0%) -- a\n\
+ ├──465,567,744 B (57.14%) ── c\n\
+ └──349,175,808 B (42.86%) ── b\n\
+\n\
+Other Measurements\n\
+\n\
+ 1,048,576 B ── other1\n\
+104,857,600 B ── resident\n\
+\n\
+End of 3rd\n\
+2nd\n\
+Explicit Allocations\n\
+\n\
+1,048,576,000 B (100.0%) -- explicit\n\
+├────523,239,424 B (49.90%) ── a/b/c [3]\n\
+├────209,715,200 B (20.00%) ── compartment(compartment-url)\n\
+├────209,715,200 B (20.00%) ── flip/the/backslashes\n\
+└────105,906,176 B (10.10%) ── heap-unclassified\n\
+\n\
+Other Measurements\n\
+\n\
+1,048,576,000 B ── heap-allocated\n\
+ 104,857,600 B ── heap-unallocated\n\
+ 698,351,616 B ── other0\n\
+ 116,391,936 B ── other1\n\
+\n\
+End of 2nd\n\
+5th\n\
+\n\
+WARNING: the following values are negative or unreasonably large.\n\
+\n\
+ explicit/a/neg2\n\
+ explicit/a/neg1\n\
+ explicit/b/c\n\
+ explicit/b/c/d\n\
+ explicit/b/c/d/f \n\
+\n\
+This indicates a defect in one or more memory reporters. The invalid values are highlighted.\n\
+Explicit Allocations\n\
+\n\
+104,801,280 B (100.0%) -- explicit\n\
+├──103,809,024 B (99.05%) ── big\n\
+├────1,007,616 B (00.96%) ── heap-unclassified\n\
+├───────10,240 B (00.01%) -- a\n\
+│ ├──40,960 B (00.04%) ── pos\n\
+│ ├──-10,240 B (-00.01%) ── neg2 [?!]\n\
+│ └──-20,480 B (-00.02%) ── neg1 [?!]\n\
+└──────-25,600 B (-00.02%) -- b/c [?!]\n\
+ ├───10,240 B (00.01%) ── g/h\n\
+ ├────5,120 B (00.00%) ── i/j\n\
+ └──-40,960 B (-00.04%) -- d [?!]\n\
+ ├───20,480 B (00.02%) ── e\n\
+ └──-61,440 B (-00.06%) ── f [?!]\n\
+\n\
+Other Measurements\n\
+\n\
+104,857,600 B ── heap-allocated\n\
+\n\
+End of 5th\n\
+";
+
+ function finish()
+ {
+ mgr.unblockRegistrationAndRestoreOriginalReporters();
+ SimpleTest.finish();
+ }
+
+ // Cut+paste the entire page and check that the cut text matches what we
+ // expect. This tests the output in general and also that the cutting and
+ // pasting works as expected.
+ function test(aFrameId, aVerbose, aExpected, aNext) {
+ SimpleTest.executeSoon(function() {
+ ok(document.title === "about:memory", "document.title is correct");
+ let mostRecentActual;
+ let frame = document.getElementById(aFrameId);
+ frame.focus();
+
+ // Set the verbose checkbox value and click the go button.
+ let doc = frame.contentWindow.document;
+ let measureButton = doc.getElementById("measureButton");
+ let verbose = doc.getElementById("verbose");
+ verbose.checked = aVerbose;
+ measureButton.click();
+
+ SimpleTest.waitForClipboard(
+ function(aActual) {
+ mostRecentActual = aActual;
+ let rslt = aActual.trim() === aExpected.trim();
+ if (!rslt) {
+ // Try copying again.
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ }
+
+ return rslt;
+ },
+ function() {
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ },
+ aNext,
+ function() {
+ ok(false, "pasted text doesn't match for " + aFrameId);
+ dump("******EXPECTED******\n");
+ dump("<<<" + aExpected + ">>>\n");
+ dump("*******ACTUAL*******\n");
+ dump("<<<" + mostRecentActual + ">>>\n");
+ dump("********************\n");
+ finish();
+ }
+ );
+ });
+ }
+
+ SimpleTest.waitForFocus(function() {
+ test(
+ "amFrame",
+ /* verbose = */ false,
+ amExpectedText,
+ function() {
+ test(
+ "amvFrame",
+ /* verbose = */ true,
+ amvExpectedText,
+ function() {
+ finish()
+ }
+ )
+ }
+ );
+ });
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml
new file mode 100644
index 0000000000..28c8f7fa4f
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml
@@ -0,0 +1,420 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file tests the collapsing and expanding of sub-trees in
+ about:memory. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ // Hide all the real reporters; we'll restore them at the end.
+ mgr.blockRegistrationAndHideExistingReporters();
+
+ // Setup various fake-but-deterministic reporters.
+ const KB = 1024;
+ const MB = KB * KB;
+ const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
+ const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
+ const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+
+ let hiPath = "explicit/h/i";
+ let hi2Path = "explicit/h/i2";
+ let jkPath = "explicit/j/k";
+ let jk2Path = "explicit/j/k2";
+
+ let fakeReporters = [
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP, aK, aA) {
+ aCbObj.callback("Main Process ABC", aP, aK, BYTES, aA, "Desc.", aClosure);
+ }
+ f("heap-allocated", OTHER, 250 * MB);
+ f("explicit/a/b", HEAP, 50 * MB);
+ f("explicit/a/c/d", HEAP, 25 * MB);
+ f("explicit/a/c/e", HEAP, 15 * MB);
+ f("explicit/a/f", HEAP, 30 * MB);
+ f("explicit/g", HEAP, 100 * MB);
+ f(hiPath, HEAP, 10 * MB);
+ f(hi2Path, HEAP, 9 * MB);
+ f(jkPath, HEAP, 0.5 * MB);
+ f(jk2Path, HEAP, 0.3 * MB);
+ f("explicit/a/l/m", HEAP, 0.1 * MB);
+ f("explicit/a/l/n", HEAP, 0.1 * MB);
+ }
+ }
+ ];
+
+ for (let i = 0; i < fakeReporters.length; i++) {
+ mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]);
+ }
+
+ ]]>
+ </script>
+
+ <iframe id="amFrame" height="500" src="about:memory"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function finish()
+ {
+ mgr.unblockRegistrationAndRestoreOriginalReporters();
+ SimpleTest.finish();
+ }
+
+ // Click on the identified element, then cut+paste the entire page and
+ // check that the cut text matches what we expect.
+ function test(aId, aSwap, aExpected, aNext) {
+ let win = document.getElementById("amFrame").contentWindow;
+ if (aId) {
+ let node = win.document.getElementById(aId);
+
+ // Yuk: clicking a button is easy; but for tree entries we need to
+ // click on a child of the span identified via |id|.
+ if (node.nodeName === "button") {
+ if (aSwap) {
+ // We swap hipath/hi2Path and jkPath/jk2Path just before updating, to
+ // test what happens when significant nodes become insignificant and
+ // vice versa.
+ hiPath = "explicit/j/k";
+ hi2Path = "explicit/j/k2";
+ jkPath = "explicit/h/i";
+ jk2Path = "explicit/h/i2";
+ }
+ node.click();
+ } else {
+ node.childNodes[0].click();
+ }
+ }
+
+ SimpleTest.executeSoon(function() {
+ let mostRecentActual;
+ document.getElementById("amFrame").focus();
+ SimpleTest.waitForClipboard(
+ function(aActual) {
+ mostRecentActual = aActual;
+ let rslt = aActual.trim() === aExpected.trim();
+ if (!rslt) {
+ // Try copying again.
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ }
+
+ return rslt;
+ },
+ function() {
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ },
+ aNext,
+ function() {
+ ok(false, "pasted text doesn't match");
+ dump("******EXPECTED******\n");
+ dump(aExpected);
+ dump("*******ACTUAL*******\n");
+ dump(mostRecentActual);
+ dump("********************\n");
+ finish();
+ }
+ );
+ });
+ }
+
+ // Returns a function that chains together one test() call per id.
+ function chain(aIds) {
+ let x = aIds.shift();
+ if (x) {
+ return function() { test(x.id, x.swap, x.expected, chain(aIds)); }
+ }
+ return function() { finish(); };
+ }
+
+ let startExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) -- c\n\
+│ │ ├──25.00 MB (10.00%) ── d\n\
+│ │ └──15.00 MB (06.00%) ── e\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) ++ l\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- h\n\
+│ ├──10.00 MB (04.00%) ── i\n\
+│ └───9.00 MB (03.60%) ── i2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ j\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let acCollapsedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) ++ c\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) ++ l\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- h\n\
+│ ├──10.00 MB (04.00%) ── i\n\
+│ └───9.00 MB (03.60%) ── i2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ j\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let alExpandedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) ++ c\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) -- l\n\
+│ ├──0.10 MB (00.04%) ── m\n\
+│ └──0.10 MB (00.04%) ── n\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- h\n\
+│ ├──10.00 MB (04.00%) ── i\n\
+│ └───9.00 MB (03.60%) ── i2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ j\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let aCollapsedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) ++ a\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- h\n\
+│ ├──10.00 MB (04.00%) ── i\n\
+│ └───9.00 MB (03.60%) ── i2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ j\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let hCollapsedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) ++ a\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) ++ h\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ j\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let jExpandedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) ++ a\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) ++ h\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) -- j\n\
+ ├──0.50 MB (00.20%) ── k\n\
+ └──0.30 MB (00.12%) ── k2\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ // The important thing here is that two values have been swapped.
+ // explicit/h/i should remain collapsed, and explicit/j/k should remain
+ // expanded. See bug 724863.
+ let updatedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) ++ a\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- j\n\
+│ ├──10.00 MB (04.00%) ── k\n\
+│ └───9.00 MB (03.60%) ── k2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ h\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let aExpandedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) ++ c\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) -- l\n\
+│ ├──0.10 MB (00.04%) ── m\n\
+│ └──0.10 MB (00.04%) ── n\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- j\n\
+│ ├──10.00 MB (04.00%) ── k\n\
+│ └───9.00 MB (03.60%) ── k2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ h\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let acExpandedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) -- c\n\
+│ │ ├──25.00 MB (10.00%) ── d\n\
+│ │ └──15.00 MB (06.00%) ── e\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) -- l\n\
+│ ├──0.10 MB (00.04%) ── m\n\
+│ └──0.10 MB (00.04%) ── n\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- j\n\
+│ ├──10.00 MB (04.00%) ── k\n\
+│ └───9.00 MB (03.60%) ── k2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ h\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ let alCollapsedExpected =
+"\
+Main Process ABC\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) -- c\n\
+│ │ ├──25.00 MB (10.00%) ── d\n\
+│ │ └──15.00 MB (06.00%) ── e\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) ++ l\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- j\n\
+│ ├──10.00 MB (04.00%) ── k\n\
+│ └───9.00 MB (03.60%) ── k2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ h\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process ABC\n\
+";
+
+ // Test the following cases:
+ // - explicit/a/c is significant, we collapse it, it's unchanged upon
+ // update, we re-expand it
+ // - explicit/a/l is insignificant, we expand it, it's unchanged upon
+ // update, we re-collapse it
+ // - explicit/a is significant, we collapse it (which hides its
+ // sub-trees), it's unchanged upon update, we re-expand it
+ // - explicit/h is significant, we collapse it, it becomes insignificant
+ // upon update (and should remain collapsed)
+ // - explicit/j is insignificant, we expand it, it becomes significant
+ // upon update (and should remain expanded)
+ //
+ let idsToClick = [
+ { id: "measureButton", swap: 0, expected: startExpected },
+ { id: "Main Process ABC:explicit/a/c", swap: 0, expected: acCollapsedExpected },
+ { id: "Main Process ABC:explicit/a/l", swap: 0, expected: alExpandedExpected },
+ { id: "Main Process ABC:explicit/a", swap: 0, expected: aCollapsedExpected },
+ { id: "Main Process ABC:explicit/h", swap: 0, expected: hCollapsedExpected },
+ { id: "Main Process ABC:explicit/j", swap: 0, expected: jExpandedExpected },
+ { id: "measureButton", swap: 1, expected: updatedExpected },
+ { id: "Main Process ABC:explicit/a", swap: 0, expected: aExpandedExpected },
+ { id: "Main Process ABC:explicit/a/c", swap: 0, expected: acExpandedExpected },
+ { id: "Main Process ABC:explicit/a/l", swap: 0, expected: alCollapsedExpected }
+ ];
+
+ SimpleTest.waitForFocus(chain(idsToClick));
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml
new file mode 100644
index 0000000000..1391af38b7
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml
@@ -0,0 +1,558 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file tests the saving and loading of memory reports to/from file in
+ about:memory. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ // Hide all the real reporters; we'll restore them at the end.
+ mgr.blockRegistrationAndHideExistingReporters();
+
+ // Setup a minimal number of fake reporters.
+ const KB = 1024;
+ const MB = KB * KB;
+ const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
+ const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
+ const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+
+ let fakeReporters = [
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP, aK, aA, aD) {
+ aCbObj.callback("", aP, aK, BYTES, aA, aD, aClosure);
+ }
+ f("heap-allocated", OTHER, 250 * MB, "Heap allocated.");
+ f("explicit/a/b", HEAP, 50 * MB, "A b.");
+ f("other/a", OTHER, 0.2 * MB, "Other a.");
+ f("other/b", OTHER, 0.1 * MB, "Other b.");
+ }
+ }
+ ];
+
+ for (let i = 0; i < fakeReporters.length; i++) {
+ mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]);
+ }
+
+ ]]>
+ </script>
+
+ <iframe id="amFrame" height="400" src="about:memory"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function finish()
+ {
+ mgr.unblockRegistrationAndRestoreOriginalReporters();
+ SimpleTest.finish();
+ }
+
+ // Load the given file into the frame, then copy+paste the entire frame and
+ // check that the cut text matches what we expect.
+ function test(aFilename, aFilename2, aExpected, aDumpFirst, aVerbose, aNext) {
+ let frame = document.getElementById("amFrame");
+ frame.focus();
+
+ let doc = frame.contentWindow.document;
+ let verbosity = doc.getElementById("verbose");
+ verbosity.checked = aVerbose;
+
+ function getFilePath(aFilename) {
+ let file = SpecialPowers.Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ file.append("chrome");
+ file.append("toolkit");
+ file.append("components");
+ file.append("aboutmemory");
+ file.append("tests");
+ file.append(aFilename);
+ return file.path;
+ }
+
+ let filePath = getFilePath(aFilename);
+
+ let e = document.createEvent('Event');
+ e.initEvent('change', true, true);
+
+ function check() {
+ // Initialize the clipboard contents.
+ SpecialPowers.clipboardCopyString("initial clipboard value");
+
+ let numFailures = 0, maxFailures = 30;
+
+ // Because the file load is async, we don't know when it will finish and
+ // the output will show up. So we poll.
+ function copyPasteAndCheck() {
+ // Copy and paste frame contents, and filter out non-deterministic
+ // differences.
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ let actual = SpecialPowers.getClipboardData("text/plain");
+ actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)");
+
+ if (actual.trim() === aExpected.trim()) {
+ SimpleTest.ok(true, "Clipboard has the expected contents");
+ aNext();
+ } else {
+ numFailures++;
+ if (numFailures === maxFailures) {
+ ok(false, "pasted text doesn't match");
+ dump("******EXPECTED******\n");
+ dump(aExpected);
+ dump("*******ACTUAL*******\n");
+ dump(actual);
+ dump("********************\n");
+ finish();
+ } else {
+ setTimeout(copyPasteAndCheck, 100);
+ }
+ }
+ }
+ copyPasteAndCheck();
+ }
+
+ if (!aFilename2) {
+ function loadAndCheck() {
+ let fileInput1 =
+ frame.contentWindow.document.getElementById("fileInput1");
+ fileInput1.value = filePath; // this works because it's a chrome test
+
+ fileInput1.dispatchEvent(e);
+ check();
+ }
+
+ if (aDumpFirst) {
+ let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
+ getService(Ci.nsIMemoryInfoDumper);
+ dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null,
+ /* anonymize = */ false,
+ /* minimize memory usage = */ false);
+ } else {
+ loadAndCheck();
+ }
+
+ } else {
+ let fileInput2 =
+ frame.contentWindow.document.getElementById("fileInput2");
+ fileInput2.value = filePath; // this works because it's a chrome test
+
+ // Hack alert: fileInput2's onchange handler calls fileInput2.click().
+ // But we don't want that to happen, because we want to bypass the file
+ // picker for the test. So we set |e.skipClick|, which causes
+ // fileInput2.click() to be skipped, and dispatch the second change event
+ // directly ourselves.
+
+ e.skipClick = true;
+ fileInput2.dispatchEvent(e);
+
+ let filePath2 = getFilePath(aFilename2);
+ fileInput2.value = filePath2; // this works because it's a chrome test
+
+ let e2 = document.createEvent('Event');
+ e2.initEvent('change', true, true);
+ fileInput2.dispatchEvent(e);
+
+ check();
+ }
+ }
+
+ // Returns a function that chains together multiple test() calls.
+ function chain(aPieces) {
+ let x = aPieces.shift();
+ if (x) {
+ return function() { test(x.filename, x.filename2, x.expected, x.dumpFirst, x.verbose, chain(aPieces)); }
+ }
+ return function() { finish(); };
+ }
+
+ let expectedGood =
+"\
+Main Process (pid NNN)\n\
+Explicit Allocations\n\
+\n\
+262,144,000 B (100.0%) -- explicit\n\
+├──209,715,200 B (80.00%) ── heap-unclassified\n\
+└───52,428,800 B (20.00%) ── a/b\n\
+\n\
+Other Measurements\n\
+\n\
+1,024 B (100.0%) -- compartments\n\
+└──1,024 B (100.0%) ── system/a\n\
+\n\
+1,024 B (100.0%) -- ghost-windows\n\
+└──1,024 B (100.0%) ── a\n\
+\n\
+314,572 B (100.0%) -- other\n\
+├──209,715 B (66.67%) ── a\n\
+└──104,857 B (33.33%) ── b\n\
+\n\
+1,024 B (100.0%) -- pss\n\
+└──1,024 B (100.0%) ── a\n\
+\n\
+1,024 B (100.0%) -- rss\n\
+└──1,024 B (100.0%) ── a\n\
+\n\
+1,024 B (100.0%) -- size\n\
+└──1,024 B (100.0%) ── a\n\
+\n\
+1,024 B (100.0%) -- swap\n\
+└──1,024 B (100.0%) ── a\n\
+\n\
+262,144,000 B ── heap-allocated\n\
+\n\
+End of Main Process (pid NNN)\n\
+Explicit-only process\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+100,000 B (100.0%) -- explicit\n\
+└──100,000 B (100.0%) ── a/b\n\
+\n\
+End of Explicit-only process\n\
+Heap-unclassified process\n\
+Explicit Allocations\n\
+\n\
+262,144,000 B (100.0%) -- explicit\n\
+├──209,715,200 B (80.00%) ── heap-unclassified\n\
+└───52,428,800 B (20.00%) ── a/b\n\
+\n\
+Other Measurements\n\
+\n\
+262,144,000 B ── heap-allocated\n\
+\n\
+End of Heap-unclassified process\n\
+Other-only process\n\
+Other Measurements\n\
+\n\
+200,000 B (100.0%) -- a\n\
+├──100,000 B (50.00%) ── b\n\
+└──100,000 B (50.00%) ── c\n\
+\n\
+500,000 B ── heap-allocated\n\
+\n\
+End of Other-only process\n\
+";
+
+ let expectedGood2 =
+"\
+Main Process (pid NNN)\n\
+Explicit Allocations\n\
+\n\
+262,144,000 B (100.0%) -- explicit\n\
+├──209,715,200 B (80.00%) ── heap-unclassified\n\
+└───52,428,800 B (20.00%) ── a/b\n\
+\n\
+Other Measurements\n\
+\n\
+314,572 B (100.0%) -- other\n\
+├──209,715 B (66.67%) ── a\n\
+└──104,857 B (33.33%) ── b\n\
+\n\
+262,144,000 B ── heap-allocated\n\
+\n\
+End of Main Process (pid NNN)\n\
+";
+
+ // This is the output for a malformed data file.
+ let expectedBad =
+"\
+Error: Invalid memory report(s): missing 'hasMozMallocUsableSize' property\
+";
+
+ // This is the output for a non-verbose diff.
+ let expectedDiffNonVerbose =
+"\
+P\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+-0.01 MB (100.0%) -- explicit\n\
+├──-0.01 MB (99.95%) ── storage/prefixset/goog-phish-shavar\n\
+└──-0.00 MB (00.05%) ++ (2 tiny)\n\
+\n\
+Other Measurements\n\
+\n\
+0.96 MB (100.0%) -- a\n\
+├──0.95 MB (99.80%) ── b\n\
+├──0.00 MB (00.10%) -- c\n\
+│ ├──-0.95 MB (-99.70%) ── e\n\
+│ ├──0.95 MB (99.60%) ── d\n\
+│ └──0.00 MB (00.20%) ++ (2 tiny)\n\
+└──0.00 MB (00.10%) ── h\n\
+\n\
+ 0.00 MB ── canvas-2d-pixel-bytes [2] [+]\n\
+-0.00 MB ── foobar [-]\n\
+\n\
+End of P\n\
+P2 (pid NNN)\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+0.00 MB (100.0%) -- explicit\n\
+└──0.00 MB (100.0%) ── window-objects/top(bar.com, id=NNN)/...\n\
+\n\
+Other Measurements\n\
+\n\
+0.00 MB (100.0%) -- p3\n\
+└──0.00 MB (100.0%) ── zone(0xNNN)/p3\n\
+\n\
+0.00 MB (100.0%) -- p4\n\
+└──0.00 MB (100.0%) ── js-zone(0xNNN)/p4\n\
+\n\
+0.00 MB (100.0%) -- p5\n\
+└──0.00 MB (100.0%) ── worker(foo.com, 0xNNN)/p5\n\
+\n\
+0.00 MB (100.0%) -- p6\n\
+└──0.00 MB (100.0%) ── z-moz-nullprincipal:{NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN}/p6\n\
+\n\
+0.00 MB (100.0%) -- p7\n\
+└──0.00 MB (100.0%) ── js-main-runtime-compartments/system/jar:file:///.../omni.ja!/p7\n\
+\n\
+0.00 MB ── p1 (pid NNN)\n\
+0.00 MB ── p2 (blah, pid=NNN)\n\
+\n\
+End of P2 (pid NNN)\n\
+P3\n\
+Other Measurements\n\
+\n\
+-0.00 MB ── p3 [-]\n\
+\n\
+End of P3\n\
+P4\n\
+Other Measurements\n\
+\n\
+0.00 MB ── p4 [+]\n\
+\n\
+End of P4\n\
+P7\n\
+Other Measurements\n\
+\n\
+0.00 MB (100.0%) -- p7\n\
+├──0.00 MB (57.14%) ── c [+]\n\
+└──0.00 MB (42.86%) ── b [+]\n\
+\n\
+-0.00 MB ── p7 [-]\n\
+\n\
+End of P7\n\
+P8\n\
+Other Measurements\n\
+\n\
+-0.00 MB (100.0%) -- p8\n\
+└──-0.00 MB (100.0%) -- a\n\
+ ├──-0.00 MB (50.00%) -- b\n\
+ │ ├──-0.00 MB (31.82%) -- c\n\
+ │ │ ├──-0.00 MB (18.18%) ── e [-]\n\
+ │ │ └──-0.00 MB (13.64%) ── d [-]\n\
+ │ ├──-0.00 MB (22.73%) ── f [-]\n\
+ │ └───0.00 MB (-04.55%) ── (fake child) [!]\n\
+ └──-0.00 MB (50.00%) -- g\n\
+ ├──-0.00 MB (31.82%) ── i [-]\n\
+ ├──-0.00 MB (27.27%) ── h [-]\n\
+ └───0.00 MB (-09.09%) ── (fake child) [!]\n\
+\n\
+End of P8\n\
+P9\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+0.00 MB (100.0%) -- explicit\n\
+└──0.00 MB (100.0%) ── threads/stacks/DNS Resolver #N (tid=NNN) [3]\n\
+\n\
+End of P9\n\
+";
+
+ // This is the output for a verbose diff.
+ let expectedDiffVerbose =
+"\
+P\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+-10,005 B (100.0%) -- explicit\n\
+├──-10,000 B (99.95%) ── storage/prefixset/goog-phish-shavar\n\
+├───────-6 B (00.06%) ── spell-check [2]\n\
+└────────1 B (-00.01%) ── xpcom/category-manager\n\
+\n\
+Other Measurements\n\
+\n\
+1,002,000 B (100.0%) -- a\n\
+├──1,000,000 B (99.80%) ── b\n\
+├──────1,000 B (00.10%) -- c\n\
+│ ├──-999,000 B (-99.70%) ── e\n\
+│ ├──998,000 B (99.60%) ── d\n\
+│ ├──1,000 B (00.10%) ── f\n\
+│ └──1,000 B (00.10%) ── g\n\
+└──────1,000 B (00.10%) ── h\n\
+\n\
+3,000 B ── canvas-2d-pixel-bytes [2] [+]\n\
+ -100 B ── foobar [-]\n\
+\n\
+End of P\n\
+P2 (pid NNN)\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+11 B (100.0%) -- explicit\n\
+└──11 B (100.0%) ── window-objects/top(bar.com, id=NNN)/...\n\
+\n\
+Other Measurements\n\
+\n\
+11 B (100.0%) -- p3\n\
+└──11 B (100.0%) ── zone(0xNNN)/p3\n\
+\n\
+11 B (100.0%) -- p4\n\
+└──11 B (100.0%) ── js-zone(0xNNN)/p4\n\
+\n\
+11 B (100.0%) -- p5\n\
+└──11 B (100.0%) ── worker(foo.com, 0xNNN)/p5\n\
+\n\
+11 B (100.0%) -- p6\n\
+└──11 B (100.0%) ── z-moz-nullprincipal:{NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN}/p6\n\
+\n\
+11 B (100.0%) -- p7\n\
+└──11 B (100.0%) ── js-main-runtime-compartments/system/jar:file:///.../omni.ja!/p7\n\
+\n\
+11 B ── p1 (pid NNN)\n\
+11 B ── p2 (blah, pid=NNN)\n\
+\n\
+End of P2 (pid NNN)\n\
+P3\n\
+Other Measurements\n\
+\n\
+-55 B ── p3 [-]\n\
+\n\
+End of P3\n\
+P4\n\
+Other Measurements\n\
+\n\
+66 B ── p4 [+]\n\
+\n\
+End of P4\n\
+P7\n\
+Other Measurements\n\
+\n\
+7 B (100.0%) -- p7\n\
+├──4 B (57.14%) ── c [+]\n\
+└──3 B (42.86%) ── b [+]\n\
+\n\
+-5 B ── p7 [-]\n\
+\n\
+End of P7\n\
+P8\n\
+Other Measurements\n\
+\n\
+-22 B (100.0%) -- p8\n\
+└──-22 B (100.0%) -- a\n\
+ ├──-11 B (50.00%) -- b\n\
+ │ ├───-7 B (31.82%) -- c\n\
+ │ │ ├──-4 B (18.18%) ── e [-]\n\
+ │ │ └──-3 B (13.64%) ── d [-]\n\
+ │ ├───-5 B (22.73%) ── f [-]\n\
+ │ └────1 B (-04.55%) ── (fake child) [!]\n\
+ └──-11 B (50.00%) -- g\n\
+ ├───-7 B (31.82%) ── i [-]\n\
+ ├───-6 B (27.27%) ── h [-]\n\
+ └────2 B (-09.09%) ── (fake child) [!]\n\
+\n\
+End of P8\n\
+P9\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+5,000 B (100.0%) -- explicit\n\
+└──5,000 B (100.0%) ── threads/stacks/DNS Resolver #N (tid=NNN) [3]\n\
+\n\
+End of P9\n\
+";
+
+ // This is the output for the crash reports diff.
+ let expectedDiff2 =
+"\
+Main Process (pid NNN)\n\
+Other Measurements\n\
+\n\
+1 B ── heap-allocated\n\
+\n\
+End of Main Process (pid NNN)\n\
+";
+
+ let expectedDiffFiss =
+"\
+P (pid NNN)\n\
+Explicit Allocations\n\
+\n\
+3,000,000 B (100.0%) -- explicit\n\
+├──2,999,700 B (99.99%) ── heap-unclassified\n\
+└────────300 B (00.01%) ── foobar\n\
+\n\
+Other Measurements\n\
+\n\
+3,000,000 B ── heap-allocated\n\
+\n\
+End of P (pid NNN)\n\
+web (pid NNN)\n\
+Explicit Allocations\n\
+\n\
+2,000,000 B (100.0%) -- explicit\n\
+├──1,000,000 B (50.00%) ── a/c/d\n\
+└──1,000,000 B (50.00%) ── heap-unclassified\n\
+\n\
+Other Measurements\n\
+\n\
+2,000,000 B ── heap-allocated\n\
+\n\
+End of web (pid NNN)\n\
+";
+
+ let frames = [
+ // This loads a pre-existing memory reports file that is valid.
+ { filename: "memory-reports-good.json", expected: expectedGood, dumpFirst: false, verbose: true },
+
+ // This loads a pre-existing crash dump file that is valid.
+ { filename: "crash-dump-good.json", expected: expectedGood2, dumpFirst: false, verbose: true },
+
+ // This dumps to a file and then reads it back in. (The result is the same
+ // as the previous test.)
+ { filename: "memory-reports-dumped.json.gz", expected: expectedGood2, dumpFirst: true, verbose: true },
+
+ // This loads a pre-existing file that is invalid.
+ { filename: "memory-reports-bad.json", expected: expectedBad, dumpFirst: false, verbose: true },
+
+ // This diffs two pre-existing memory reports files.
+ { filename: "memory-reports-diff1.json", filename2: "memory-reports-diff2.json", expected: expectedDiffNonVerbose, dumpFirst: false, verbose: false },
+
+ // Ditto.
+ { filename: "memory-reports-diff1.json", filename2: "memory-reports-diff2.json", expected: expectedDiffVerbose, dumpFirst: false, verbose: true },
+
+ // This diffs two pre-existing crash report files.
+ { filename: "crash-dump-diff1.json", filename2: "crash-dump-diff2.json", expected: expectedDiff2, dumpFirst: false, verbose: true },
+
+ // This diffs a non-Fission and a Fission memory report.
+ { filename: "fiss-diff1.json", filename2: "fiss-diff2.json", expected: expectedDiffFiss, dumpFirst: false, verbose: true }
+ ];
+
+ SimpleTest.waitForFocus(chain(frames));
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml
new file mode 100644
index 0000000000..9d9db694f2
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml
@@ -0,0 +1,176 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file tests the loading of memory reports from file when specified
+ in about:memory's URL (via the "file=" suffix). -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ function makePathname(aFilename) {
+ let file = SpecialPowers.Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ file.append("chrome");
+ file.append("toolkit");
+ file.append("components");
+ file.append("aboutmemory");
+ file.append("tests");
+ file.append(aFilename);
+ return file.path;
+ }
+
+ // Load the given file into the frame, then copy+paste the entire frame and
+ // check that the cut text matches what we expect.
+ function test(aFilename, aExpected, aNext) {
+ let frame = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe")
+ frame.height = 300;
+ frame.src = "about:memory?file=" + makePathname(aFilename);
+ document.documentElement.appendChild(frame);
+ frame.addEventListener("load", function onFrameLoad(e) {
+ frame.focus();
+
+ // Initialize the clipboard contents.
+ SpecialPowers.clipboardCopyString("initial clipboard value");
+
+ let numFailures = 0, maxFailures = 30;
+
+ // Because the file load is async, we don't know when it will finish and
+ // the output will show up. So we poll.
+ function copyPasteAndCheck() {
+ // Copy and paste frame contents, and filter out non-deterministic
+ // differences.
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ let actual = SpecialPowers.getClipboardData("text/plain");
+ actual = actual.replace(/\(pid \d+\)/, "(pid NNN)");
+
+ if (actual.trim() === aExpected.trim()) {
+ SimpleTest.ok(true, "Clipboard has the expected contents");
+ aNext();
+ } else {
+ numFailures++;
+ if (numFailures === maxFailures) {
+ ok(false, "pasted text doesn't match");
+ dump("******EXPECTED******\n");
+ dump(aExpected);
+ dump("*******ACTUAL*******\n");
+ dump(actual);
+ dump("********************\n");
+ SimpleTest.finish();
+ } else {
+ setTimeout(copyPasteAndCheck, 100);
+ }
+ }
+ }
+ copyPasteAndCheck();
+ }, {once: true});
+ }
+
+ // Returns a function that chains together multiple test() calls.
+ function chain(aFrameIds) {
+ let x = aFrameIds.shift();
+ if (x) {
+ return function() { test(x.filename, x.expected, chain(aFrameIds)); }
+ }
+ return function() { SimpleTest.finish(); };
+ }
+
+ // This is pretty simple output, but that's ok; this file is about testing
+ // the loading of data from file. If we got this far, we're doing fine.
+ let expectedGood =
+"\
+Main Process (pid NNN)\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──200.00 MB (80.00%) ── heap-unclassified\n\
+└───50.00 MB (20.00%) ── a/b\n\
+\n\
+Other Measurements\n\
+\n\
+0.00 MB (100.0%) -- compartments\n\
+└──0.00 MB (100.0%) ── system/a\n\
+\n\
+0.00 MB (100.0%) -- ghost-windows\n\
+└──0.00 MB (100.0%) ── a\n\
+\n\
+0.30 MB (100.0%) -- other\n\
+├──0.20 MB (66.67%) ── a\n\
+└──0.10 MB (33.33%) ── b\n\
+\n\
+0.00 MB (100.0%) -- pss\n\
+└──0.00 MB (100.0%) ── a\n\
+\n\
+0.00 MB (100.0%) -- rss\n\
+└──0.00 MB (100.0%) ── a\n\
+\n\
+0.00 MB (100.0%) -- size\n\
+└──0.00 MB (100.0%) ── a\n\
+\n\
+0.00 MB (100.0%) -- swap\n\
+└──0.00 MB (100.0%) ── a\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process (pid NNN)\n\
+Explicit-only process\n\
+\n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
+Explicit Allocations\n\
+\n\
+0.10 MB (100.0%) -- explicit\n\
+└──0.10 MB (100.0%) ── a/b\n\
+\n\
+End of Explicit-only process\n\
+Heap-unclassified process\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──200.00 MB (80.00%) ── heap-unclassified\n\
+└───50.00 MB (20.00%) ── a/b\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Heap-unclassified process\n\
+Other-only process\n\
+Other Measurements\n\
+\n\
+0.19 MB (100.0%) -- a\n\
+├──0.10 MB (50.00%) ── b\n\
+└──0.10 MB (50.00%) ── c\n\
+\n\
+0.48 MB ── heap-allocated\n\
+\n\
+End of Other-only process\n\
+";
+
+ // This is the output for a malformed data file.
+ let expectedBad =
+"\
+Error: Invalid memory report(s): missing 'hasMozMallocUsableSize' property";
+
+ let frames = [
+ // This loads a pre-existing file that is valid.
+ { filename: "memory-reports-good.json", expected: expectedGood },
+
+ // This loads a pre-existing file that is valid.
+ { filename: "memory-reports-bad.json", expected: expectedBad }
+ ];
+
+ SimpleTest.waitForFocus(chain(frames));
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory5.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory5.xhtml
new file mode 100644
index 0000000000..7b63179767
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory5.xhtml
@@ -0,0 +1,169 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file tests the saving and loading of memory reports to/from file in
+ about:memory in the presence of child processes. It is also notable
+ for being an about:memory test that uses the real reporters, rather
+ than fake deterministic ones, and so tends to show up problems in the
+ real reporters (like bogus negative values). -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <iframe id="amFrame" height="400" src="about:memory"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ let numRemotes = 3;
+ let numReady = 0;
+
+ // Create some remote processes, and set up message-passing so that
+ // we know when each child is fully initialized.
+ let remotes = [];
+
+ let prefs = [
+ ["dom.ipc.processCount", 3], // Allow up to 3 child processes
+ ["memory.report_concurrency", 2] // Cover more child handling cases
+ ];
+
+ SpecialPowers.pushPrefEnv({"set": prefs}).then(function() {
+ for (let i = 0; i < numRemotes; i++) {
+ let w = remotes[i] = window.browsingContext.topChromeWindow.open("remote.xhtml", "", "chrome");
+
+ w.addEventListener("load", function loadHandler() {
+ let remoteBrowser = w.document.getElementById("remote");
+ let mm = remoteBrowser.messageManager;
+ mm.addMessageListener("test:ready", function readyHandler() {
+ mm.removeMessageListener("test:ready", readyHandler);
+ numReady++;
+ if (numReady == numRemotes) {
+ // All the remote processes are ready.
+ SimpleTest.waitForFocus(onFocus);
+ }
+ });
+ mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true);
+ });
+ }
+ });
+
+ // Load the given file into the frame, then copy+paste the entire frame and
+ // check that the cut text matches what we expect.
+ function onFocus() {
+ let frame = document.getElementById("amFrame");
+ frame.focus();
+
+ function getFilePath(aFilename) {
+ let file = SpecialPowers.Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ file.append("chrome");
+ file.append("toolkit");
+ file.append("components");
+ file.append("aboutmemory");
+ file.append("tests");
+ file.append(aFilename);
+ return file.path;
+ }
+
+ let filePath = getFilePath("memory-reports-dumped.json.gz");
+
+ let e = document.createEvent('Event');
+ e.initEvent('change', true, true);
+
+ let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
+ getService(Ci.nsIMemoryInfoDumper);
+ dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null,
+ /* anonymize = */ false,
+ /* minimize memory usage = */ false);
+
+ function loadAndCheck() {
+ // Load the file.
+ let fileInput1 =
+ frame.contentWindow.document.getElementById("fileInput1");
+ fileInput1.value = filePath; // this works because it's a chrome test
+ fileInput1.dispatchEvent(e);
+
+ // Initialize the clipboard contents.
+ SpecialPowers.clipboardCopyString("initial clipboard value");
+
+ let numFailures = 0, maxFailures = 30;
+
+ copyPasteAndCheck();
+
+ // Because the file load is async, we don't know when it will finish and
+ // the output will show up. So we poll.
+ function copyPasteAndCheck() {
+ // Copy and paste frame contents, and filter out non-deterministic
+ // differences.
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ let actual = SpecialPowers.getClipboardData("text/plain");
+
+ // If we have more than 1000 chars, we've probably successfully
+ // copy+pasted.
+ if (actual.length > 1000) {
+
+ let good = true;
+
+ if (actual.match("End of System")) {
+ let m1 = actual.match("anonymous") &&
+ actual.match("shared-libraries");
+ ok(m1, "system-wide reporter")
+ good = good && !!m1;
+ }
+
+ // Note: Match "vsize" but not "vsize-max-contiguous".
+ let vsizes = actual.match(/vsize[^-]/g);
+ let endOfBrowsers = actual.match(/End of Browser/g);
+ if (endOfBrowsers == null) {
+ endOfBrowsers = actual.match(/End of [wW]eb /g);
+ }
+
+ let socketProcessRunning = 0;
+ if (SpecialPowers.Services.io.socketProcessLaunched) {
+ socketProcessRunning = 1;
+ }
+
+ let m2 = (vsizes.length == (4 + socketProcessRunning + ChromeUtils.aliveUtilityProcesses) &&
+ endOfBrowsers.length == 3);
+ ok(m2, "three content processes present in loaded data");
+ good = good && !!m2;
+
+ if (!good) {
+ dump("*******ACTUAL*******\n");
+ dump(actual);
+ dump("********************\n");
+ }
+
+ // Close the remote processes.
+ for (let i = 0; i < numRemotes; i++) {
+ remotes[i].close();
+ }
+
+ SimpleTest.finish();
+
+ } else {
+ numFailures++;
+ if (numFailures === maxFailures) {
+ ok(false, "not enough chars in pasted output");
+ SimpleTest.finish();
+ } else {
+ setTimeout(copyPasteAndCheck, 100);
+ }
+ }
+ }
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory6.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory6.xhtml
new file mode 100644
index 0000000000..a3a76532e7
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory6.xhtml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file tests the saving of GC and CC logs in both concise and
+ verbose formats. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <iframe id="amFrame" height="400" src="about:memory"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ function onFocus() {
+ let frame = document.getElementById("amFrame");
+ frame.focus();
+
+ // Checks that a file exists on the local file system and removes it if it
+ // is present.
+ function checkForFileAndRemove(aFilename) {
+ let localFile = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsIFile);
+ localFile.initWithPath(aFilename);
+
+ let exists = localFile.exists();
+ if (exists) {
+ localFile.remove(/* recursive = */ false);
+ }
+
+ return exists;
+ }
+
+ // Given a save log button, triggers the action and checks if both CC & GC
+ // logs were written to disk.
+ function saveLogs(aLogButton, aCCLogType)
+ {
+ // trigger the log saving
+ aLogButton.click();
+
+ // mainDiv
+ // |-> section
+ // | -> div gc log path
+ // | -> div cc log path
+ let mainDiv = frame.contentWindow.document.getElementById("mainDiv");
+ let logNodes = mainDiv.childNodes[0];
+
+ // we expect 2 logs listed
+ let numOfLogs = logNodes.childNodes.length;
+ ok(numOfLogs == 2, "two log entries generated")
+
+ // grab the path portion of the text
+ let gcLogPath = logNodes.childNodes[0].textContent
+ .replace("Saved GC log to ", "");
+ let ccLogPath = logNodes.childNodes[1].textContent
+ .replace("Saved " + aCCLogType + " CC log to ", "");
+
+ // check that the files actually exist
+ ok(checkForFileAndRemove(gcLogPath), "GC log file exists");
+ ok(checkForFileAndRemove(ccLogPath), "CC log file exists");
+ }
+
+ // get the log buttons to test
+ let saveConcise = frame.contentWindow.document
+ .getElementById("saveLogsConcise");
+ let saveVerbose = frame.contentWindow.document
+ .getElementById("saveLogsVerbose");
+
+ saveLogs(saveConcise, "concise");
+ saveLogs(saveVerbose, "verbose");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForFocus(onFocus);
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml
new file mode 100644
index 0000000000..28872cd516
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml
@@ -0,0 +1,215 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- This file tests filtering in about:memory. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ "use strict";
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ // Hide all the real reporters; we'll restore them at the end.
+ mgr.blockRegistrationAndHideExistingReporters();
+
+ // Setup various fake-but-deterministic reporters.
+ const KB = 1024;
+ const MB = KB * KB;
+ const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
+ const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
+ const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+
+ let fakeReporters = [
+ { collectReports(aCbObj, aClosure, aAnonymize) {
+ function f(aP, aK, aA) {
+ aCbObj.callback("Main Process", aP, aK, BYTES, aA, "Desc.", aClosure);
+ }
+ f("heap-allocated", OTHER, 250 * MB);
+ f("explicit/a/b", HEAP, 50 * MB);
+ f("explicit/a/c/d", HEAP, 25 * MB);
+ f("explicit/a/c/e", HEAP, 15 * MB);
+ f("explicit/a/f", HEAP, 30 * MB);
+ f("explicit/g", HEAP, 100 * MB);
+ f("explicit/h/i", HEAP, 10 * MB);
+ f("explicit/h/i2", HEAP, 9 * MB);
+ f("explicit/j/k", HEAP, 0.5 * MB);
+ f("explicit/j/k2", HEAP, 0.3 * MB);
+ f("explicit/a/l/m", HEAP, 0.1 * MB);
+ f("explicit/a/l/n", HEAP, 0.1 * MB);
+ }
+ }
+ ];
+
+ for (let i = 0; i < fakeReporters.length; i++) {
+ mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]);
+ }
+
+ ]]>
+ </script>
+
+ <iframe id="amFrame" height="500" src="about:memory"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function finish()
+ {
+ mgr.unblockRegistrationAndRestoreOriginalReporters();
+ SimpleTest.finish();
+ }
+
+ // Click on the identified element, then cut+paste the entire page and
+ // check that the cut text matches what we expect.
+ function testClick(aId, aExpected, aNext) {
+ let win = document.getElementById("amFrame").contentWindow;
+
+ win.document.getElementById(aId).click();
+
+ testClipboard(aExpected, aNext, 0);
+ }
+
+ // Apply the specified filter, then cut+paste the entire page and
+ // check that the cut text matches what we expect.
+ function testFilter(aFilterString, aRegEx, aExpected, aNext) {
+ let win = document.getElementById("amFrame").contentWindow;
+
+ let filterInput = win.document.querySelector(".filterInput");
+ let filterRegExCheckbox =
+ win.document.querySelector(".filterInput + * input[type=checkbox]");
+
+ filterInput.value = aFilterString;
+ filterRegExCheckbox.checked = aRegEx;
+
+ // Dispatch a synthetic input event, since assigning to .value above
+ // doesn't trigger this.
+ filterInput.dispatchEvent(new win.Event("input"));
+
+ // about:memory delays 300 ms before applying the filter, so we wait a
+ // a bit longer than that before checking the clipboard.
+ testClipboard(aExpected, aNext, /* delay */ 600);
+ }
+
+ function testClipboard(aExpected, aNext, aDelay) {
+ setTimeout(function() {
+ let mostRecentActual;
+ document.getElementById("amFrame").focus();
+ SimpleTest.waitForClipboard(
+ function(aActual) {
+ mostRecentActual = aActual;
+ let rslt = aActual.trim() === aExpected.trim();
+ if (!rslt) {
+ // Try copying again.
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ }
+
+ return rslt;
+ },
+ function() {
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("C", {accelKey: true});
+ },
+ aNext,
+ function() {
+ ok(false, "pasted text doesn't match");
+ dump("******EXPECTED******\n");
+ dump(aExpected);
+ dump("*******ACTUAL*******\n");
+ dump(mostRecentActual);
+ dump("********************\n");
+ finish();
+ }
+ );
+ }, aDelay);
+ }
+
+ // Returns a function that chains together one test() call per id.
+ function chain(aIds) {
+ let x = aIds.shift();
+ if (x) {
+ if (x.click) {
+ return function() { testClick(x.click, x.expected, chain(aIds)); }
+ }
+ return function() { testFilter(x.filter, x.regex, x.expected, chain(aIds)); }
+ }
+ return function() { finish(); };
+ }
+
+ let startExpected =
+"\
+Main Process\n\
+Explicit Allocations\n\
+\n\
+250.00 MB (100.0%) -- explicit\n\
+├──120.20 MB (48.08%) -- a\n\
+│ ├───50.00 MB (20.00%) ── b\n\
+│ ├───40.00 MB (16.00%) -- c\n\
+│ │ ├──25.00 MB (10.00%) ── d\n\
+│ │ └──15.00 MB (06.00%) ── e\n\
+│ ├───30.00 MB (12.00%) ── f\n\
+│ └────0.20 MB (00.08%) ++ l\n\
+├──100.00 MB (40.00%) ── g\n\
+├───19.00 MB (07.60%) -- h\n\
+│ ├──10.00 MB (04.00%) ── i\n\
+│ └───9.00 MB (03.60%) ── i2\n\
+├───10.00 MB (04.00%) ── heap-unclassified\n\
+└────0.80 MB (00.32%) ++ j\n\
+\n\
+Other Measurements\n\
+\n\
+250.00 MB ── heap-allocated\n\
+\n\
+End of Main Process\n\
+";
+
+ let acFilterExpected =
+"\
+Main Process\n\
+Explicit Allocations\n\
+\n\
+40.00 MB (100.0%) -- explicit\n\
+└──40.00 MB (100.0%) -- a/c\n\
+ ├──25.00 MB (62.50%) ── d\n\
+ └──15.00 MB (37.50%) ── e\n\
+\n\
+End of Main Process\n\
+";
+
+ let hjFilterExpected =
+"\
+Main Process\n\
+Explicit Allocations\n\
+\n\
+19.80 MB (100.0%) -- explicit\n\
+├──19.00 MB (95.96%) -- h\n\
+│ ├──10.00 MB (50.51%) ── i\n\
+│ └───9.00 MB (45.45%) ── i2\n\
+└───0.80 MB (04.04%) -- j\n\
+ ├──0.50 MB (02.53%) ── k\n\
+ └──0.30 MB (01.52%) ── k2\n\
+\n\
+End of Main Process\n\
+";
+
+ let filtersToApplyOrIdsToClick = [
+ { click: "measureButton", expected: startExpected },
+ { filter: "a/c", regex: false, expected: acFilterExpected },
+ { filter: "/[hj]", regex: false, expected: "No results found." },
+ { filter: "/[hj]", regex: true, expected: hjFilterExpected },
+ ];
+
+ SimpleTest.waitForFocus(chain(filtersToApplyOrIdsToClick));
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xhtml b/toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xhtml
new file mode 100644
index 0000000000..9f3cdfe774
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xhtml
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="GC/CC logging with child processes"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ let numRemotes = 3;
+ let numReady = 0;
+
+ // Create some remote processes, and set up message-passing so that
+ // we know when each child is fully initialized.
+ let remotes = [];
+ SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", numRemotes]]}).then(function() {
+ for (let i = 0; i < numRemotes; i++) {
+ let w = remotes[i] = window.browsingContext.topChromeWindow.open("remote.xhtml", "", "chrome");
+
+ w.addEventListener("load", function loadHandler() {
+ let remoteBrowser = w.document.getElementById("remote");
+ let mm = remoteBrowser.messageManager;
+ mm.addMessageListener("test:ready", function readyHandler() {
+ mm.removeMessageListener("test:ready", readyHandler);
+ numReady++;
+ if (numReady == numRemotes) {
+ // All the remote processes are ready. Run test.
+ runTest();
+ }
+ });
+ mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true);
+ }, {once: true});
+ }
+ });
+
+ let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
+ getService(Ci.nsIMemoryInfoDumper);
+
+ function runTest() {
+ let numParents = 0;
+ let numChildren = 0;
+ dumper.dumpGCAndCCLogsToFile(
+ /* identifier: */ "test." + Date.now(),
+ /* allTraces: */ false,
+ /* childProcesses: */ true,
+ {
+ onDump(gcLog, ccLog, isParent) {
+ if (isParent) {
+ numParents++;
+ } else {
+ numChildren++;
+ }
+ checkAndRemoveLog(gcLog);
+ checkAndRemoveLog(ccLog);
+ },
+ onFinish() {
+ is(numParents, 1, "GC/CC logs for the parent process");
+ is(numChildren, numRemotes, "GC/CC logs for each child process");
+ cleanUpAndFinish();
+ },
+ }
+ );
+ }
+
+ function cleanUpAndFinish() {
+ // Close the remote processes.
+ for (let i = 0; i < numRemotes; i++) {
+ remotes[i].close();
+ }
+ SimpleTest.finish();
+ }
+
+ function checkAndRemoveLog(logFile) {
+ let name = logFile.path;
+ ok(logFile.exists(), "log file "+name+" exists");
+ ok(logFile.isFile(), "log file "+name+" is a regular file");
+ ok(logFile.fileSize > 0, "log file "+name+" is not empty");
+ logFile.remove(/* recursive: */ false);
+ }
+
+ ]]></script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml b/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml
new file mode 100644
index 0000000000..c97981258e
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml
@@ -0,0 +1,433 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Memory reporters"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- This file tests (in a rough fashion) whether the memory reporters are
+ producing sensible results. test_aboutmemory.xhtml tests the
+ presentation of memory reports in about:memory. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <!-- In bug 773533, <marquee> elements crashed the JS memory reporter -->
+ <marquee>Marquee</marquee>
+ </body>
+
+ <!-- some URIs that should be anonymized in anonymous mode -->
+ <iframe id="amFrame" height="200" src="http://example.org:80"></iframe>
+ <iframe id="amFrame" height="200" src="https://example.com:443"></iframe>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ "use strict";
+
+ const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
+ const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
+ const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
+
+ const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+ const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
+ const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
+ const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
+
+ // Use backslashes instead of forward slashes due to memory reporting's hacky
+ // handling of URLs.
+ const XUL_NS =
+ "http:\\\\www.mozilla.org\\keymaster\\gatekeeper\\there.is.only.xul";
+
+ SimpleTest.waitForExplicitFinish();
+
+ let vsizeAmounts = [];
+ let residentAmounts = [];
+ let heapAllocatedAmounts = [];
+ let storageSqliteAmounts = [];
+
+ let jsGcHeapUsedGcThingsTotal = 0;
+ let jsGcHeapUsedGcThings = {};
+
+ let present = {}
+
+ // Generate a long, random string. We'll check that this string is
+ // reported in at least one of the memory reporters.
+ let bigString = "";
+ while (bigString.length < 10000) {
+ bigString += Math.random();
+ }
+ let bigStringPrefix = bigString.substring(0, 100);
+
+ // Generate many copies of two distinctive short strings, "!)(*&" and
+ // "@)(*&". We'll check that these strings are reported in at least one of
+ // the memory reporters. We will create these strings in the tenured heap to
+ // avoid nursery string deduplication.
+ let shortStrings = [];
+ let newString = Cu.getJSTestingFunctions().newString;
+ for (let i = 0; i < 10000; i++) {
+ let str = (Math.random() > 0.5 ? "!" : "@") + ")(*&";
+ shortStrings.push(newString(str, {tenured: true}));
+ }
+
+ // Strings in the nursery are not reported, so make sure the above test
+ // strings are tenured (not all are created tenured).
+ Cu.forceGC();
+
+ let mySandbox = Cu.Sandbox(document.nodePrincipal,
+ { sandboxName: "this-is-a-sandbox-name" });
+
+ function handleReportNormal(aProcess, aPath, aKind, aUnits, aAmount,
+ aDescription)
+ {
+ if (aProcess.startsWith(`Utility `)) {
+ // The Utility process that runs the ORB JavaScript validator starts on first
+ // idle in the parent process. This makes it notoriously hard to know _if_ it
+ // actually has started. So we bail. See Bug 1813985.
+ return;
+ }
+ // Record the values of some notable reporters.
+ if (aPath === "vsize") {
+ vsizeAmounts.push(aAmount);
+ } else if (aPath === "resident") {
+ residentAmounts.push(aAmount);
+ } else if (aPath.search(/^js-main-runtime-gc-heap-committed\/used\/gc-things\//) >= 0) {
+ jsGcHeapUsedGcThingsTotal += aAmount;
+ jsGcHeapUsedGcThings[aPath] = (jsGcHeapUsedGcThings[aPath] | 0) + 1;
+ } else if (aPath === "heap-allocated") {
+ heapAllocatedAmounts.push(aAmount);
+ } else if (aPath === "storage-sqlite") {
+ storageSqliteAmounts.push(aAmount);
+
+ // Check the presence of some other notable reporters.
+ } else if (aPath.search(/^explicit\/js-non-window\/.*realm\(/) >= 0) {
+ present.jsNonWindowRealms = true;
+ } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-realm\(/) >= 0) {
+ present.windowObjectsJsRealms = true;
+ } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) {
+ present.places = true;
+ } else if (aPath.search(/^explicit\/images/) >= 0) {
+ present.images = true;
+ } else if (aPath.search(/^explicit\/atoms\/dynamic-objects-and-chars$/) >= 0) {
+ present.dynamicObjectsAndChars = true;
+ } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) {
+ // A system compartment with a location (such as a sandbox) should
+ // show that location.
+ present.sandboxLocation = true;
+ } else if (aPath.includes(bigStringPrefix)) {
+ present.bigString = true;
+ } else if (aPath.includes("!)(*&")) {
+ present.smallString1 = true;
+ } else if (aPath.includes("@)(*&")) {
+ present.smallString2 = true;
+ }
+
+ // Shouldn't get any anonymized paths.
+ if (aPath.includes('<anonymized')) {
+ present.anonymizedWhenUnnecessary = aPath;
+ }
+ }
+
+ function handleReportAnonymized(aProcess, aPath, aKind, aUnits, aAmount,
+ aDescription)
+ {
+ // Path might include an xmlns using http, which is safe to ignore.
+ let reducedPath = aPath.replace(XUL_NS, "");
+
+ // Shouldn't get http: or https: in any paths.
+ if (reducedPath.includes('http:')) {
+ present.httpWhenAnonymized = aPath;
+ }
+
+ // file: URLs should have their path anonymized.
+ if (reducedPath.search('file:..[^<]') !== -1) {
+ present.unanonymizedFilePathWhenAnonymized = aPath;
+ }
+ }
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ let amounts = [
+ "vsize",
+ "vsizeMaxContiguous",
+ "resident",
+ "residentFast",
+ "residentPeak",
+ "residentUnique",
+ "heapAllocated",
+ "heapOverheadFraction",
+ "JSMainRuntimeGCHeap",
+ "JSMainRuntimeTemporaryPeak",
+ "JSMainRuntimeRealmsSystem",
+ "JSMainRuntimeRealmsUser",
+ "imagesContentUsedUncompressed",
+ "storageSQLite",
+ "lowMemoryEventsPhysical",
+ "ghostWindows",
+ "pageFaultsHard",
+ ];
+ for (let i = 0; i < amounts.length; i++) {
+ try {
+ // If mgr[amounts[i]] throws an exception, just move on -- some amounts
+ // aren't available on all platforms. But if the attribute simply
+ // isn't present, that indicates the distinguished amounts have changed
+ // and this file hasn't been updated appropriately.
+ let dummy = mgr[amounts[i]];
+ ok(dummy !== undefined,
+ "accessed an unknown distinguished amount: " + amounts[i]);
+ } catch (ex) {
+ }
+ }
+
+ // Run sizeOfTab() to make sure it doesn't crash. We can't check the result
+ // values because they're non-deterministic.
+ let jsObjectsSize = {};
+ let jsStringsSize = {};
+ let jsOtherSize = {};
+ let domSize = {};
+ let styleSize = {};
+ let otherSize = {};
+ let totalSize = {};
+ let jsMilliseconds = {};
+ let nonJSMilliseconds = {};
+ mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize,
+ domSize, styleSize, otherSize, totalSize,
+ jsMilliseconds, nonJSMilliseconds);
+
+ let asyncSteps = [
+ getReportsNormal,
+ getReportsAnonymized,
+ checkResults,
+ test_register_strong,
+ test_register_strong, // Make sure re-registering works
+ test_register_weak,
+ SimpleTest.finish
+ ];
+
+ function runNext() {
+ setTimeout(asyncSteps.shift(), 0);
+ }
+
+ function getReportsNormal()
+ {
+ mgr.getReports(handleReportNormal, null,
+ runNext, null,
+ /* anonymize = */ false);
+ }
+
+ function getReportsAnonymized()
+ {
+ mgr.getReports(handleReportAnonymized, null,
+ runNext, null,
+ /* anonymize = */ true);
+ }
+
+ function checkSizeReasonable(aName, aAmount)
+ {
+ // Check the size is reasonable -- i.e. not ridiculously large or small.
+ ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000,
+ aName + "'s size is reasonable");
+ }
+
+ function checkSpecialReport(aName, aAmounts, aCanBeUnreasonable)
+ {
+ let socketProcessRunning = 0;
+ if (SpecialPowers.Services.io.socketProcessLaunched) {
+ socketProcessRunning = 1;
+ }
+ is(aAmounts.length, 1 + socketProcessRunning,
+ aName + " has " + aAmounts.length + " report");
+ let n = aAmounts[0];
+ if (!aCanBeUnreasonable) {
+ checkSizeReasonable(aName, n);
+ }
+ }
+
+ function checkResults()
+ {
+ try {
+ // Nb: mgr.heapAllocated will throw NS_ERROR_NOT_AVAILABLE if this is a
+ // --disable-jemalloc build. Allow for skipping this test on that
+ // exception, but *only* that exception.
+ // eslint-disable-next-line no-unused-vars
+ let dummy = mgr.heapAllocated;
+ checkSpecialReport("heap-allocated", heapAllocatedAmounts);
+ } catch (ex) {
+ is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.heapAllocated exception");
+ }
+ // vsize may be unreasonable if ASAN is enabled
+ checkSpecialReport("vsize", vsizeAmounts, /*canBeUnreasonable*/true);
+ checkSpecialReport("resident", residentAmounts);
+
+ for (var reporter in jsGcHeapUsedGcThings) {
+ ok(jsGcHeapUsedGcThings[reporter] == 1);
+ }
+ checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
+ jsGcHeapUsedGcThingsTotal);
+
+ ok(present.jsNonWindowRealms, "js-non-window realms are present");
+ ok(present.windowObjectsJsRealms, "window-objects/.../js realms are present");
+ ok(present.places, "places is present");
+ ok(present.images, "images is present");
+ ok(present.dynamicObjectsAndChars, "dynamic-objects-and-chars is present");
+ ok(present.sandboxLocation, "sandbox locations are present");
+ ok(present.bigString, "large string is present");
+ ok(present.smallString1, "small string 1 is present");
+ ok(present.smallString2, "small string 2 is present");
+
+ ok(!present.anonymizedWhenUnnecessary,
+ "anonymized paths are not present when unnecessary. Failed case: " +
+ present.anonymizedWhenUnnecessary);
+ ok(!present.httpWhenAnonymized,
+ "http URLs are anonymized when necessary. Failed case: " +
+ present.httpWhenAnonymized);
+ ok(!present.unanonymizedFilePathWhenAnonymized,
+ "file URLs are anonymized when necessary. Failed case: " +
+ present.unanonymizedFilePathWhenAnonymized);
+
+ runNext();
+ }
+
+ // Reporter registration tests
+
+ // collectReports() calls to the test reporter.
+ let called = 0;
+
+ // The test memory reporter, testing the various report units.
+ // Also acts as a report collector, verifying the reported values match the
+ // expected ones after passing through XPConnect / nsMemoryReporterManager
+ // and back.
+ function MemoryReporterAndCallback() {
+ this.seen = 0;
+ }
+ MemoryReporterAndCallback.prototype = {
+ // The test reports.
+ // Each test key corresponds to the path of the report. |amount| is a
+ // function called when generating the report. |expected| is a function
+ // to be tested when receiving a report during collection. If |expected| is
+ // omitted the |amount| will be checked instead.
+ tests: {
+ "test-memory-reporter-bytes1": {
+ units: BYTES,
+ amount: () => 0
+ },
+ "test-memory-reporter-bytes2": {
+ units: BYTES,
+ amount: () => (1<<30) * 8 // awkward way to say 8G in JS
+ },
+ "test-memory-reporter-counter": {
+ units: COUNT,
+ amount: () => 2
+ },
+ "test-memory-reporter-ccounter": {
+ units: COUNT_CUMULATIVE,
+ amount: () => ++called,
+ expected: () => called
+ },
+ "test-memory-reporter-percentage": {
+ units: PERCENTAGE,
+ amount: () => 9999
+ }
+ },
+ // nsIMemoryReporter
+ collectReports(callback, data, anonymize) {
+ for (let path of Object.keys(this.tests)) {
+ try {
+ let test = this.tests[path];
+ callback.callback(
+ "", // Process. Should be "" initially.
+ path,
+ OTHER,
+ test.units,
+ test.amount(),
+ "Test " + path + ".",
+ data);
+ }
+ catch (ex) {
+ ok(false, ex);
+ }
+ }
+ },
+ // nsIHandleReportCallback
+ callback(process, path, kind, units, amount, data) {
+ if (path in this.tests) {
+ this.seen++;
+ let test = this.tests[path];
+ ok(units === test.units, "Test reporter units match");
+ ok(amount === (test.expected || test.amount)(),
+ "Test reporter values match: " + amount);
+ }
+ },
+ // Checks that the callback has seen the expected number of reports, and
+ // resets the callback counter.
+ // @param expected Optional. Expected number of reports the callback
+ // should have processed.
+ finish(expected) {
+ if (expected === undefined) {
+ expected = Object.keys(this.tests).length;
+ }
+ is(expected, this.seen,
+ "Test reporter called the correct number of times: " + expected);
+ this.seen = 0;
+ }
+ };
+
+ // General memory reporter + registerStrongReporter tests.
+ function test_register_strong() {
+ let reporterAndCallback = new MemoryReporterAndCallback();
+ // Registration works.
+ mgr.registerStrongReporter(reporterAndCallback);
+
+ // Check the generated reports.
+ mgr.getReports(reporterAndCallback, null,
+ () => {
+ reporterAndCallback.finish();
+ window.setTimeout(test_unregister_strong, 0, reporterAndCallback);
+ }, null,
+ /* anonymize = */ false);
+ }
+
+ function test_unregister_strong(aReporterAndCallback)
+ {
+ mgr.unregisterStrongReporter(aReporterAndCallback);
+
+ // The reporter was unregistered, hence there shouldn't be any reports from
+ // the test reporter.
+ mgr.getReports(aReporterAndCallback, null,
+ () => {
+ aReporterAndCallback.finish(0);
+ runNext();
+ }, null,
+ /* anonymize = */ false);
+ }
+
+ // Check that you cannot register JS components as weak reporters.
+ function test_register_weak() {
+ let reporterAndCallback = new MemoryReporterAndCallback();
+ try {
+ // Should fail! nsMemoryReporterManager will only hold a raw pointer to
+ // "weak" reporters. When registering a weak reporter, XPConnect will
+ // create a WrappedJS for JS components. This WrappedJS would be
+ // successfully registered with the manager, only to be destroyed
+ // immediately after, which would eventually lead to a crash when
+ // collecting the reports. Therefore nsMemoryReporterManager should
+ // reject WrappedJS reporters, which is what is tested here.
+ // See bug 950391 comment #0.
+ mgr.registerWeakReporter(reporterAndCallback);
+ ok(false, "Shouldn't be allowed to register a JS component (WrappedJS)");
+ }
+ catch (ex) {
+ ok(ex.message.includes("NS_ERROR_"),
+ "WrappedJS reporter got rejected: " + ex);
+ }
+
+ runNext();
+ }
+
+ // Kick-off the async tests.
+ runNext();
+
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml
new file mode 100644
index 0000000000..b7251c4e62
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Memory reporters with child processes"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- This file tests (in a rough fashion) whether the memory reporters are
+ producing sensible results in the presence of child processes. -->
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+ let socketProcessRunning = 0;
+ if (SpecialPowers.Services.io.socketProcessLaunched) {
+ socketProcessRunning = 1;
+ }
+ let numToOpen = 3;
+ const expectedNumRemotes = numToOpen + socketProcessRunning;
+ let numReady = 0;
+
+ // Create some remote processes, and set up message-passing so that
+ // we know when each child is fully initialized.
+ let remotes = [];
+ SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 3]]}).then(function() {
+ for (let i = 0; i < numToOpen; i++) {
+ let w = remotes[i] = window.browsingContext.topChromeWindow.open("remote.xhtml", "", "chrome");
+
+ w.addEventListener("load", function loadHandler() {
+ let remoteBrowser = w.document.getElementById("remote");
+ let mm = remoteBrowser.messageManager;
+ mm.addMessageListener("test:ready", function readyHandler() {
+ mm.removeMessageListener("test:ready", readyHandler);
+ numReady++;
+ if (numReady == numToOpen) {
+ // All the remote processes are ready. Do memory reporting.
+ doReports();
+ }
+ });
+ mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true);
+ }, {once: true});
+ }
+ });
+
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+
+ function doReports()
+ {
+ let residents = {};
+
+ let handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
+ if (aProcess.startsWith(`Utility `)) {
+ // The Utility process that runs the ORB JavaScript validator starts on first
+ // idle in the parent process. This makes it notoriously hard to know _if_ it
+ // actually has started. So we bail. See Bug 1813985.
+ return;
+ }
+
+ if (aPath === "resident") {
+ ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000,
+ "resident is reasonable");
+ residents[aProcess] = aAmount;
+ }
+ }
+
+ let processReports = function() {
+ // First, test a failure case: calling getReports() before the previous
+ // getReports() has finished should silently abort. (And the arguments
+ // won't be used.)
+ mgr.getReports(
+ () => ok(false, "handleReport called for nested getReports() call"),
+ null, null, null, /* anonymize = */ false
+ );
+
+ // Close the remote processes.
+ for (let i = 0; i < numToOpen; i++) {
+ remotes[i].close();
+ }
+
+ // Check the results.
+
+ let processes = Object.keys(residents);
+ ok(processes.length == expectedNumRemotes + 1, "correct resident count");
+
+ let numEmptyProcesses = 0, numNonEmptyProcesses = 0;
+ for (let i = 0; i < processes.length; i++) {
+ if (processes[i] == "") {
+ numEmptyProcesses++;
+ } else {
+ ok(processes[i].startsWith("Browser (") || processes[i].startsWith("Web Content (") ||
+ (processes[i].startsWith("Socket (") && socketProcessRunning)
+ || processes[i].startsWith("web (") || processes[i].startsWith("Utility ("),
+ "correct non-empty process name prefix: " + processes[i]);
+ numNonEmptyProcesses++;
+ }
+ }
+ ok(numEmptyProcesses == 1, "correct empty process name count");
+ ok(numNonEmptyProcesses == expectedNumRemotes,
+ "correct non-empty process name count");
+
+ SimpleTest.finish();
+ }
+
+ mgr.getReports(handleReport, null, processReports, null,
+ /* anonymize = */ false);
+ }
+
+ ]]></script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xhtml b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xhtml
new file mode 100644
index 0000000000..206cee5434
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xhtml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="about:memory"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml"></body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ // Test for bug 708248, where the SQLite memory multi-reporter was
+ // crashing when a DB was closed.
+
+ // Nb: this test is all JS and chould be done with an xpcshell test,
+ // but all the other memory reporter tests are mochitests, so it's easier
+ // if this one is too.
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Make a fake DB file.
+ let file = SpecialPowers.Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append("test_sqliteMultiReporter-fake-DB-tmp.sqlite");
+
+ // Open and close the DB.
+ let db = SpecialPowers.Services.storage.openDatabase(file);
+ db.close();
+
+ // Invoke all the reporters. The SQLite multi-reporter is among
+ // them. It shouldn't crash.
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+ getService(Ci.nsIMemoryReporterManager);
+ mgr.getReports(function(){}, null,
+ () => {
+ ok(true, "didn't crash");
+ SimpleTest.finish();
+ }, null,
+ /* anonymize = */ false);
+
+ ]]>
+ </script>
+</window>
diff --git a/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js b/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js
new file mode 100644
index 0000000000..4a77752b17
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+function run_test() {
+ let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(
+ Ci.nsIMemoryReporterManager
+ );
+
+ let ok = gfxInfo.controlGPUProcessForXPCShell(true);
+ Assert.equal(ok, true);
+
+ let endTesting = function () {
+ gfxInfo.controlGPUProcessForXPCShell(false);
+ do_test_finished();
+ };
+
+ let foundGPUProcess = false;
+ let onHandleReport = function (
+ aProcess,
+ aPath,
+ aKind,
+ aUnits,
+ aAmount,
+ aDescription
+ ) {
+ if (/GPU \(pid \d+\)/.test(aProcess)) {
+ foundGPUProcess = true;
+ }
+ };
+ let onFinishReporting = function () {
+ Assert.equal(foundGPUProcess, true);
+ endTesting();
+ };
+
+ mgr.getReports(onHandleReport, null, onFinishReporting, null, false);
+ do_test_pending();
+}
diff --git a/toolkit/components/aboutmemory/tests/xpcshell/xpcshell.ini b/toolkit/components/aboutmemory/tests/xpcshell/xpcshell.ini
new file mode 100644
index 0000000000..3a0598bf49
--- /dev/null
+++ b/toolkit/components/aboutmemory/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+skip-if = toolkit != 'windows'
+
+[test_gpuprocess.js]