[gjs: 1/2] Add some profiling labels




commit 0cf0406902e8efb4601b05e325ac479b6c174024
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Sat Feb 27 17:39:59 2021 +0300

    Add some profiling labels
    
    Add profiler labels for things like GObject property getters and setters
    and method calls. This way glue overhead and inner calls are correctly
    grouped and shown in stack traces.

 gi/function.cpp        | 12 ++++++++++++
 gi/object.cpp          | 25 +++++++++++++++++++++++++
 gi/wrapperutils.h      | 18 ++++++++++++++++--
 gjs/profiler-private.h | 25 +++++++++++++++++++++++++
 4 files changed, 78 insertions(+), 2 deletions(-)
---
diff --git a/gi/function.cpp b/gi/function.cpp
index 4f7077f0..c8d336de 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -46,6 +46,7 @@
 #include "gjs/global.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/mem-private.h"
+#include "gjs/profiler-private.h"
 #include "util/log.h"
 
 /* We use guint8 for arguments; functions can't
@@ -869,6 +870,8 @@ bool Function::invoke(JSContext* context, const JS::CallArgs& args,
     if (!args.isConstructing() && !args.computeThis(context, &obj))
         return false;
 
+    std::string dynamicString("(unknown)");
+
     if (state.is_method) {
         GjsArgumentCache* cache = &m_arguments[-state.first_arg_offset()];
         GIArgument* in_value = &state.in_cvalues[-state.first_arg_offset()];
@@ -886,8 +889,17 @@ bool Function::invoke(JSContext* context, const JS::CallArgs& args,
         if (g_type_is_a(cache->contents.object.gtype, G_TYPE_OBJECT) ||
             g_type_is_a(cache->contents.object.gtype, G_TYPE_INTERFACE))
             state.instance_object = obj;
+
+        if (g_type_is_a(cache->contents.object.gtype, G_TYPE_OBJECT)) {
+            auto* o = ObjectBase::for_js(context, obj);
+            dynamicString = o->format_name();
+        }
     }
 
+    dynamicString += '.';
+    dynamicString += format_name();
+    AutoProfilerLabel label(context, "", dynamicString.c_str());
+
     state.processed_c_args = ffi_arg_pos;
     for (gi_arg_pos = 0; gi_arg_pos < state.gi_argc;
          gi_arg_pos++, ffi_arg_pos++) {
diff --git a/gi/object.cpp b/gi/object.cpp
index abcda838..c811a065 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -56,6 +56,7 @@
 #include "gjs/jsapi-util-args.h"
 #include "gjs/jsapi-util-root.h"
 #include "gjs/mem-private.h"
+#include "gjs/profiler-private.h"
 #include "util/log.h"
 
 class JSTracer;
@@ -311,6 +312,9 @@ bool ObjectBase::prop_getter(JSContext* cx, unsigned argc, JS::Value* vp) {
     JS::RootedString name(cx,
         gjs_dynamic_property_private_slot(&args.callee()).toString());
 
+    std::string fullName = priv->format_name() + "." + gjs_debug_string(name);
+    AutoProfilerLabel label(cx, "property getter", fullName.c_str());
+
     priv->debug_jsprop("Property getter", name, obj);
 
     if (priv->is_prototype())
@@ -383,6 +387,9 @@ bool ObjectBase::field_getter(JSContext* cx, unsigned argc, JS::Value* vp) {
     JS::RootedString name(cx,
         gjs_dynamic_property_private_slot(&args.callee()).toString());
 
+    std::string fullName = priv->format_name() + "." + gjs_debug_string(name);
+    AutoProfilerLabel label(cx, "field getter", fullName.c_str());
+
     priv->debug_jsprop("Field getter", name, obj);
 
     if (priv->is_prototype())
@@ -446,6 +453,9 @@ bool ObjectBase::prop_setter(JSContext* cx, unsigned argc, JS::Value* vp) {
     JS::RootedString name(cx,
         gjs_dynamic_property_private_slot(&args.callee()).toString());
 
+    std::string fullName = priv->format_name() + "." + gjs_debug_string(name);
+    AutoProfilerLabel label(cx, "property setter", fullName.c_str());
+
     priv->debug_jsprop("Property setter", name, obj);
 
     if (priv->is_prototype())
@@ -503,6 +513,9 @@ bool ObjectBase::field_setter(JSContext* cx, unsigned argc, JS::Value* vp) {
     JS::RootedString name(cx,
         gjs_dynamic_property_private_slot(&args.callee()).toString());
 
+    std::string fullName = priv->format_name() + "." + gjs_debug_string(name);
+    AutoProfilerLabel label(cx, "field setter", fullName.c_str());
+
     priv->debug_jsprop("Field setter", name, obj);
 
     if (priv->is_prototype())
@@ -1863,6 +1876,11 @@ ObjectInstance::connect_impl(JSContext          *context,
                              "callback", &callback))
         return false;
 
+    std::string dynamicString = format_name() + '.' +
+                                (after ? "connect_after" : "connect") + "('" +
+                                signal_name.get() + "')";
+    AutoProfilerLabel label(context, "", dynamicString.c_str());
+
     if (!JS::IsCallable(callback)) {
         gjs_throw(context, "second arg must be a callback");
         return false;
@@ -1919,6 +1937,10 @@ ObjectInstance::emit_impl(JSContext          *context,
                              "signal name", &signal_name))
         return false;
 
+    std::string dynamicString =
+        format_name() + "emit('" + signal_name.get() + "')";
+    AutoProfilerLabel label(context, "", dynamicString.c_str());
+
     if (!g_signal_parse_name(signal_name.get(), gtype(), &signal_id,
                              &signal_detail, false)) {
         gjs_throw(context, "No signal '%s' on object '%s'",
@@ -2216,6 +2238,9 @@ bool ObjectBase::init_gobject(JSContext* context, unsigned argc,
     if (!priv->check_is_instance(context, "initialize"))
         return false;
 
+    std::string dynamicString = priv->format_name() + "._init";
+    AutoProfilerLabel label(context, "", dynamicString.c_str());
+
     return priv->to_instance()->init_impl(context, argv, &obj);
 }
 
diff --git a/gi/wrapperutils.h b/gi/wrapperutils.h
index 1057149d..0febd1bc 100644
--- a/gi/wrapperutils.h
+++ b/gi/wrapperutils.h
@@ -32,6 +32,7 @@
 #include "gjs/jsapi-class.h"  // IWYU pragma: keep
 #include "gjs/jsapi-util.h"
 #include "gjs/macros.h"
+#include "gjs/profiler-private.h"
 #include "util/log.h"
 
 struct JSFunctionSpec;
@@ -190,6 +191,14 @@ class GIWrapperBase : public CWrapperPointerOps<Base> {
         return info() ? g_base_info_get_name(info()) : type_name();
     }
 
+    [[nodiscard]] std::string format_name() const {
+        std::string retval = ns();
+        if (!retval.empty())
+            retval += '.';
+        retval += name();
+        return retval;
+    }
+
  private:
     // Accessor for Instance member. Used only in debug methods and toString().
     [[nodiscard]] const void* ptr_addr() const {
@@ -429,8 +438,13 @@ class GIWrapperBase : public CWrapperPointerOps<Base> {
 
         Instance* priv = Instance::new_for_js_object(cx, obj);
 
-        if (!priv->constructor_impl(cx, obj, args))
-            return false;
+        {
+            std::string fullName = priv->format_name();
+            AutoProfilerLabel label(cx, "constructor", fullName.c_str());
+
+            if (!priv->constructor_impl(cx, obj, args))
+                return false;
+        }
 
         static_cast<GIWrapperBase*>(priv)->debug_lifecycle(obj,
                                                            "JSObject created");
diff --git a/gjs/profiler-private.h b/gjs/profiler-private.h
index 0bbb501d..53487ffb 100644
--- a/gjs/profiler-private.h
+++ b/gjs/profiler-private.h
@@ -7,10 +7,35 @@
 
 #include <stdint.h>
 
+#include <js/ProfilingStack.h>
+#include <js/RootingAPI.h>
+
 #include "gjs/context.h"
 #include "gjs/macros.h"
 #include "gjs/profiler.h"
 
+class AutoProfilerLabel {
+ public:
+    explicit inline AutoProfilerLabel(JSContext* cx, const char* label,
+                                      const char* dynamicString,
+                                      JS::ProfilingCategoryPair categoryPair =
+                                          JS::ProfilingCategoryPair::OTHER,
+                                      uint32_t flags = 0)
+        : m_stack(js::GetContextProfilingStackIfEnabled(cx)) {
+        if (m_stack)
+            m_stack->pushLabelFrame(label, dynamicString, this, categoryPair,
+                                    flags);
+    }
+
+    inline ~AutoProfilerLabel() {
+        if (m_stack)
+            m_stack->pop();
+    }
+
+ private:
+    ProfilingStack* m_stack;
+};
+
 GjsProfiler *_gjs_profiler_new(GjsContext *context);
 void _gjs_profiler_free(GjsProfiler *self);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]