validate: Fix GI warnings
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-runner.c
1 /* GStreamer
2  *
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>
6  *
7  * gst-validate-runner.c - Validate Runner class
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include <string.h>
26 #include <stdlib.h>
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #include "validate.h"
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"
38
39 GST_DEBUG_CATEGORY_STATIC (gst_validate_runner_debug);
40 #undef GST_CAT_DEFAULT
41 #define GST_CAT_DEFAULT gst_validate_runner_debug
42
43 static gboolean element_created = FALSE;
44
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.
49  */
50 static GstValidateRunner *first_runner = NULL;
51
52 /**
53  * SECTION:gst-validate-runner
54  * @short_description: Class that runs Gst Validate tests for a pipeline
55  *
56  * Allows you to test a pipeline within GstValidate. It is the object where
57  * all issue reporting is done.
58  *
59  * In the tools using GstValidate the only minimal code to be able to monitor
60  * your pipelines is:
61  *
62  * |[
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);
67  *
68  *  // Run the pipeline and do whatever you want with it
69  *
70  *  // In that same order
71  *  gst_object_unref (pipeline);
72  *  gst_object_unref (runner);
73  *  gst_object_unref (monitor);
74  * ]|
75  */
76
77 struct _GstValidateRunnerPrivate
78 {
79   GMutex mutex;
80   GList *reports;
81   GstValidateReportingDetails default_level;
82   GHashTable *reports_by_type;
83
84   /* A list of PatternLevel */
85   GList *report_pattern_levels;
86
87   /* Whether the runner was create with GST_TRACERS=validate or not) */
88   gboolean user_created;
89
90   gchar *pipeline_names;
91   gchar **pipeline_names_strv;
92 };
93
94 /* Describes the reporting level to apply to a name pattern */
95 typedef struct _PatternLevel
96 {
97   GPatternSpec *pattern;
98   GstValidateReportingDetails level;
99 } PatternLevel;
100
101 #define GST_VALIDATE_RUNNER_LOCK(r)                     \
102   G_STMT_START {                                        \
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); \
106   } G_STMT_END
107
108 #define GST_VALIDATE_RUNNER_UNLOCK(r)                   \
109   G_STMT_START {                                        \
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); \
113   } G_STMT_END
114
115 #define gst_validate_runner_parent_class parent_class
116 G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, GST_TYPE_TRACER);
117
118 /* signals */
119 enum
120 {
121   REPORT_ADDED_SIGNAL,
122   STOPPING_SIGNAL,
123   /* add more above */
124   LAST_SIGNAL
125 };
126
127 enum
128 {
129   PROP_0,
130   PROP_PARAMS,
131   PROP_LAST
132 };
133
134 static GParamSpec *properties[PROP_LAST];
135
136 static guint _signals[LAST_SIGNAL] = { 0, };
137
138 static gboolean
139 gst_validate_runner_should_monitor (GstValidateRunner * self,
140     GstElement * element)
141 {
142   gint i;
143   GstValidateMonitor *monitor;
144
145   if (!GST_IS_PIPELINE (element)) {
146     return FALSE;
147   }
148
149   if (self->priv->user_created)
150     return FALSE;
151
152   if (!self->priv->pipeline_names_strv)
153     return TRUE;
154
155   monitor = gst_validate_get_monitor (G_OBJECT (element));
156
157   if (monitor) {
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.",
161         element, monitor,
162         gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
163   }
164
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)))
168       return TRUE;
169   }
170
171   return FALSE;
172 }
173
174 static void
175 do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
176 {
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);
181   }
182 }
183
184 static gboolean
185 _parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
186 {
187   if (!str)
188     return FALSE;
189
190   /* works in place */
191   g_strstrip (str);
192
193   if (g_ascii_isdigit (str[0])) {
194     unsigned long l;
195     char *endptr;
196     l = strtoul (str, &endptr, 10);
197     if (endptr > str && endptr[0] == 0) {
198       *level = (GstValidateReportingDetails) l;
199     } else {
200       return FALSE;
201     }
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;
214   } else
215     return FALSE;
216
217   return TRUE;
218 }
219
220 static void
221 _free_report_pattern_level (PatternLevel * pattern_level)
222 {
223   g_pattern_spec_free (pattern_level->pattern);
224   g_free (pattern_level);
225 }
226
227 static void
228 _set_reporting_level_for_name (GstValidateRunner * runner,
229     const gchar * pattern, GstValidateReportingDetails level)
230 {
231   PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel));
232   GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
233
234   pattern_level->pattern = pattern_spec;
235   pattern_level->level = level;
236
237   /* Allow the user to single out a pad with the "element-name__pad-name" syntax
238    */
239   if (g_strrstr (pattern, "__"))
240     runner->priv->report_pattern_levels =
241         g_list_prepend (runner->priv->report_pattern_levels, pattern_level);
242   else
243     runner->priv->report_pattern_levels =
244         g_list_append (runner->priv->report_pattern_levels, pattern_level);
245 }
246
247 static void
248 _replace_double_colons (gchar * word)
249 {
250   while (word) {
251     word = strstr (word, "::");
252     if (word) {
253       word[0] = '_';
254       word[1] = '_';
255     }
256   }
257 }
258
259 static void
260 _set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
261 {
262   gchar **split;
263   gchar **walk;
264
265   g_assert (list);
266
267   GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
268
269   split = g_strsplit (list, ",", 0);
270
271   for (walk = split; *walk; walk++) {
272     _replace_double_colons (*walk);
273     if (strchr (*walk, ':')) {
274       gchar **values = g_strsplit (*walk, ":", 2);
275
276       if (values[0] && values[1]) {
277         GstValidateReportingDetails level;
278
279         if (_parse_reporting_level (values[1], &level))
280           _set_reporting_level_for_name (self, values[0], level);
281       }
282
283       g_strfreev (values);
284     } else {
285       GstValidateReportingDetails level;
286
287       if (_parse_reporting_level (*walk, &level))
288         self->priv->default_level = level;
289     }
290   }
291
292   g_strfreev (split);
293 }
294
295 static void
296 _init_report_levels (GstValidateRunner * self)
297 {
298   const gchar *env;
299
300   env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS");
301   if (env)
302     _set_report_levels_from_string (self, env);
303 }
304
305 static void
306 _unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
307 {
308   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
309 }
310
311 static void
312 gst_validate_runner_finalize (GObject * object)
313 {
314   GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
315
316   if (!runner->priv->user_created)
317     gst_validate_runner_exit (runner, TRUE);
318
319   g_list_free_full (runner->priv->reports,
320       (GDestroyNotify) gst_validate_report_unref);
321
322   g_list_free_full (runner->priv->report_pattern_levels,
323       (GDestroyNotify) _free_report_pattern_level);
324
325   g_mutex_clear (&runner->priv->mutex);
326
327   g_free (runner->priv->pipeline_names);
328   g_strfreev (runner->priv->pipeline_names_strv);
329
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);
333
334   G_OBJECT_CLASS (parent_class)->finalize (object);
335
336   if (!runner->priv->user_created)
337     gst_validate_deinit ();
338 }
339
340 static GObject *
341 gst_validate_runner_constructor (GType type, guint n_construct_params,
342     GObjectConstructParam * construct_params)
343 {
344   GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
345       n_construct_params, construct_params);
346
347   if (!gst_validate_is_initialized ()) {
348     first_runner = GST_VALIDATE_RUNNER (runner);
349     gst_validate_init ();
350     first_runner = NULL;
351
352     return runner;
353   }
354
355   return runner;
356 }
357
358
359 static void
360 gst_validate_runner_get_property (GObject * object, guint prop_id,
361     GValue * value, GParamSpec * pspec)
362 {
363   GstValidateRunner *runner;
364
365   runner = GST_VALIDATE_RUNNER (object);
366   switch (prop_id) {
367     case PROP_PARAMS:
368     {
369       g_value_set_string (value, runner->priv->pipeline_names);
370       break;
371     }
372     default:
373       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
374       break;
375   }
376 }
377
378 static void
379 gst_validate_runner_set_property (GObject * object, guint prop_id,
380     const GValue * value, GParamSpec * pspec)
381 {
382   GstValidateRunner *runner;
383
384   runner = GST_VALIDATE_RUNNER (object);
385   switch (prop_id) {
386     case PROP_PARAMS:
387     {
388       g_free (runner->priv->pipeline_names);
389       g_strfreev (runner->priv->pipeline_names_strv);
390
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);
395       break;
396     }
397     default:
398       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399       break;
400   }
401 }
402
403 static void
404 gst_validate_runner_class_init (GstValidateRunnerClass * klass)
405 {
406   GObjectClass *gobject_class;
407
408   gobject_class = G_OBJECT_CLASS (klass);
409
410   gobject_class->finalize = gst_validate_runner_finalize;
411
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;
415
416   g_type_class_add_private (klass, sizeof (GstValidateRunnerPrivate));
417
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);
421
422   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
423
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);
428
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);
432
433   GST_DEBUG_CATEGORY_INIT (gst_validate_runner_debug, "gstvalidaterunner",
434       GST_DEBUG_FG_YELLOW, "Gst validate runner");
435 }
436
437 static void
438 gst_validate_runner_init (GstValidateRunner * runner)
439 {
440   runner->priv = G_TYPE_INSTANCE_GET_PRIVATE (runner, GST_TYPE_VALIDATE_RUNNER,
441       GstValidateRunnerPrivate);
442   g_mutex_init (&runner->priv->mutex);
443
444   runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
445       g_direct_equal);
446
447   runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
448   _init_report_levels (runner);
449
450   gst_tracing_register_hook (GST_TRACER (runner), "element-new",
451       G_CALLBACK (do_element_new));
452 }
453
454 /**
455  * gst_validate_runner_new:
456  *
457  * Create a new #GstValidateRunner
458  *
459  * Returns: A newly created #GstValidateRunner
460  */
461 GstValidateRunner *
462 gst_validate_runner_new (void)
463 {
464   GstValidateRunner *runner;
465
466   if (first_runner) {
467     runner = first_runner;
468     first_runner = NULL;
469   } else if (element_created) {
470     g_error ("Should never create a GstValidateRunner after a GstElement"
471         "has been created in the same process.");
472
473     return NULL;
474   } else {
475     runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
476     runner->priv->user_created = TRUE;
477   }
478
479   return runner;
480 }
481
482 /*
483  * gst_validate_runner_get_default_reporting_level:
484  *
485  * Returns: the default #GstValidateReportingDetails used to output a report.
486  */
487 GstValidateReportingDetails
488 gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
489 {
490   return runner->priv->default_level;
491 }
492
493 /*
494  * gst_validate_runner_get_reporting_level_for_name:
495  *
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.
500  */
501 GstValidateReportingDetails
502 gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
503     const gchar * name)
504 {
505   GList *tmp;
506   gchar *fixed_name = g_strdup (name);
507
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)) {
512       g_free (fixed_name);
513
514       return pattern_level->level;
515     }
516   }
517
518   g_free (fixed_name);
519   return GST_VALIDATE_SHOW_UNKNOWN;
520 }
521
522 static void
523 synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
524 {
525   GstValidateIssueId issue_id;
526   GList *reports;
527
528   issue_id = report->issue->issue_id;
529
530   GST_VALIDATE_RUNNER_LOCK (runner);
531   reports =
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,
536       reports);
537   GST_VALIDATE_RUNNER_UNLOCK (runner);
538 }
539
540 static void
541 _dot_pipeline (GstValidateReport * report, GstStructure * config)
542 {
543   GstPipeline *pipeline = gst_validate_reporter_get_pipeline (report->reporter);
544
545   if (pipeline) {
546     gint details = GST_DEBUG_GRAPH_SHOW_ALL;
547     report->dotfile_name =
548         g_strdup_printf ("%" GST_TIME_FORMAT "-validate-report-%s-on-%s-%s",
549         GST_TIME_ARGS (GST_CLOCK_DIFF (_priv_start_time,
550                 gst_util_get_timestamp ())),
551         gst_validate_report_level_get_name (report->level),
552         gst_validate_reporter_get_name (report->reporter),
553         g_quark_to_string (report->issue->issue_id));
554
555     if (config)
556       gst_structure_get_int (config, "details", &details);
557
558     GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline),
559         GST_DEBUG_GRAPH_SHOW_ALL, report->dotfile_name);
560
561     gst_object_unref (pipeline);
562   }
563
564 }
565
566 static void
567 gst_validate_runner_maybe_dot_pipeline (GstValidateRunner * runner,
568     GstValidateReport * report)
569 {
570   GList *config;
571
572   if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL ||
573       gst_validate_report_check_abort (report)) {
574
575     _dot_pipeline (report, NULL);
576     return;
577   }
578
579   for (config = gst_validate_plugin_get_config (NULL);
580       config; config = config->next) {
581
582     if (gst_structure_has_name (config->data, "core")) {
583       GstValidateReportLevel level;
584       const gchar *level_str,
585           *action = gst_structure_get_string (config->data, "action");
586
587       if (g_strcmp0 (action, "dot-pipeline"))
588         continue;
589
590       level_str = gst_structure_get_string (config->data, "report-level");
591       level = level_str ? gst_validate_report_level_from_name (level_str) :
592           GST_VALIDATE_REPORT_LEVEL_CRITICAL;
593
594       if (level >= report->level) {
595         _dot_pipeline (report, config->data);
596
597         return;
598       }
599     }
600   }
601 }
602
603 void
604 gst_validate_runner_add_report (GstValidateRunner * runner,
605     GstValidateReport * report)
606 {
607   GstValidateReportingDetails details, reporter_details, issue_type_details;
608
609   gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
610           report));
611   gst_validate_runner_maybe_dot_pipeline (runner, report);
612
613   details = reporter_details =
614       gst_validate_reporter_get_reporting_level (report->reporter);
615   issue_type_details =
616       gst_validate_runner_get_reporting_level_for_name (runner,
617       g_quark_to_string (report->issue->issue_id));
618
619   if (reporter_details == GST_VALIDATE_SHOW_UNKNOWN)
620     details = issue_type_details;
621
622   /* Let's use our own reporting strategy */
623   if (details == GST_VALIDATE_SHOW_UNKNOWN) {
624     gst_validate_report_set_reporting_level (report,
625         runner->priv->default_level);
626     switch (runner->priv->default_level) {
627       case GST_VALIDATE_SHOW_NONE:
628         return;
629       case GST_VALIDATE_SHOW_SMART:
630         if (!gst_validate_report_check_abort (report) &&
631             report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
632             !report->trace) {
633           synthesize_reports (runner, report);
634           return;
635         }
636       case GST_VALIDATE_SHOW_SYNTHETIC:
637         if (!report->trace) {
638           synthesize_reports (runner, report);
639           return;
640         }
641       default:
642         break;
643     }
644   } else if (details == GST_VALIDATE_SHOW_NONE) {
645     GST_DEBUG ("Not reporting.");
646     return;
647   }
648
649   GST_VALIDATE_RUNNER_LOCK (runner);
650   runner->priv->reports =
651       g_list_append (runner->priv->reports, gst_validate_report_ref (report));
652   GST_VALIDATE_RUNNER_UNLOCK (runner);
653
654   g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
655 }
656
657 /**
658  * gst_validate_runner_get_reports_count:
659  * @runner: The $GstValidateRunner to get the number of report from
660  *
661  * Get the number of reports present in the runner:
662  *
663  * Returns: The number of report present in the runner.
664  */
665 guint
666 gst_validate_runner_get_reports_count (GstValidateRunner * runner)
667 {
668   GList *tmp;
669   guint l;
670
671   g_return_val_if_fail (runner != NULL, 0);
672
673   GST_VALIDATE_RUNNER_LOCK (runner);
674   l = g_list_length (runner->priv->reports);
675   for (tmp = runner->priv->reports; tmp; tmp = tmp->next)
676     l += g_list_length (((GstValidateReport *) tmp->data)->repeated_reports);
677   l += g_hash_table_size (runner->priv->reports_by_type);
678   GST_VALIDATE_RUNNER_UNLOCK (runner);
679
680   return l;
681 }
682
683 /**
684  * gst_validate_runner_get_reports:
685  * @runner: The #GstValidateRunner
686  *
687  * Return: (element-type GstValidateReport)(transfer full): all the reports
688  */
689 GList *
690 gst_validate_runner_get_reports (GstValidateRunner * runner)
691 {
692   GList *ret;
693
694   GST_VALIDATE_RUNNER_LOCK (runner);
695   ret =
696       g_list_copy_deep (runner->priv->reports,
697       (GCopyFunc) gst_validate_report_ref, NULL);
698   GST_VALIDATE_RUNNER_UNLOCK (runner);
699
700   return ret;
701 }
702
703 static GList *
704 _do_report_synthesis (GstValidateRunner * runner)
705 {
706   GHashTableIter iter;
707   GList *reports, *tmp;
708   gpointer key, value;
709   GList *criticals = NULL;
710
711   /* Take the lock so the hash table won't be modified while we are iterating
712    * over it */
713   GST_VALIDATE_RUNNER_LOCK (runner);
714   g_hash_table_iter_init (&iter, runner->priv->reports_by_type);
715   while (g_hash_table_iter_next (&iter, &key, &value)) {
716     GstValidateReport *report;
717     reports = (GList *) value;
718
719     if (!reports)
720       continue;
721
722     report = (GstValidateReport *) (reports->data);
723
724     gst_validate_report_print_level (report);
725     gst_validate_report_print_detected_on (report);
726
727     if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
728       criticals = g_list_append (criticals, report);
729       gst_validate_report_print_details (report);
730     }
731
732     for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
733       report = (GstValidateReport *) (tmp->data);
734       gst_validate_report_print_detected_on (report);
735
736       if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
737         criticals = g_list_append (criticals, report);
738         gst_validate_report_print_details (report);
739       }
740     }
741     report = (GstValidateReport *) (reports->data);
742     gst_validate_report_print_description (report);
743     gst_validate_printf (NULL, "\n");
744   }
745   GST_VALIDATE_RUNNER_UNLOCK (runner);
746
747   return criticals;
748 }
749
750 /**
751  * gst_validate_runner_printf:
752  * @runner: The #GstValidateRunner to print all the reports for
753  *
754  * Prints all the report on the terminal or on wherever set
755  * in the #GST_VALIDATE_FILE env variable.
756  *
757  * Returns: 0 if no critical error has been found and 18 if a critical
758  * error has been detected. That return value is usually to be used as
759  * exit code of the application.
760  */
761 int
762 gst_validate_runner_printf (GstValidateRunner * runner)
763 {
764   GList *reports, *tmp;
765   int ret = 0;
766   GList *criticals = NULL;
767
768   criticals = _do_report_synthesis (runner);
769   reports = gst_validate_runner_get_reports (runner);
770   for (tmp = reports; tmp; tmp = tmp->next) {
771     GstValidateReport *report = tmp->data;
772
773     if (gst_validate_report_should_print (report))
774       gst_validate_report_printf (report);
775
776     if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
777       criticals = g_list_append (criticals, tmp->data);
778     }
779   }
780
781   if (criticals) {
782     GList *iter;
783
784     g_printerr ("\n\n==== Got criticals, Return value set to 18 ====\n");
785     ret = 18;
786
787     for (iter = criticals; iter; iter = iter->next) {
788       g_printerr ("     Critical error %s\n",
789           ((GstValidateReport *) (iter->data))->message);
790     }
791     g_printerr ("\n");
792   }
793
794   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
795   g_list_free (criticals);
796   gst_validate_printf (NULL, "Issues found: %u\n",
797       gst_validate_runner_get_reports_count (runner));
798   return ret;
799 }
800
801 int
802 gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
803 {
804   gint ret = 0;
805
806   g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
807
808   if (print_result) {
809     ret = gst_validate_runner_printf (runner);
810   } else {
811     GList *tmp;
812
813     for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
814       GstValidateReport *report = tmp->data;
815
816       if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
817         ret = 18;
818     }
819   }
820
821   return ret;
822 }
823
824 void
825 gst_validate_init_runner (void)
826 {
827   if (!first_runner) {
828     first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
829     first_runner->priv->user_created = TRUE;
830   }                             /* else the first runner has been created through the GST_TRACERS system */
831 }
832
833 void
834 gst_validate_deinit_runner (void)
835 {
836   g_clear_object (&first_runner);
837 }
838
839 GstValidateReportingDetails
840 gst_validate_runner_get_default_reporting_details (GstValidateRunner * runner)
841 {
842   return runner->priv->default_level;
843 }
844
845 #ifdef __GST_VALIDATE_PLUGIN
846 static gboolean
847 plugin_init (GstPlugin * plugin)
848 {
849   if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
850     return FALSE;
851
852   return TRUE;
853 }
854
855 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
856     "GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
857     GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
858 #endif /* __GST_VALIDATE_PLUGIN */