Bug 1347274 (part 4) - Improve the profiler's gtest. r=mstange.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 01 Jun 2017 19:54:56 +1000
changeset 361978 b0e196c8fcd770b9ce8be81ff0fbcffabc7b8808
parent 361977 90f080141b3d55ec2dde80ec2b03314f04838180
child 361979 63cd7aaf05d865a5ffc030a3c336b4cec40f45f2
push id31953
push usercbook@mozilla.com
push dateFri, 02 Jun 2017 12:22:33 +0000
treeherdermozilla-central@2a8478029a0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1347274
milestone55.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 1347274 (part 4) - Improve the profiler's gtest. r=mstange. Two improvements. - More testing of markers by introducing and using a ProfilerMarkerPayload subclass that keeps track of how many instances have been created, streamed, and destroyed. This change would have caught the defect that was in the first landing of part 3 of this bug. - More testing of JSON output, to check for various substrings that are expected to be present.
tools/profiler/tests/gtest/GeckoProfiler.cpp
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -25,17 +25,17 @@
 // Note: profiler_init() has already been called in XRE_main(), so we can't
 // test it here. Likewise for profiler_shutdown(), and GeckoProfilerInitRAII
 // (which is just an RAII wrapper for profiler_init() and profiler_shutdown()).
 
 using namespace mozilla;
 
 typedef Vector<const char*> StrVec;
 
-void
+static void
 InactiveFeaturesAndParamsCheck()
 {
   int entries;
   double interval;
   uint32_t features;
   StrVec filters;
 
   ASSERT_TRUE(!profiler_is_active());
@@ -46,17 +46,17 @@ InactiveFeaturesAndParamsCheck()
   profiler_get_start_params(&entries, &interval, &features, &filters);
 
   ASSERT_TRUE(entries == 0);
   ASSERT_TRUE(interval == 0);
   ASSERT_TRUE(features == 0);
   ASSERT_TRUE(filters.empty());
 }
 
-void
+static void
 ActiveParamsCheck(int aEntries, double aInterval, uint32_t aFeatures,
                   const char** aFilters, size_t aFiltersLen)
 {
   int entries;
   double interval;
   uint32_t features;
   StrVec filters;
 
@@ -315,16 +315,55 @@ TEST(GeckoProfiler, Pause)
 
   ASSERT_TRUE(!profiler_is_paused());
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_is_paused());
 }
 
+// A class that keeps track of how many instances have been created, streamed,
+// and destroyed.
+class GTestPayload : public ProfilerMarkerPayload
+{
+public:
+  explicit GTestPayload(int aN)
+    : mN(aN)
+  {
+    sNumCreated++;
+  }
+
+  virtual ~GTestPayload() { sNumDestroyed++; }
+
+  virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
+                             UniqueStacks& aUniqueStacks) override
+  {
+    streamCommonProps("gtest", aWriter, aStartTime, aUniqueStacks);
+    char buf[64];
+    SprintfLiteral(buf, "gtest-%d", mN);
+    aWriter.IntProperty(buf, mN);
+    sNumStreamed++;
+  }
+
+private:
+  int mN;
+
+public:
+  // The number of GTestPayload instances that have been created, streamed, and
+  // destroyed.
+  static int sNumCreated;
+  static int sNumStreamed;
+  static int sNumDestroyed;
+};
+
+int GTestPayload::sNumCreated = 0;
+int GTestPayload::sNumStreamed = 0;
+int GTestPayload::sNumDestroyed = 0;
+
 TEST(GeckoProfiler, Markers)
 {
   uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
                  features, filters, MOZ_ARRAY_LENGTH(filters));
 
@@ -344,17 +383,60 @@ TEST(GeckoProfiler, Markers)
   profiler_add_marker("M1");
   profiler_add_marker(
     "M2", new ProfilerMarkerTracing("C", TRACING_EVENT));
   PROFILER_MARKER("M3");
   PROFILER_MARKER_PAYLOAD(
     "M4", new ProfilerMarkerTracing("C", TRACING_EVENT,
                                     profiler_get_backtrace()));
 
