1 /* GStreamer
2 * Copyright (C) 2016 Stefan Sauer <ensonic@users.sf.net>
3 *
4 * gsttracerrecord.c: tracer log record class
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 /**
23 * SECTION:gsttracerrecord
24 * @title: GstTracerRecord
25 * @short_description: Trace log entry class
26 *
27 * Tracing modules will create instances of this class to announce the data they
28 * will log and create a log formatter.
29 *
30 * Since: 1.8
31 */
32
33 #define GST_USE_UNSTABLE_API
34
35 #include "gst_private.h"
36 #include "gstenumtypes.h"
37 #include "gstinfo.h"
38 #include "gststructure.h"
39 #include "gsttracerrecord.h"
40 #include "gstvalue.h"
41 #include <gobject/gvaluecollector.h>
42
43 GST_DEBUG_CATEGORY_EXTERN (tracer_debug);
44 #define GST_CAT_DEFAULT tracer_debug
45
46 struct _GstTracerRecord
47 {
48 GstObject parent;
49
50 GstStructure *spec;
51 gchar *format;
52 };
53
54 struct _GstTracerRecordClass
55 {
56 GstObjectClass parent_class;
57 };
58
59 #define gst_tracer_record_parent_class parent_class
60 G_DEFINE_TYPE (GstTracerRecord, gst_tracer_record, GST_TYPE_OBJECT);
61
62 static gboolean
build_field_template(GQuark field_id,const GValue * value,gpointer user_data)63 build_field_template (GQuark field_id, const GValue * value, gpointer user_data)
64 {
65 GString *s = (GString *) user_data;
66 const GstStructure *sub;
67 GValue template_value = { 0, };
68 GType type = G_TYPE_INVALID;
69 GstTracerValueFlags flags = GST_TRACER_VALUE_FLAGS_NONE;
70 gboolean res;
71
72 if (G_VALUE_TYPE (value) != GST_TYPE_STRUCTURE) {
73 GST_WARNING ("expected field of type GstStructure, but %s is %s",
74 g_quark_to_string (field_id), G_VALUE_TYPE_NAME (value));
75 return FALSE;
76 }
77
78 sub = gst_value_get_structure (value);
79 gst_structure_get (sub, "type", G_TYPE_GTYPE, &type, "flags",
80 GST_TYPE_TRACER_VALUE_FLAGS, &flags, NULL);
81
82 if (flags & GST_TRACER_VALUE_FLAGS_OPTIONAL) {
83 gchar *opt_name = g_strconcat ("have-", g_quark_to_string (field_id), NULL);
84
85 /* add a boolean field, that indicates the presence of the next field */
86 g_value_init (&template_value, G_TYPE_BOOLEAN);
87 priv__gst_structure_append_template_to_gstring (g_quark_from_string
88 (opt_name), &template_value, s);
89 g_value_unset (&template_value);
90 g_free (opt_name);
91 }
92
93 g_value_init (&template_value, type);
94 res = priv__gst_structure_append_template_to_gstring (field_id,
95 &template_value, s);
96 g_value_unset (&template_value);
97 return res;
98 }
99
100 static void
gst_tracer_record_build_format(GstTracerRecord * self)101 gst_tracer_record_build_format (GstTracerRecord * self)
102 {
103 GstStructure *structure = self->spec;
104 GString *s;
105 gchar *name = (gchar *) g_quark_to_string (structure->name);
106 gchar *p;
107
108 g_return_if_fail (g_str_has_suffix (name, ".class"));
109
110 /* announce the format */
111 GST_TRACE ("%" GST_PTR_FORMAT, structure);
112
113 /* cut off '.class' suffix */
114 name = g_strdup (name);
115 p = strrchr (name, '.');
116 g_assert (p != NULL);
117 *p = '\0';
118
119 s = g_string_sized_new (STRUCTURE_ESTIMATED_STRING_LEN (structure));
120 g_string_append (s, name);
121 gst_structure_foreach (structure, build_field_template, s);
122 g_string_append_c (s, ';');
123
124 self->format = g_string_free (s, FALSE);
125 GST_DEBUG ("new format string: %s", self->format);
126 g_free (name);
127 }
128
129 static void
gst_tracer_record_dispose(GObject * object)130 gst_tracer_record_dispose (GObject * object)
131 {
132 GstTracerRecord *self = GST_TRACER_RECORD (object);
133
134 if (self->spec) {
135 gst_structure_free (self->spec);
136 self->spec = NULL;
137 }
138 g_free (self->format);
139 self->format = NULL;
140 }
141
142 static void
gst_tracer_record_class_init(GstTracerRecordClass * klass)143 gst_tracer_record_class_init (GstTracerRecordClass * klass)
144 {
145 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
146
147 gobject_class->dispose = gst_tracer_record_dispose;
148 }
149
150 static void
gst_tracer_record_init(GstTracerRecord * self)151 gst_tracer_record_init (GstTracerRecord * self)
152 {
153 }
154
155 /**
156 * gst_tracer_record_new:
157 * @name: name of new record, must end on ".class".
158 * @firstfield: name of first field to set
159 * @...: additional arguments
160
161 *
162 * Create a new tracer record. The record instance can be used to efficiently
163 * log entries using gst_tracer_record_log().
164 *
165 * The @name without the ".class" suffix will be used for the log records.
166 * There must be fields for each value that gets logged where the field name is
167 * the value name. The field must be a #GstStructure describing the value. The
168 * sub structure must contain a field called 'type' of %G_TYPE_GTYPE that
169 * contains the GType of the value. The resulting #GstTracerRecord will take
170 * ownership of the field structures.
171 *
172 * The way to deal with optional values is to log an additional boolean before
173 * the optional field, that if %TRUE signals that the optional field is valid
174 * and %FALSE signals that the optional field should be ignored. One must still
175 * log a placeholder value for the optional field though. Please also note, that
176 * pointer type values must not be NULL - the underlying serialisation can not
177 * handle that right now.
178 *
179 * > Please note that this is still under discussion and subject to change.
180 *
181 * Returns: (transfer full): a new #GstTracerRecord
182 */
183 GstTracerRecord *
gst_tracer_record_new(const gchar * name,const gchar * firstfield,...)184 gst_tracer_record_new (const gchar * name, const gchar * firstfield, ...)
185 {
186 GstTracerRecord *self;
187 GstStructure *structure;
188 va_list varargs;
189 gchar *err = NULL;
190 GType type;
191 GQuark id;
192
193 va_start (varargs, firstfield);
194 structure = gst_structure_new_empty (name);
195
196 while (firstfield) {
197 GValue val = { 0, };
198
199 id = g_quark_from_string (firstfield);
200 type = va_arg (varargs, GType);
201
202 /* all fields passed here must be GstStructures which we take over */
203 if (type != GST_TYPE_STRUCTURE) {
204 GST_WARNING ("expected field of type GstStructure, but %s is %s",
205 firstfield, g_type_name (type));
206 }
207
208 G_VALUE_COLLECT_INIT (&val, type, varargs, G_VALUE_NOCOPY_CONTENTS, &err);
209 if (G_UNLIKELY (err)) {
210 g_critical ("%s", err);
211 g_free (err);
212 break;
213 }
214 /* see boxed_proxy_collect_value */
215 val.data[1].v_uint &= ~G_VALUE_NOCOPY_CONTENTS;
216 gst_structure_id_take_value (structure, id, &val);
217
218 firstfield = va_arg (varargs, gchar *);
219 }
220 va_end (varargs);
221
222 self = g_object_new (GST_TYPE_TRACER_RECORD, NULL);
223
224 /* Clear floating flag */
225 gst_object_ref_sink (self);
226
227 self->spec = structure;
228 gst_tracer_record_build_format (self);
229
230 return self;
231 }
232
233 #ifndef GST_DISABLE_GST_DEBUG
234 /**
235 * gst_tracer_record_log:
236 * @self: the tracer-record
237 * @...: the args as described in the spec-
238 *
239 * Serialzes the trace event into the log.
240 *
241 * Right now this is using the gstreamer debug log with the level TRACE (7) and
242 * the category "GST_TRACER".
243 *
244 * > Please note that this is still under discussion and subject to change.
245 */
246 void
gst_tracer_record_log(GstTracerRecord * self,...)247 gst_tracer_record_log (GstTracerRecord * self, ...)
248 {
249 va_list var_args;
250
251 /*
252 * does it make sense to use the {file, line, func} from the tracer hook?
253 * a)
254 * - we'd need to pass them in the macros to gst_tracer_dispatch()
255 * - and each tracer needs to grab them from the va_list and pass them here
256 * b)
257 * - we create a context in dispatch, pass that to the tracer
258 * - and the tracer will pass that here
259 * ideally we also use *our* ts instead of the one that
260 * gst_debug_log_default() will pick
261 */
262
263 va_start (var_args, self);
264 if (G_LIKELY (GST_LEVEL_TRACE <= _gst_debug_min)) {
265 gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_TRACE, "", "", 0, NULL,
266 self->format, var_args);
267 }
268 va_end (var_args);
269 }
270 #endif
271