Bug 1424760 (Part 3) - Add a TelemetryGC unit test r=gfritzsche
authorPaul Bone <pbone@mozilla.com>
Tue, 16 Jan 2018 10:46:00 +1100
changeset 453685 b4ecd0f0fc8dd3b0d8f35515ca6acdb11c07d3af
parent 453684 e65bb33f2ae069c91f7e379d1b19e814934f5fb7
child 453686 1f1f863ea198cd04e1a90af2be6d953e0f5502ad
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs1424760
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1424760 (Part 3) - Add a TelemetryGC unit test r=gfritzsche toolkit/components/telemetry/tests/unit/test_TelemetryGC.js: toolkit/components/telemetry/tests/unit/xpcshell.ini: Add new unit test. toolkit/components/telemetry/GCTelemetry.jsm: Add an extra method to make testing easier. js/src/gc/Statistics.cpp: Add a reference to the unit test for anyone modifying this code.
js/src/gc/Statistics.cpp
toolkit/components/telemetry/GCTelemetry.jsm
toolkit/components/telemetry/tests/unit/test_TelemetryGC.js
toolkit/components/telemetry/tests/unit/xpcshell.ini
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -593,20 +593,25 @@ Statistics::renderJsonMessage(uint64_t t
 
     return UniqueChars(printer.release());
 }
 
 void
 Statistics::formatJsonDescription(uint64_t timestamp, JSONPrinter& json) const
 {
     // If you change JSON properties here, please update:
-    // Telemetry ping code: toolkit/components/telemetry/GCTelemetry.jsm
-    // Telemetry documentation: toolkit/components/telemetry/docs/data/main-ping.rst
-    // Telemetry tests: toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js
-    // Perf.html: https://github.com/devtools-html/perf.html
+    // Telemetry ping code:
+    //   toolkit/components/telemetry/GCTelemetry.jsm
+    // Telemetry documentation:
+    //   toolkit/components/telemetry/docs/data/main-ping.rst
+    // Telemetry tests:
+    //   toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js,
+    //   toolkit/components/telemetry/tests/unit/test_TelemetryGC.js
+    // Perf.html:
+    //   https://github.com/devtools-html/perf.html
     //
     // Please also number each property to help correctly maintain the Telemetry ping code
 
     json.property("timestamp", timestamp); // # JSON Key #2
 
     TimeDuration total, longest;
     gcDuration(&total, &longest);
     json.property("max_pause", longest, JSONPrinter::MILLISECONDS); // #3
@@ -646,20 +651,26 @@ Statistics::formatJsonDescription(uint64
     json.property("minor_gc_number", startingMinorGCNumber); // #21
     json.property("slice_number", startingSliceNumber); // #22
 }
 
 void
 Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice, JSONPrinter& json) const
 {
     // If you change JSON properties here, please update:
-    // Telemetry ping code: toolkit/components/telemetry/GCTelemetry.jsm
-    // Telemetry documentation: toolkit/components/telemetry/docs/data/main-ping.rst
-    // Telemetry tests: toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js
-    // Perf.html: https://github.com/devtools-html/perf.html
+    // Telemetry ping code:
+    //   toolkit/components/telemetry/GCTelemetry.jsm
+    // Telemetry documentation:
+    //   toolkit/components/telemetry/docs/data/main-ping.rst
+    // Telemetry tests:
+    //   toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js,
+    //   toolkit/components/telemetry/tests/unit/test_TelemetryGC.js
+    // Perf.html:
+    //   https://github.com/devtools-html/perf.html
+    //
     char budgetDescription[200];
     slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
     TimeStamp originTime = TimeStamp::ProcessCreation();
 
     json.property("slice", i); // JSON Property #1
     json.property("pause", slice.duration(), JSONPrinter::MILLISECONDS); // #2
     json.property("reason", ExplainReason(slice.reason)); // #3
     json.property("initial_state", gc::StateName(slice.initialState)); // #4
--- a/toolkit/components/telemetry/GCTelemetry.jsm
+++ b/toolkit/components/telemetry/GCTelemetry.jsm
@@ -186,18 +186,22 @@ var GCTelemetry = {
 
     if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
       Services.ppmm.removeMessageListener("Telemetry:GCStatistics", this);
     }
     this.initialized = false;
   },
 
   observe(subject, topic, arg) {
-    let data = JSON.parse(arg);
+    this.observeRaw(JSON.parse(arg));
+  },
 