+  for (int i = 0; i < 10; i++) {
+    PROFILER_MARKER_PAYLOAD("M5", new GTestPayload(i));
+  }
+
+  // Sleep briefly to ensure a sample is taken and the pending markers are
+  // processed.
+  PR_Sleep(PR_MillisecondsToInterval(500));
+
+  SpliceableChunkedJSONWriter w;
+  ASSERT_TRUE(profiler_stream_json_for_this_process(w));
+
+  UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
+
+  // The GTestPayloads should have been created and streamed, but not yet
+  // destroyed.
+  ASSERT_TRUE(GTestPayload::sNumCreated == 10);
+  ASSERT_TRUE(GTestPayload::sNumStreamed == 10);
+  ASSERT_TRUE(GTestPayload::sNumDestroyed == 0);
+  for (int i = 0; i < 10; i++) {
+    char buf[64];
+    SprintfLiteral(buf, "\"gtest-%d\"", i);
+    ASSERT_TRUE(strstr(profile.get(), buf));
+  }
+
   profiler_stop();
+
+  // The GTestPayloads should have been destroyed.
+  ASSERT_TRUE(GTestPayload::sNumDestroyed == 10);
+
+  for (int i = 0; i < 10; i++) {
+    PROFILER_MARKER_PAYLOAD("M5", new GTestPayload(i));
+  }
+
+  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
+                 features, filters, MOZ_ARRAY_LENGTH(filters));
+
+  ASSERT_TRUE(profiler_stream_json_for_this_process(w));
+
+  profiler_stop();
+
+  // The second set of GTestPayloads should not have been streamed.
+  ASSERT_TRUE(GTestPayload::sNumCreated == 20);
+  ASSERT_TRUE(GTestPayload::sNumStreamed == 10);
+  ASSERT_TRUE(GTestPayload::sNumDestroyed == 20);
 }
 
 TEST(GeckoProfiler, Time)
 {
   uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   double t1 = profiler_time();
@@ -389,33 +471,57 @@ TEST(GeckoProfiler, GetProfile)
   UniquePtr<char[]> profile = profiler_get_profile();
   ASSERT_TRUE(profile && profile[0] == '{');
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_get_profile());
 }
 
+static void
+JSONOutputCheck(const char* aOutput)
+{
+  // Check that various expected strings are in the JSON.
+
+  ASSERT_TRUE(aOutput);
+  ASSERT_TRUE(aOutput[0] == '{');
+
+  ASSERT_TRUE(strstr(aOutput, "\"libs\""));
+
+  ASSERT_TRUE(strstr(aOutput, "\"meta\""));
+  ASSERT_TRUE(strstr(aOutput, "\"version\""));
+  ASSERT_TRUE(strstr(aOutput, "\"startTime\""));
+
+  ASSERT_TRUE(strstr(aOutput, "\"threads\""));
+  ASSERT_TRUE(strstr(aOutput, "\"GeckoMain\""));
+  ASSERT_TRUE(strstr(aOutput, "\"samples\""));
+  ASSERT_TRUE(strstr(aOutput, "\"markers\""));
+  ASSERT_TRUE(strstr(aOutput, "\"stackTable\""));
+  ASSERT_TRUE(strstr(aOutput, "\"frameTable\""));
+  ASSERT_TRUE(strstr(aOutput, "\"stringTable\""));
+}
+
 TEST(GeckoProfiler, StreamJSONForThisProcess)
 {
   uint32_t features = ProfilerFeature::StackWalk;
   const char* filters[] = { "GeckoMain" };
 
   SpliceableChunkedJSONWriter w;
   ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
 
   profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
                  features, filters, MOZ_ARRAY_LENGTH(filters));
 
   w.Start(SpliceableJSONWriter::SingleLineStyle);
   ASSERT_TRUE(profiler_stream_json_for_this_process(w));
   w.End();
 
   UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
-  ASSERT_TRUE(profile && profile[0] == '{');
+
+  JSONOutputCheck(profile.get());
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
 }
 
 TEST(GeckoProfiler, StreamJSONForThisProcessThreaded)
 {
@@ -437,17 +543,18 @@ TEST(GeckoProfiler, StreamJSONForThisPro
   // Call profiler_stream_json_for_this_process on a background thread.
   thread->Dispatch(NS_NewRunnableFunction([&]() {
     w.Start(SpliceableJSONWriter::SingleLineStyle);
     ASSERT_TRUE(profiler_stream_json_for_this_process(w));
     w.End();
   }), NS_DISPATCH_SYNC);
 
   UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
-  ASSERT_TRUE(profile && profile[0] == '{');
+
+  JSONOutputCheck(profile.get());
 
   // Stop the profiler and call profiler_stream_json_for_this_process on a
   // background thread.
   thread->Dispatch(NS_NewRunnableFunction([&]() {
     profiler_stop();
     ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
   }), NS_DISPATCH_SYNC);
   thread->Shutdown();