Bug 1369955: Introduce a perfstream::Measure subtree for SpiderMonkey. draft
authorJim Blandy <jimb@mozilla.com>
Fri, 06 Oct 2017 15:38:05 -0700
changeset 684580 23f51596ac386586bbe7505f82f671143edee43c
parent 684579 d4580c831f968bcb7bce9a4f699f0e2ea92bb21d
child 684581 49b733ffdcb6e15f338ee212cd964671c4e32931
push id85652
push userbmo:jimb@mozilla.com
push dateMon, 23 Oct 2017 05:02:44 +0000
bugs1369955
milestone58.0a1
Bug 1369955: Introduce a perfstream::Measure subtree for SpiderMonkey. js/src/vm/Measure.cpp implements a perfstream::Group under which SpiderMonkey performance measurements can be placed. js/public/Measure.h holds the public interface and comments. MozReview-Commit-ID: 4azhU83aorE
config/check_spidermonkey_style.py
js/public/Measure.h
js/src/moz.build
js/src/vm/Measure.cpp
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -254,16 +254,21 @@ def check_style():
     with get_repository_from_env() as repo:
         # Select the appropriate files.
         for filename in repo.get_files_in_working_directory():
             for non_js_dir in non_js_dirnames:
                 if filename.startswith(non_js_dir) and filename.endswith('.h'):
                     inclname = 'mozilla/' + filename.split('/')[-1]
                     non_js_inclnames.add(inclname)
 
+            # perfstream headers appear as mozilla/perfstream/foo.h.
+            if filename.startswith('perfstream/') and filename.endswith('.h'):
+                inclname = 'mozilla/' + filename
+                non_js_inclnames.add(inclname)
+
             if filename.startswith('js/public/') and filename.endswith('.h'):
                 inclname = 'js/' + filename[len('js/public/'):]
                 js_names[filename] = inclname
 
             if filename.startswith('js/src/') and \
                not filename.startswith(tuple(ignored_js_src_dirs)) and \
                filename.endswith(('.c', '.cpp', '.h', '.tbl', '.msg')):
                 inclname = filename[len('js/src/'):]
new file mode 100644
--- /dev/null
+++ b/js/public/Measure.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Measure_h
+#define js_Measure_h
+
+#include "mozilla/perfstream/Measure.h"
+#include "mozilla/UniquePtr.h"
+
+#include "jspubtd.h"
+
+namespace JS {
+
+/**
+ * Return a tree of SpiderMonkey performance measures.
+ *
+ * This returns a mozilla::perfstream::Group that is the root of the measure
+ * tree for the given JSContext's runtime. See perfstream/Measure.h for an
+ * explanation of how to use Groups and Measures.
+ *
+ * On OOM, return a null UniquePtr.
+ */
+extern JS_PUBLIC_API(mozilla::perfstream::GroupPtr)
+GetMeasures(JSContext*);
+
+/**
+ * Like GetMeasures, but include testing-only measures.
+ */
+extern JS_PUBLIC_API(mozilla::perfstream::GroupPtr)
+GetMeasuresWithTests(JSContext*);
+
+} // namespace JS
+
+#endif /* js_Measure_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -109,16 +109,17 @@ EXPORTS.js += [
     '../public/GCPolicyAPI.h',
     '../public/GCVariant.h',
     '../public/GCVector.h',
     '../public/HashTable.h',
     '../public/HeapAPI.h',
     '../public/Id.h',
     '../public/Initialization.h',
     '../public/LegacyIntTypes.h',
+    '../public/Measure.h',
     '../public/MemoryMetrics.h',
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/Proxy.h',
     '../public/Realm.h',
     '../public/RefCounted.h',
     '../public/RequiredDefines.h',
