diff options
-rw-r--r-- | docs/plugins/gst_plugins_cache.json | 1 | ||||
-rw-r--r-- | plugins/tracers/gstfactories.c | 187 | ||||
-rw-r--r-- | plugins/tracers/gstfactories.h | 46 | ||||
-rw-r--r-- | plugins/tracers/gsttracers.c | 4 | ||||
-rw-r--r-- | plugins/tracers/meson.build | 1 | ||||
-rw-r--r-- | tools/gst-stats.c | 151 |
6 files changed, 390 insertions, 0 deletions
diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 66f5bd1f6c..67927f5652 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -2868,6 +2868,7 @@ "package": "GStreamer", "source": "gstreamer", "tracers": { + "factories": {}, "latency": {}, "leaks": {}, "log": {}, diff --git a/plugins/tracers/gstfactories.c b/plugins/tracers/gstfactories.c new file mode 100644 index 0000000000..fbb2505e20 --- /dev/null +++ b/plugins/tracers/gstfactories.c @@ -0,0 +1,187 @@ +/* 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)); +} diff --git a/plugins/tracers/gstfactories.h b/plugins/tracers/gstfactories.h new file mode 100644 index 0000000000..950551538a --- /dev/null +++ b/plugins/tracers/gstfactories.h @@ -0,0 +1,46 @@ +/* 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__ */ diff --git a/plugins/tracers/gsttracers.c b/plugins/tracers/gsttracers.c index b03e25aeec..e5fe5d0581 100644 --- a/plugins/tracers/gsttracers.c +++ b/plugins/tracers/gsttracers.c @@ -29,6 +29,7 @@ #include "gstrusage.h" #include "gststats.h" #include "gstleaks.h" +#include "gstfactories.h" static gboolean plugin_init (GstPlugin * plugin) @@ -47,6 +48,9 @@ plugin_init (GstPlugin * plugin) return FALSE; if (!gst_tracer_register (plugin, "leaks", gst_leaks_tracer_get_type ())) return FALSE; + if (!gst_tracer_register (plugin, "factories", + gst_factories_tracer_get_type ())) + return FALSE; return TRUE; } diff --git a/plugins/tracers/meson.build b/plugins/tracers/meson.build index 879cd9394f..8039c9be85 100644 --- a/plugins/tracers/meson.build +++ b/plugins/tracers/meson.build @@ -10,6 +10,7 @@ gst_tracers_sources = [ 'gstleaks.c', 'gststats.c', 'gsttracers.c', + 'gstfactories.c' ] if gst_debug diff --git a/tools/gst-stats.c b/tools/gst-stats.c index 8b490905c0..597c1ba1b7 100644 --- a/tools/gst-stats.c +++ b/tools/gst-stats.c @@ -47,6 +47,8 @@ static GstClockTime last_ts = G_GUINT64_CONSTANT (0); 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; @@ -122,6 +124,22 @@ typedef struct 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 @@ -283,6 +301,36 @@ free_thread_stats (gpointer data) 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) @@ -608,6 +656,48 @@ do_element_reported_latency (GstStructure * s) 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 @@ -874,6 +964,8 @@ init (void) free_latency_stats); element_reported_latencies = g_queue_new (); + plugin_stats = g_ptr_array_new_with_free_func (free_plugin_stats); + return TRUE; } @@ -902,12 +994,32 @@ done (void) 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) { @@ -1012,6 +1124,43 @@ 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 @@ -1072,6 +1221,8 @@ collect_stats (const gchar * filename) 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")) { |