3 * Copyright (C) 2013-2016 Collabora Ltd.
4 * Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
5 * Author: Thibault Saunier <thibault.saunier@collabora.com>
7 * gst-validate-runner.c - Validate Runner class
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
33 #include "gst-validate-internal.h"
34 #include "gst-validate-report.h"
35 #include "gst-validate-monitor-factory.h"
36 #include "gst-validate-override-registry.h"
37 #include "gst-validate-runner.h"
39 GST_DEBUG_CATEGORY_STATIC (gst_validate_runner_debug);
40 #undef GST_CAT_DEFAULT
41 #define GST_CAT_DEFAULT gst_validate_runner_debug
43 static gboolean element_created = FALSE;
45 /* We create a GstValidateRunner on _init ()
46 * so that we keep backward compatibility when
47 * the user create a Runner after creating the pipeline
48 * but the runner was actually already ready to be used.
50 static GstValidateRunner *first_runner = NULL;
53 * SECTION:gst-validate-runner
54 * @short_description: Class that runs Gst Validate tests for a pipeline
56 * Allows you to test a pipeline within GstValidate. It is the object where
57 * all issue reporting is done.
59 * In the tools using GstValidate the only minimal code to be able to monitor
63 * GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline");
64 * GstValidateRunner *runner = gst_validate_runner_new ();
65 * GstValidateMonitor *monitor = gst_validate_monitor_factory_create (
66 * GST_OBJECT (pipeline), runner, NULL);
68 * // Run the pipeline and do whatever you want with it
70 * // In that same order
71 * gst_object_unref (pipeline);
72 * gst_object_unref (runner);
73 * gst_object_unref (monitor);
77 struct _GstValidateRunnerPrivate
81 GstValidateReportingDetails default_level;
82 GHashTable *reports_by_type;
84 /* A list of PatternLevel */
85 GList *report_pattern_levels;
87 /* Whether the runner was create with GST_TRACERS=validate or not) */
88 gboolean user_created;
90 gchar *pipeline_names;
91 gchar **pipeline_names_strv;
94 /* Describes the reporting level to apply to a name pattern */
95 typedef struct _PatternLevel
97 GPatternSpec *pattern;
98 GstValidateReportingDetails level;
101 #define GST_VALIDATE_RUNNER_LOCK(r) \
103 GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
104 (g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \
105 GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
108 #define GST_VALIDATE_RUNNER_UNLOCK(r) \
110 GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
111 (g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex)); \
112 GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
115 #define gst_validate_runner_parent_class parent_class
116 G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, GST_TYPE_TRACER);
134 static GParamSpec *properties[PROP_LAST];
136 static guint _signals[LAST_SIGNAL] = { 0, };
139 gst_validate_runner_should_monitor (GstValidateRunner * self,
140 GstElement * element)
143 GstValidateMonitor *monitor;
145 if (!GST_IS_PIPELINE (element)) {
149 if (self->priv->user_created)
152 if (!self->priv->pipeline_names_strv)
155 monitor = gst_validate_get_monitor (G_OBJECT (element));
158 GST_ERROR_OBJECT (self, "Pipeline %" GST_PTR_FORMAT " is already"
159 " monitored by %" GST_PTR_FORMAT " using runner: %" GST_PTR_FORMAT
160 " NOT monitoring again.",
162 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
165 for (i = 0; self->priv->pipeline_names_strv[i]; i++) {
166 if (g_pattern_match_simple (self->priv->pipeline_names_strv[i],
167 GST_OBJECT_NAME (element)))
175 do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
177 element_created = TRUE;
178 if (gst_validate_runner_should_monitor (self, element)) {
179 /* the reference to the monitor is lost */
180 gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), self, NULL);
185 _parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
193 if (g_ascii_isdigit (str[0])) {
196 l = strtoul (str, &endptr, 10);
197 if (endptr > str && endptr[0] == 0) {
198 *level = (GstValidateReportingDetails) l;
202 } else if (g_ascii_strcasecmp (str, "smart") == 0) {
203 *level = GST_VALIDATE_SHOW_SMART;
204 } else if (g_ascii_strcasecmp (str, "none") == 0) {
205 *level = GST_VALIDATE_SHOW_NONE;
206 } else if (g_ascii_strcasecmp (str, "synthetic") == 0) {
207 *level = GST_VALIDATE_SHOW_SYNTHETIC;
208 } else if (g_ascii_strcasecmp (str, "subchain") == 0) {
209 *level = GST_VALIDATE_SHOW_SUBCHAIN;
210 } else if (g_ascii_strcasecmp (str, "monitor") == 0) {
211 *level = GST_VALIDATE_SHOW_MONITOR;
212 } else if (g_ascii_strcasecmp (str, "all") == 0) {
213 *level = GST_VALIDATE_SHOW_ALL;
221 _free_report_pattern_level (PatternLevel * pattern_level)
223 g_pattern_spec_free (pattern_level->pattern);
224 g_free (pattern_level);
228 _set_reporting_level_for_name (GstValidateRunner * runner,
229 const gchar * pattern, GstValidateReportingDetails level)
231 PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel));
232 GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
234 pattern_level->pattern = pattern_spec;
235 pattern_level->level = level;
237 /* Allow the user to single out a pad with the "element-name__pad-name" syntax
239 if (g_strrstr (pattern, "__"))
240 runner->priv->report_pattern_levels =
241 g_list_prepend (runner->priv->report_pattern_levels, pattern_level);
243 runner->priv->report_pattern_levels =
244 g_list_append (runner->priv->report_pattern_levels, pattern_level);
248 _replace_double_colons (gchar * word)
251 word = strstr (word, "::");
260 _set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
267 GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
269 split = g_strsplit (list, ",", 0);
271 for (walk = split; *walk; walk++) {
272 _replace_double_colons (*walk);
273 if (strchr (*walk, ':')) {
274 gchar **values = g_strsplit (*walk, ":", 2);
276 if (values[0] && values[1]) {
277 GstValidateReportingDetails level;
279 if (_parse_reporting_level (values[1], &level))
280 _set_reporting_level_for_name (self, values[0], level);
285 GstValidateReportingDetails level;
287 if (_parse_reporting_level (*walk, &level))
288 self->priv->default_level = level;
296 _init_report_levels (GstValidateRunner * self)
300 env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS");
302 _set_report_levels_from_string (self, env);
306 _unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
308 g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
312 gst_validate_runner_finalize (GObject * object)
314 GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
316 if (!runner->priv->user_created)
317 gst_validate_runner_exit (runner, TRUE);
319 g_list_free_full (runner->priv->reports,
320 (GDestroyNotify) gst_validate_report_unref);
322 g_list_free_full (runner->priv->report_pattern_levels,
323 (GDestroyNotify) _free_report_pattern_level);
325 g_mutex_clear (&runner->priv->mutex);
327 g_free (runner->priv->pipeline_names);
328 g_strfreev (runner->priv->pipeline_names_strv);
330 g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc)
331 _unref_report_list, NULL);
332 g_hash_table_destroy (runner->priv->reports_by_type);
334 G_OBJECT_CLASS (parent_class)->finalize (object);
336 if (!runner->priv->user_created)
337 gst_validate_deinit ();
341 gst_validate_runner_constructor (GType type, guint n_construct_params,
342 GObjectConstructParam * construct_params)
344 GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
345 n_construct_params, construct_params);
347 if (!gst_validate_is_initialized ()) {
348 first_runner = GST_VALIDATE_RUNNER (runner);
349 gst_validate_init ();
360 gst_validate_runner_get_property (GObject * object, guint prop_id,
361 GValue * value, GParamSpec * pspec)
363 GstValidateRunner *runner;
365 runner = GST_VALIDATE_RUNNER (object);
369 g_value_set_string (value, runner->priv->pipeline_names);
373 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
379 gst_validate_runner_set_property (GObject * object, guint prop_id,
380 const GValue * value, GParamSpec * pspec)
382 GstValidateRunner *runner;
384 runner = GST_VALIDATE_RUNNER (object);
388 g_free (runner->priv->pipeline_names);
389 g_strfreev (runner->priv->pipeline_names_strv);
391 runner->priv->pipeline_names = g_value_dup_string (value);
392 if (runner->priv->pipeline_names)
393 runner->priv->pipeline_names_strv =
394 g_strsplit (runner->priv->pipeline_names, ",", -1);
398 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404 gst_validate_runner_class_init (GstValidateRunnerClass * klass)
406 GObjectClass *gobject_class;
408 gobject_class = G_OBJECT_CLASS (klass);
410 gobject_class->finalize = gst_validate_runner_finalize;
412 gobject_class->set_property = gst_validate_runner_set_property;
413 gobject_class->get_property = gst_validate_runner_get_property;
414 gobject_class->constructor = gst_validate_runner_constructor;
416 g_type_class_add_private (klass, sizeof (GstValidateRunnerPrivate));
418 properties[PROP_PARAMS] =
419 g_param_spec_string ("params", "Params", "Extra configuration parameters",
420 NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
422 g_object_class_install_properties (gobject_class, PROP_LAST, properties);
424 _signals[REPORT_ADDED_SIGNAL] =
425 g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
426 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
427 GST_TYPE_VALIDATE_REPORT);
429 _signals[STOPPING_SIGNAL] =
430 g_signal_new ("stopping", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
431 NULL, NULL, NULL, G_TYPE_NONE, 0);
433 GST_DEBUG_CATEGORY_INIT (gst_validate_runner_debug, "gstvalidaterunner",
434 GST_DEBUG_FG_YELLOW, "Gst validate runner");
438 gst_validate_runner_init (GstValidateRunner * runner)
440 runner->priv = G_TYPE_INSTANCE_GET_PRIVATE (runner, GST_TYPE_VALIDATE_RUNNER,
441 GstValidateRunnerPrivate);
442 g_mutex_init (&runner->priv->mutex);
444 runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
447 runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
448 _init_report_levels (runner);
450 gst_tracing_register_hook (GST_TRACER (runner), "element-new",
451 G_CALLBACK (do_element_new));
455 * gst_validate_runner_new:
457 * Create a new #GstValidateRunner
459 * Returns: A newly created #GstValidateRunner
462 gst_validate_runner_new (void)
464 GstValidateRunner *runner;
467 runner = first_runner;
469 } else if (element_created) {
470 g_error ("Should never create a GstValidateRunner after a GstElement"
471 "has been created in the same process.");
475 runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
476 runner->priv->user_created = TRUE;
483 * gst_validate_runner_get_default_reporting_level:
485 * Returns: the default #GstValidateReportingDetails used to output a report.
487 GstValidateReportingDetails
488 gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
490 return runner->priv->default_level;
494 * gst_validate_runner_get_reporting_level_for_name:
496 * Returns: the #GstValidateReportingDetails that will be applied for a given name.
497 * If no pattern was set for such a name, this function will return
498 * #GST_VALIDATE_SHOW_UNKNOWN, and reporting for that name will
499 * default to the global reporting level.
501 GstValidateReportingDetails
502 gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
506 gchar *fixed_name = g_strdup (name);
508 _replace_double_colons (fixed_name);
509 for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) {
510 PatternLevel *pattern_level = (PatternLevel *) tmp->data;
511 if (g_pattern_match_string (pattern_level->pattern, fixed_name)) {
514 return pattern_level->level;
519 return GST_VALIDATE_SHOW_UNKNOWN;
523 synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
525 GstValidateIssueId issue_id;
528 issue_id = report->issue->issue_id;
530 GST_VALIDATE_RUNNER_LOCK (runner);
532 g_hash_table_lookup (runner->priv->reports_by_type,
533 (gconstpointer) issue_id);
534 reports = g_list_append (reports, gst_validate_report_ref (report));
535 g_hash_table_insert (runner->priv->reports_by_type, (gpointer) issue_id,
537 GST_VALIDATE_RUNNER_UNLOCK (runner);
541 gst_validate_runner_add_report (GstValidateRunner * runner,
542 GstValidateReport * report)
544 GstValidateReportingDetails details, reporter_details, issue_type_details;
546 gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
549 details = reporter_details =
550 gst_validate_reporter_get_reporting_level (report->reporter);
552 gst_validate_runner_get_reporting_level_for_name (runner,
553 g_quark_to_string (report->issue->issue_id));
555 if (reporter_details == GST_VALIDATE_SHOW_UNKNOWN)
556 details = issue_type_details;
558 /* Let's use our own reporting strategy */
559 if (details == GST_VALIDATE_SHOW_UNKNOWN) {
560 gst_validate_report_set_reporting_level (report,
561 runner->priv->default_level);
562 switch (runner->priv->default_level) {
563 case GST_VALIDATE_SHOW_NONE:
565 case GST_VALIDATE_SHOW_SMART:
566 if (!gst_validate_report_check_abort (report) &&
567 report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
568 synthesize_reports (runner, report);
571 case GST_VALIDATE_SHOW_SYNTHETIC:
572 synthesize_reports (runner, report);
577 } else if (details == GST_VALIDATE_SHOW_NONE) {
578 GST_DEBUG ("Not reporting.");
582 GST_VALIDATE_RUNNER_LOCK (runner);
583 runner->priv->reports =
584 g_list_append (runner->priv->reports, gst_validate_report_ref (report));
585 GST_VALIDATE_RUNNER_UNLOCK (runner);
587 g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
591 * gst_validate_runner_get_reports_count:
592 * @runner: The $GstValidateRunner to get the number of report from
594 * Get the number of reports present in the runner:
596 * Returns: The number of report present in the runner.
599 gst_validate_runner_get_reports_count (GstValidateRunner * runner)
604 g_return_val_if_fail (runner != NULL, 0);
606 GST_VALIDATE_RUNNER_LOCK (runner);
607 l = g_list_length (runner->priv->reports);
608 for (tmp = runner->priv->reports; tmp; tmp = tmp->next)
609 l += g_list_length (((GstValidateReport *) tmp->data)->repeated_reports);
610 l += g_hash_table_size (runner->priv->reports_by_type);
611 GST_VALIDATE_RUNNER_UNLOCK (runner);
617 * gst_validate_runner_get_reports:
618 * @runner: The #GstValidateRunner
620 * Return: (element-type GstValidate.Report)(transfer full): all the reports
623 gst_validate_runner_get_reports (GstValidateRunner * runner)
627 GST_VALIDATE_RUNNER_LOCK (runner);
629 g_list_copy_deep (runner->priv->reports,
630 (GCopyFunc) gst_validate_report_ref, NULL);
631 GST_VALIDATE_RUNNER_UNLOCK (runner);
637 _do_report_synthesis (GstValidateRunner * runner)
640 GList *reports, *tmp;
642 GList *criticals = NULL;
644 /* Take the lock so the hash table won't be modified while we are iterating
646 GST_VALIDATE_RUNNER_LOCK (runner);
647 g_hash_table_iter_init (&iter, runner->priv->reports_by_type);
648 while (g_hash_table_iter_next (&iter, &key, &value)) {
649 GstValidateReport *report;
650 reports = (GList *) value;
655 report = (GstValidateReport *) (reports->data);
657 gst_validate_report_print_level (report);
658 gst_validate_report_print_detected_on (report);
660 if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
661 criticals = g_list_append (criticals, report);
662 gst_validate_report_print_details (report);
665 for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
666 report = (GstValidateReport *) (tmp->data);
667 gst_validate_report_print_detected_on (report);
669 if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
670 criticals = g_list_append (criticals, report);
671 gst_validate_report_print_details (report);
674 report = (GstValidateReport *) (reports->data);
675 gst_validate_report_print_description (report);
676 gst_validate_printf (NULL, "\n");
678 GST_VALIDATE_RUNNER_UNLOCK (runner);
684 * gst_validate_runner_printf:
685 * @runner: The #GstValidateRunner to print all the reports for
687 * Prints all the report on the terminal or on wherever set
688 * in the #GST_VALIDATE_FILE env variable.
690 * Returns: 0 if no critical error has been found and 18 if a critical
691 * error has been detected. That return value is usually to be used as
692 * exit code of the application.
695 gst_validate_runner_printf (GstValidateRunner * runner)
697 GList *reports, *tmp;
699 GList *criticals = NULL;
701 criticals = _do_report_synthesis (runner);
702 reports = gst_validate_runner_get_reports (runner);
703 for (tmp = reports; tmp; tmp = tmp->next) {
704 GstValidateReport *report = tmp->data;
706 if (gst_validate_report_should_print (report))
707 gst_validate_report_printf (report);
709 if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
710 criticals = g_list_append (criticals, tmp->data);
717 g_printerr ("\n\n==== Got criticals, Return value set to 18 ====\n");
720 for (iter = criticals; iter; iter = iter->next) {
721 g_printerr (" Critical error %s\n",
722 ((GstValidateReport *) (iter->data))->message);
727 g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
728 g_list_free (criticals);
729 gst_validate_printf (NULL, "Issues found: %u\n",
730 gst_validate_runner_get_reports_count (runner));
735 gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
739 g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
742 ret = gst_validate_runner_printf (runner);
746 for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
747 GstValidateReport *report = tmp->data;
749 if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
758 gst_validate_init_runner (void)
761 first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
762 first_runner->priv->user_created = TRUE;
763 } /* else the first runner has been created through the GST_TRACERS system */
767 gst_validate_deinit_runner (void)
769 g_clear_object (&first_runner);
772 GstValidateReportingDetails
773 gst_validate_runner_get_default_reporting_details (GstValidateRunner * runner)
775 return runner->priv->default_level;
778 #ifdef __GST_VALIDATE_PLUGIN
780 plugin_init (GstPlugin * plugin)
782 if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
788 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
789 "GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
790 GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
791 #endif /* __GST_VALIDATE_PLUGIN */