@@ -323,16 +324,17 @@ UNIFIED_SOURCES += [
     'vm/GeckoProfiler.cpp',
     'vm/GeneratorObject.cpp',
     'vm/GlobalObject.cpp',
     'vm/HelperThreads.cpp',
     'vm/Id.cpp',
     'vm/Initialization.cpp',
     'vm/JSONParser.cpp',
     'vm/JSONPrinter.cpp',
+    'vm/Measure.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/NativeObject.cpp',
     'vm/ObjectGroup.cpp',
     'vm/PIC.cpp',
     'vm/Printer.cpp',
     'vm/Probes.cpp',
     'vm/ProxyObject.cpp',
     'vm/Realm.cpp',
@@ -756,8 +758,10 @@ if CONFIG['CLANG_CXX'] or CONFIG['GNU_CX
 
 # Generate GC statistics phase data.
 GENERATED_FILES += ['gc/StatsPhasesGenerated.h']
 StatsPhasesGeneratedHeader = GENERATED_FILES['gc/StatsPhasesGenerated.h']
 StatsPhasesGeneratedHeader.script = 'gc/GenerateStatsPhases.py:generateHeader'
 GENERATED_FILES += ['gc/StatsPhasesGenerated.cpp']
 StatsPhasesGeneratedCpp = GENERATED_FILES['gc/StatsPhasesGenerated.cpp']
 StatsPhasesGeneratedCpp.script = 'gc/GenerateStatsPhases.py:generateCpp'
+
+USE_LIBS += ['perfstream']
new file mode 100644
--- /dev/null
+++ b/js/src/vm/Measure.cpp
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Root of the tree of mozilla::perfstream performance measures for Spidermonkey. */
+
+#include "js/Measure.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Move.h"
+#include "mozilla/perfstream/Measure.h"
+#include "mozilla/perfstream/StaticGroup.h"
+
+#include "jscntxt.h"
+
+using mozilla::ArrayLength;
+using mozilla::Atomic;
+using mozilla::Maybe;
+using mozilla::Move;
+using mozilla::Nothing;
+using mozilla::perfstream::Description;
+using mozilla::perfstream::Group;
+using mozilla::perfstream::GroupPtr;
+using mozilla::perfstream::GroupVector;
+using mozilla::perfstream::Measure;
+using mozilla::perfstream::MeasurePtr;
+using mozilla::perfstream::MeasureVector;
+using mozilla::perfstream::StaticGroup;
+using mozilla::Some;
+
+
+namespace js {
+
+// A dummy measure, for testing only.
+class TestMeasure: public Measure {
+
+  public:
+    TestMeasure(const char* identifier, Atomic<bool>& enabled)
+      : identifier_(identifier), enabled_(enabled)
+    { }
+
+    static MeasurePtr Make(const char* identifier, Atomic<bool>& enabled) {
+        return mozilla::MakeUnique<TestMeasure>(identifier, enabled);
+    }
+
+    bool IsEnabled() const override {
+        return enabled_;
+    }
+    bool Enable() override {
+        enabled_ = true;
+        return true;
+    }
+    bool Disable() override {
+        enabled_ = false;
+        return false;
+    }
+
+    Description GetDescription() const override {
+        static const char* const metadata[] = {
+            "tooltip-fluent: SpiderMonkey.MeasureTests.toy",
+            "tooltip: A toy measure for testing the measure machinery. Can be enabled; does nothing.",
+            "help-url: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey",
+            nullptr
+        };
+
+        Description description = {
+            identifier_,
+            metadata
+        };
+
+        return description;
+    }
+
+  private:
+    const char* identifier_;
+    Atomic<bool> &enabled_;
+};
+
+// A group of toy measures, for use in tests.
+// This is actually an impl class for use with StaticGroup.
+class MeasureTestsImpl {
+  public:
+    MeasureTestsImpl() = default;
+    MeasureTestsImpl(const MeasureTestsImpl&) = default;
+    MeasureTestsImpl(MeasureTestsImpl&&) = default;
+
+    template<typename Functor>
+    bool EnumerateMeasures(Functor F) const {
+        return F("Flopsy",     [=] { return TestMeasure::Make("Flopsy",     enabled[0]); }) &&
+               F("Mopsy",      [=] { return TestMeasure::Make("Mopsy",      enabled[1]); }) &&
+               F("Cottontail", [=] { return TestMeasure::Make("Cottontail", enabled[2]); });
+    }
+
+    template<typename Functor>
+    bool EnumerateSubgroups(Functor F) const { return true; }
+
+    static const Description sDescription;
+
+  private:
+    // Measures are supposed to be references to some underlying machinery that
+    // actually does the measurement. To emphasize this, all TestMeasures are references
+    // to the same underlying set of bits.
+    static Atomic<bool> enabled[3];
+
+    // Metadata for the group.
+    static const char* const metadata[];
+};
+
+Atomic<bool> MeasureTestsImpl::enabled[3];
+
+const Description MeasureTestsImpl::sDescription = { "MeasureTests", metadata };
+
+const char* const MeasureTestsImpl::metadata[] = {
+    "tooltip: Test measure group for SpiderMonkey.",
+    "prams: definitely",
+    "prawns: probably",
+    nullptr
+};
+
+using MeasureTests = StaticGroup<MeasureTestsImpl>;
+
+class JSMeasuresImpl {
+  public:
+    enum Options {
+        Testing = 1,
+    };
+
+    JSMeasuresImpl(JSContext* cx, bool options) : cx(cx), options(options) { }
+    JSMeasuresImpl(const JSMeasuresImpl& rhs) = default;
+    JSMeasuresImpl(JSMeasuresImpl&& rhs) = default;
+
+    template<typename F>
+    bool EnumerateMeasures(F functor) const { return true; }
+
+    template<typename F>
+    bool EnumerateSubgroups(F functor) const {
+        // If our JSRuntime is dead, then we have no children.
+        if (!checkContext())
+            return true;
+
+        // Add more children here.
+
+        if (options & Options::Testing) {
+            if (!functor("MeasureTests", [this]() {
+                        return mozilla::MakeUnique<MeasureTests>(MeasureTestsImpl());
+                    }))
+                return false;
+            // Add more testing-only children here.
+        }
+
+        return true;
+    }
+
+    static const Description sDescription;
+
+  private:
+    // The context for the JSRuntime we operate on. This JSRuntime could be
+    // destroyed, so we need to check it before using it.
+    JSContext* cx;
+
+    // Bits from JSMeasures::Options.
+    int options;
+
+    bool checkContext() const {
+        return cx == TlsContext.get() &&
+               cx->runtime()->activeContext() == cx;
+    }
+
+    static const char* const metadata[];
+};
+
+const Description JSMeasuresImpl::sDescription = {
+    "SpiderMonkey",
+    metadata
+};
+
+const char* const JSMeasuresImpl::metadata[] = {
+    "tooltip-fluent: SpiderMonkey.measureGroup.description",
+    "tooltip: the SpiderMonkey JavaScript engine",
+    "help-url: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey",
+    nullptr
+};
+
+using JSMeasures = StaticGroup<JSMeasuresImpl>;
+
+} // namespace js
+
+namespace JS {
+
+JS_PUBLIC_API(GroupPtr)
+GetMeasures(JSContext* cx)
+{
+    JSMeasuresImpl impl(cx, 0);
+    return mozilla::MakeUnique<JSMeasures>(impl);
+}
+
+JS_PUBLIC_API(GroupPtr)
+GetMeasuresWithTests(JSContext* cx)
+{
+    JSMeasuresImpl impl(cx, JSMeasuresImpl::Testing);
+    return mozilla::MakeUnique<JSMeasures>(impl);
+}
+
+} // namespace JS