+  // We expose this method so unit tests can call it, no need to test JSON
+  // parsing.
+  observeRaw(data) {
     limitSize(data);
 
     if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
       processData.get("main").record(data);
     } else {
       Services.cpmm.sendAsyncMessage("Telemetry:GCStatistics", data);
     }
   },
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryGC.js
@@ -0,0 +1,130 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+"use strict";
+
+Cu.import("resource://gre/modules/GCTelemetry.jsm", this);
+
+function do_register_cleanup() {
+    GCTelemetry.shutdown();
+}
+
+/*
+ * These tests are very basic, my goal was to add enough testing to support
+ * a change made in Bug 1424760.  TODO Bug 1429635 for adding more extensive
+ * tests and deleting this comment.
+ */
+
+function run_test() {
+  // Test initialisation
+  Assert.ok(GCTelemetry.init(), "Initialize success");
+  Assert.ok(!GCTelemetry.init(), "Wont initialize twice");
+
+  // Test the basic success path.
+
+  // There are currently no entries
+  assert_num_entries(0, false);
+
+  // Add an entry
+  GCTelemetry.observeRaw(make_gc());
+
+  // Get it back.
+  assert_num_entries(1, false);
+  let entries = GCTelemetry.entries("main", false);
+  Assert.ok(entries, "Got entries object");
+  Assert.ok(entries.random, "Has random property");
+  Assert.ok(entries.worst, "Has worst property");
+  let entry = entries.worst[0];
+  Assert.ok(entry, "Got worst entry");
+
+
+  // "true" will cause the entry to be clared.
+  assert_num_entries(1, true);
+  // There are currently no entries.
+  assert_num_entries(0, false);
+  Assert.equal(20, Object.keys(entry).length);
+}
+
+function assert_num_entries(expect, clear) {
+  let entries = GCTelemetry.entries("main", clear);
+  Assert.equal(expect, entries.worst.length, expect + " worst entries");
+  // Randomly sampled GCs are only recorded for content processes
+  Assert.equal(0, entries.random.length, expect + " random entries");
+}
+
+/*
+ * These events are not exactly well-formed, but good enough.  For example
+ * there's no guantee that all the pause times add up to total time, or
+ * that max_pause is correct.
+ */
+function make_gc() {
+  // Timestamps are in milliseconds since startup. All the times here
+  // are wall-clock times, which may not be monotonically increasing.
+  let timestamp = Math.random() * 1000000;
+
+  let gc = {
+    "status": "completed",
+    "timestamp": timestamp,
+    // All durations are in milliseconds.
+    "max_pause": Math.random() * 95 + 5,
+    "total_time": Math.random() * 500 + 500, // Sum of all slice times.
+    "zones_collected": 9,
+    "total_zones": 9,
+    "total_compartments": 309,
+    "minor_gcs": 44,
+    "store_buffer_overflows": 19,
+    "mmu_20ms": 0,
+    "mmu_50ms": 0,
+    "nonincremental_reason": "GCBytesTrigger",
+    "allocated_bytes": 38853696, // in bytes
+    "added_chunks": 54,
+    "removed_chunks": 12,
+    "slices": 15,
+    "slice_number": 218, // The first slice number for this GC event.
+    "slices_list": [
+      {
+        "slice": 218,  // The global index of this slice.
+        "pause": Math.random() * 2 + 28,
+        "reason": "SET_NEW_DOCUMENT",
+        "initial_state": "NotActive",
+        "final_state": "Mark",
+        "budget": "10ms",
+        "page_faults": 1,
+        "start_timestamp": timestamp + Math.random() * 50000,
+        "times": {
+          "wait_background_thread": 0.012,
+          "mark_discard_code": 2.845,
+          "purge": 0.723,
+          "mark": 9.831,
+          "mark_roots": 0.102,
+          "buffer_gray_roots": 3.095,
+          "mark_cross_compartment_wrappers": 0.039,
+          "mark_c_and_js_stacks": 0.005,
+          "mark_runtime_wide_data": 2.313,
+          "mark_embedding": 0.117,
+          "mark_compartments": 2.27,
+          "unmark": 1.063,
+          "minor_gcs_to_evict_nursery": 8.701,
+        }
+      }
+    ],
+    "totals": {
+      "wait_background_thread": 0.012,
+      "mark_discard_code": 2.845,
+      "purge": 0.723,
+      "mark": 9.831,
+      "mark_roots": 0.102,
+      "buffer_gray_roots": 3.095,
+      "mark_cross_compartment_wrappers": 0.039,
+      "mark_c_and_js_stacks": 0.005,
+      "mark_runtime_wide_data": 2.313,
+      "mark_embedding": 0.117,
+      "mark_compartments": 2.27,
+      "unmark": 1.063,
+      "minor_gcs_to_evict_nursery": 8.701,
+    }
+  };
+  return gc;
+}
+
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -69,8 +69,9 @@ skip-if = toolkit == 'android'
 [test_TelemetryCaptureStack.js]
 [test_TelemetryEvents.js]
 [test_ChildEvents.js]
 skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
 [test_TelemetryModules.js]
 skip-if = (os == "android" && release_or_beta) # (see bug 1351197)
 [test_PingSender.js]
 skip-if = (os == "android") || (os == "linux" && bits == 32)
+[test_TelemetryGC.js]