--- /dev/null
+/* GStreamer
+ * Copyright (C) 2021 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete@collabora.com>
+ *
+ * gstfactories.c: A trace to log which plugin & factories are being used
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+/**
+ * SECTION:tracer-factories
+ * @short_description: log plugin and factories
+ *
+ * A tracing module that logs which plugins and factories are being used.
+ *
+ * This tracing module is particularly useful in conjuction with the `gst-stats`
+ * program to generate a list of plugins and elements that are loaded by a
+ * particular application to generate a minimal custom build of GStreamer.
+ *
+ * As a very simple example, you can run your application like this:
+ * ```
+ * $ GST_TRACERS=factories GST_DEBUG=GST_TRACER:7 gst-launch-1.0 audiotestsrc num-buffers=10 ! fakesink 2> log.txt
+ * ...
+ * $ gst-stats-1.0 log.txt
+ * Plugins used: audiotestsrc;coreelements
+ * Elements: audiotestsrc:audiotestsrc;coreelements:fakesink
+ * Device-providers:
+ * Typefinds:
+ * Dynamic-types:
+ * ```
+ *
+ * Based on this information, one can build a minimal, yet sufficient
+ * build of GStreamer using gst-build with a configuration like this one:
+ * ```
+ * meson setup builddir -Dgst-full-elements="audiotestsrc:audiotestsrc;coreelements:fakesink"
+ * ```
+ *
+ * Since: 1.20
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstfactories.h"
+
+G_DEFINE_TYPE (GstFactoriesTracer, gst_factories_tracer, GST_TYPE_TRACER);
+
+static GstTracerRecord *tr_factory_used;
+
+static void
+do_element_new (GstFactoriesTracer * self, GstClockTime ts,
+ GstElement * element)
+{
+ const gchar *plugin_name;
+ const gchar *factory_name;
+ GstPluginFeature *feature;
+ GstElementFactory *factory = gst_element_get_factory (element);
+ const gchar *source_module_name = "Unknown";
+ GstPlugin *plugin;
+
+ if (factory == NULL)
+ return;
+
+ feature = GST_PLUGIN_FEATURE (factory);
+
+ factory_name = gst_plugin_feature_get_name (feature);
+ plugin_name = gst_plugin_feature_get_plugin_name (feature);
+
+ if (factory_name == NULL)
+ factory_name = "";
+ if (plugin_name == NULL)
+ plugin_name = "";
+
+ plugin = gst_plugin_feature_get_plugin (feature);
+ if (plugin)
+ source_module_name = gst_plugin_get_source (plugin);
+
+ gst_tracer_record_log (tr_factory_used,
+ (guint64) (guintptr) g_thread_self (), ts, "element", factory_name,
+ plugin_name, source_module_name);
+
+ g_clear_object (&plugin);
+}
+
+static void
+do_plugin_feature_loaded (GstFactoriesTracer * self, GstClockTime ts,
+ GstPluginFeature * feature)
+{
+ const gchar *plugin_name;
+ const gchar *factory_name;
+ const gchar *factory_type;
+ const gchar *source_module_name = "Unknown";
+ GstPlugin *plugin;
+
+ /* Only care about elements when one is created */
+ if (GST_IS_ELEMENT_FACTORY (feature))
+ return;
+
+ if (GST_IS_TYPE_FIND_FACTORY (feature))
+ factory_type = "typefind";
+ else if (GST_IS_DEVICE_PROVIDER_FACTORY (feature))
+ factory_type = "device-provider";
+ else if (GST_IS_DYNAMIC_TYPE_FACTORY (feature))
+ factory_type = "dynamic-type";
+ else
+ g_assert_not_reached ();
+
+ factory_name = gst_plugin_feature_get_name (feature);
+ plugin_name = gst_plugin_feature_get_plugin_name (feature);
+
+ if (factory_name == NULL)
+ factory_name = "";
+ if (plugin_name == NULL)
+ plugin_name = "";
+
+ plugin = gst_plugin_feature_get_plugin (feature);
+ if (plugin)
+ source_module_name = gst_plugin_get_source (plugin);
+ if (source_module_name == NULL)
+ source_module_name = "";
+
+ gst_tracer_record_log (tr_factory_used,
+ (guint64) (guintptr) g_thread_self (), ts, factory_type, factory_name,
+ plugin_name, source_module_name);
+
+ g_clear_object (&plugin);
+}
+
+static void
+gst_factories_tracer_class_init (GstFactoriesTracerClass * klass)
+{
+ /* announce trace formats */
+ /* *INDENT-OFF* */
+ tr_factory_used = gst_tracer_record_new ("factory-used.class",
+ "thread-id", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
+ "type", G_TYPE_GTYPE, G_TYPE_UINT64,
+ "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_THREAD,
+ NULL),
+ "ts", GST_TYPE_STRUCTURE, gst_structure_new ("value",
+ "type", G_TYPE_GTYPE, G_TYPE_UINT64,
+ "description", G_TYPE_STRING, "event ts",
+ NULL),
+ "factory-type", GST_TYPE_STRUCTURE, gst_structure_new ("value",
+ "type", G_TYPE_GTYPE, G_TYPE_STRING,
+ "description", G_TYPE_STRING, "type name of the factory",
+ NULL),
+ "factory", GST_TYPE_STRUCTURE, gst_structure_new ("value",
+ "type", G_TYPE_GTYPE, G_TYPE_STRING,
+ "description", G_TYPE_STRING, "name of the object factory",
+ NULL),
+ "plugin", GST_TYPE_STRUCTURE, gst_structure_new ("value",
+ "type", G_TYPE_GTYPE, G_TYPE_STRING,
+ "description", G_TYPE_STRING, "name of the plugin",
+ NULL),
+ "source-module", GST_TYPE_STRUCTURE, gst_structure_new ("value",
+ "type", G_TYPE_GTYPE, G_TYPE_STRING,
+ "description", G_TYPE_STRING, "name of the source module this feature is from",
+ NULL),
+ NULL);
+ /* *INDENT-ON* */
+
+ GST_OBJECT_FLAG_SET (tr_factory_used, GST_OBJECT_FLAG_MAY_BE_LEAKED);
+}
+
+static void
+gst_factories_tracer_init (GstFactoriesTracer * self)
+{
+ GstTracer *tracer = GST_TRACER (self);
+
+ gst_tracing_register_hook (tracer, "element-new",
+ G_CALLBACK (do_element_new));
+ gst_tracing_register_hook (tracer, "plugin-feature-loaded",
+ G_CALLBACK (do_plugin_feature_loaded));
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2021 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete@collabora.com>
+ *
+ * gstfactories.h: A trace to log which plugin & factories are being used
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_FACTORIES_TRACER_H__
+#define __GST_FACTORIES_TRACER_H__
+
+#include <gst/gst.h>
+#include <gst/gsttracer.h>
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(GstFactoriesTracer, gst_factories_tracer, GST,
+ FACTORIES_TRACER, GstTracer)
+/**
+ * GstFactoriesTracer:
+ *
+ * Opaque #GstFactoriesTracer data structure
+ */
+struct _GstFactoriesTracer {
+ GstTracer parent;
+
+ /*< private >*/
+};
+
+G_END_DECLS
+
+#endif /* __GST_FACTORIES_TRACER_H__ */
static guint total_cpuload = 0;
static gboolean have_cpuload = FALSE;
+static GPtrArray *plugin_stats = NULL;
+
static gboolean have_latency = FALSE;
static gboolean have_element_latency = FALSE;
static gboolean have_element_reported_latency = FALSE;
guint cpuload;
} GstThreadStats;
+static const gchar *FACTORY_TYPES[] = {
+ "element",
+ "device-provider",
+ "typefind",
+ "dynamic-type",
+};
+
+#define N_FACTORY_TYPES G_N_ELEMENTS(FACTORY_TYPES)
+
+typedef struct
+{
+ gchar *name;
+
+ GPtrArray *factories[N_FACTORY_TYPES];
+} GstPluginStats;
+
/* stats helper */
static gint
g_slice_free (GstThreadStats, data);
}
+static GstPluginStats *
+new_plugin_stats (const gchar * plugin_name)
+{
+ GstPluginStats *plugin = g_slice_new (GstPluginStats);
+ guint i;
+
+ plugin->name = g_strdup (plugin_name);
+
+ for (i = 0; i < N_FACTORY_TYPES; i++)
+ plugin->factories[i] = g_ptr_array_new_with_free_func (g_free);
+
+ g_ptr_array_add (plugin_stats, plugin);
+
+ return plugin;
+}
+
+static void
+free_plugin_stats (gpointer data)
+{
+ GstPluginStats *plugin = data;
+ guint i;
+
+ g_free (plugin->name);
+
+ for (i = 0; i < N_FACTORY_TYPES; i++)
+ g_ptr_array_unref (plugin->factories[i]);
+
+ g_slice_free (GstPluginStats, data);
+}
+
static void
do_pad_stats (GstPadStats * stats, guint elem_ix, guint size, guint64 ts,
guint64 buffer_ts, guint64 buffer_dur, GstBufferFlags buffer_flags)
have_element_reported_latency = TRUE;
}
+static void
+do_factory_used (GstStructure * s)
+{
+ const gchar *factory = NULL;
+ const gchar *factory_type = NULL;
+ const gchar *plugin_name = NULL;
+ GstPluginStats *plugin = NULL;
+ guint i, f;
+
+ factory = gst_structure_get_string (s, "factory");
+ factory_type = gst_structure_get_string (s, "factory-type");
+ plugin_name = gst_structure_get_string (s, "plugin");
+
+ if (!g_strcmp0 (plugin_name, "staticelements"))
+ return;
+
+ if (plugin_name == NULL || plugin_name[0] == 0)
+ plugin_name = "built-in";
+
+ for (f = 0; f < N_FACTORY_TYPES; f++)
+ if (!g_strcmp0 (factory_type, FACTORY_TYPES[f]))
+ break;
+ if (f == N_FACTORY_TYPES)
+ return;
+
+ for (i = 0; i < plugin_stats->len; i++) {
+ GstPluginStats *tmp_plugin = g_ptr_array_index (plugin_stats, i);
+ if (!strcmp (tmp_plugin->name, plugin_name)) {
+ plugin = tmp_plugin;
+ break;
+ }
+ }
+
+ if (plugin == NULL)
+ plugin = new_plugin_stats (plugin_name);
+
+ if (factory && factory[0] &&
+ !g_ptr_array_find_with_equal_func (plugin->factories[f], factory,
+ g_str_equal, NULL))
+ g_ptr_array_add (plugin->factories[f], g_strdup (factory));
+}
+
/* reporting */
static gint
free_latency_stats);
element_reported_latencies = g_queue_new ();
+ plugin_stats = g_ptr_array_new_with_free_func (free_plugin_stats);
+
return TRUE;
}
element_reported_latencies = NULL;
}
+ g_clear_pointer (&plugin_stats, g_ptr_array_unref);
+
if (raw_log)
g_regex_unref (raw_log);
if (ansi_log)
g_regex_unref (ansi_log);
}
+static gint
+compare_plugin_stats (gconstpointer a, gconstpointer b)
+{
+ const GstPluginStats *plugin_a = *(GstPluginStats **) a;
+ const GstPluginStats *plugin_b = *(GstPluginStats **) b;
+
+ return strcmp (plugin_a->name, plugin_b->name);
+}
+
+static gint
+compare_string (gconstpointer a, gconstpointer b)
+{
+ const char *str_a = *(const char **) a;
+ const char *str_b = *(const char **) b;
+
+ return strcmp (str_a, str_b);
+}
+
static void
print_stats (void)
{
(GFunc) reported_latencies_foreach_print_stats, NULL);
puts ("");
}
+
+ if (plugin_stats->len > 0) {
+ guint i, j, f;
+
+ g_ptr_array_sort (plugin_stats, compare_plugin_stats);
+
+ printf ("Plugins used: ");
+ for (i = 0; i < plugin_stats->len; i++) {
+ GstPluginStats *ps = g_ptr_array_index (plugin_stats, i);
+ printf ("%s%s", i == 0 ? "" : ";", ps->name);
+ }
+ printf ("\n");
+
+ for (f = 0; f < N_FACTORY_TYPES; f++) {
+ gboolean first = TRUE;
+
+ printf ("%c%ss: ", g_ascii_toupper (FACTORY_TYPES[f][0]),
+ FACTORY_TYPES[f] + 1);
+ for (i = 0; i < plugin_stats->len; i++) {
+ GstPluginStats *ps = g_ptr_array_index (plugin_stats, i);
+
+ if (ps->factories[f]->len > 0) {
+ printf ("%s%s:", first ? "" : ";", ps->name);
+ first = FALSE;
+
+ g_ptr_array_sort (ps->factories[f], compare_string);
+
+ for (j = 0; j < ps->factories[f]->len; j++) {
+ const gchar *factory = g_ptr_array_index (ps->factories[f], j);
+
+ printf ("%s%s", j == 0 ? "" : ",", factory);
+ }
+ }
+ }
+ printf ("\n");
+ }
+ }
}
static void
do_element_latency_stats (s);
} else if (!strcmp (name, "element-reported-latency")) {
do_element_reported_latency (s);
+ } else if (!strcmp (name, "factory-used")) {
+ do_factory_used (s);
} else {
// TODO(ensonic): parse the xxx.class log lines
if (!g_str_has_suffix (data, ".class")) {