caps: Use correct size for caps allocation
[platform/upstream/gstreamer.git] / gst / gsttrace.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gsttrace.c: Tracing functions (deprecated)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:gsttrace
25  * @short_description: Tracing functionality
26  *
27  * Traces allows to track object allocation. They provide a instance counter per
28  * #GType. The counter is incremented for each object allocated and decremented
29  * it when it's freed.
30  *
31  * <example>
32  * <title>Tracing object instances</title>
33  *   <programlisting>
34  *     // trace un-freed object instances
35  *     gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
36  *     if (!gst_alloc_trace_available ()) {
37  *       g_warning ("Trace not available (recompile with trace enabled).");
38  *     }
39  *     gst_alloc_trace_print_live ();
40  *     // do something here
41  *     gst_alloc_trace_print_live ();
42  *   </programlisting>
43  * </example>
44  *
45  * Last reviewed on 2005-11-21 (0.9.5)
46  */
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51 #include <stdio.h>
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #include <string.h>
58 #include <errno.h>
59
60 #if defined (_MSC_VER) && _MSC_VER >= 1400
61 # include <io.h>
62 #endif
63
64 #include "gst_private.h"
65 #include "gstinfo.h"
66
67 #include "gsttrace.h"
68
69 GMutex _gst_trace_mutex;
70
71 static inline void
72 read_tsc (gint64 * dst)
73 {
74 #if defined(HAVE_RDTSC) && defined(__GNUC__)
75   guint64 tsc;
76   __asm__ __volatile__ ("rdtsc":"=A" (tsc));
77
78   *dst = tsc;
79 #else
80   *dst = 0;
81 #endif
82 }
83
84 /**
85  * gst_trace_read_tsc:
86  * @dst: (out) pointer to hold the result.
87  *
88  * Read a platform independent timer value that can be used in
89  * benchmarks.
90  */
91 void
92 gst_trace_read_tsc (gint64 * dst)
93 {
94   read_tsc (dst);
95 }
96
97 static GstTrace *_gst_trace_default = NULL;
98 gint _gst_trace_on = 1;
99
100 /**
101  * gst_trace_new:
102  * @filename: a filename
103  * @size: the max size of the file
104  *
105  * Create a ringbuffer of @size in the file with @filename to
106  * store trace results in.
107  *
108  * Free-function: gst_trace_destroy
109  *
110  * Returns: (transfer full): a new #GstTrace.
111  */
112 GstTrace *
113 gst_trace_new (const gchar * filename, gint size)
114 {
115   GstTrace *trace = g_slice_new (GstTrace);
116
117   g_return_val_if_fail (trace != NULL, NULL);
118   trace->filename = g_strdup (filename);
119   GST_DEBUG ("opening '%s'", trace->filename);
120 #ifndef S_IWUSR
121 #define S_IWUSR S_IWRITE
122 #endif
123 #ifndef S_IRUSR
124 #define S_IRUSR S_IREAD
125 #endif
126   trace->fd =
127       open (trace->filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
128   perror ("opening trace file");
129   g_return_val_if_fail (trace->fd > 0, NULL);
130   trace->buf = g_malloc (size * sizeof (GstTraceEntry));
131   g_return_val_if_fail (trace->buf != NULL, NULL);
132   trace->bufsize = size;
133   trace->bufoffset = 0;
134
135   return trace;
136 }
137
138 /**
139  * gst_trace_destroy:
140  * @trace: (in) (transfer full): the #GstTrace to destroy
141  *
142  * Flush an close the previously allocated @trace.
143  */
144 void
145 gst_trace_destroy (GstTrace * trace)
146 {
147   g_return_if_fail (trace != NULL);
148   g_return_if_fail (trace->buf != NULL);
149
150   if (gst_trace_get_remaining (trace) > 0)
151     gst_trace_flush (trace);
152   close (trace->fd);
153   g_free (trace->buf);
154   g_slice_free (GstTrace, trace);
155 }
156
157 /**
158  * gst_trace_flush:
159  * @trace: the #GstTrace to flush.
160  *
161  * Flush any pending trace entries in @trace to the trace file.
162  * @trace can be NULL in which case the default #GstTrace will be
163  * flushed.
164  */
165 void
166 gst_trace_flush (GstTrace * trace)
167 {
168   int res, buf_len;
169
170   if (!trace) {
171     trace = _gst_trace_default;
172     if (!trace)
173       return;
174   }
175
176   buf_len = trace->bufoffset * sizeof (GstTraceEntry);
177   res = write (trace->fd, trace->buf, buf_len);
178   if (res < 0) {
179     g_warning ("Failed to write trace: %s", g_strerror (errno));
180     return;
181   } else if (res < buf_len) {
182     g_warning ("Failed to write trace: only wrote %d/%d bytes", res, buf_len);
183     return;
184   }
185   trace->bufoffset = 0;
186 }
187
188 /**
189  * gst_trace_text_flush:
190  * @trace: the #GstTrace to flush.
191  *
192  * Flush any pending trace entries in @trace to the trace file,
193  * formatted as a text line with timestamp and sequence numbers.
194  * @trace can be NULL in which case the default #GstTrace will be
195  * flushed.
196  */
197 void
198 gst_trace_text_flush (GstTrace * trace)
199 {
200   int i;
201
202 #define STRSIZE (20 + 1 + 10 + 1 + 10 + 1 + 112 + 1 + 1)
203   char str[STRSIZE];
204
205   if (!trace) {
206     trace = _gst_trace_default;
207     if (!trace)
208       return;
209   }
210
211   for (i = 0; i < trace->bufoffset; i++) {
212     g_snprintf (str, STRSIZE, "%20" G_GINT64_FORMAT " %10d %10d %s\n",
213         trace->buf[i].timestamp,
214         trace->buf[i].sequence, trace->buf[i].data, trace->buf[i].message);
215     if (write (trace->fd, str, strlen (str)) < 0) {
216       g_warning ("Failed to write trace %d: %s", i, g_strerror (errno));
217       return;
218     }
219   }
220   trace->bufoffset = 0;
221 #undef STRSIZE
222 }
223
224 /**
225  * gst_trace_set_default:
226  * @trace: the #GstTrace to set as the default.
227  *
228  * Set the default #GstTrace to @trace.
229  */
230 void
231 gst_trace_set_default (GstTrace * trace)
232 {
233   g_return_if_fail (trace != NULL);
234   _gst_trace_default = trace;
235 }
236
237 void
238 _gst_trace_add_entry (GstTrace * trace, guint32 seq, guint32 data, gchar * msg)
239 {
240   GstTraceEntry *entry;
241
242   if (!trace) {
243     trace = _gst_trace_default;
244     if (!trace)
245       return;
246   }
247
248   entry = trace->buf + trace->bufoffset;
249   read_tsc (&(entry->timestamp));
250   entry->sequence = seq;
251   entry->data = data;
252   strncpy (entry->message, msg, 112);
253   entry->message[111] = '\0';
254   trace->bufoffset++;
255
256   gst_trace_flush (trace);
257 }
258
259
260 /* global flags */
261 static GstAllocTraceFlags _gst_trace_flags = GST_ALLOC_TRACE_NONE;
262
263 /* list of registered tracers */
264 static GList *_gst_alloc_tracers = NULL;
265
266 /**
267  * gst_alloc_trace_available:
268  *
269  * Check if alloc tracing was compiled into the core
270  *
271  * Returns: TRUE if the core was compiled with alloc
272  * tracing enabled.
273  */
274 gboolean
275 gst_alloc_trace_available (void)
276 {
277 #ifdef GST_DISABLE_ALLOC_TRACE
278   return FALSE;
279 #else
280   return TRUE;
281 #endif
282 }
283
284 /**
285  * _gst_alloc_trace_register:
286  * @name: the name of the new alloc trace object.
287  *
288  * Register an get a handle to a GstAllocTrace object that
289  * can be used to trace memory allocations.
290  *
291  * Returns: A handle to a GstAllocTrace.
292  */
293 GstAllocTrace *
294 _gst_alloc_trace_register (const gchar * name)
295 {
296   GstAllocTrace *trace;
297
298   g_return_val_if_fail (name, NULL);
299
300   trace = g_slice_new (GstAllocTrace);
301   trace->name = g_strdup (name);
302   trace->live = 0;
303   trace->mem_live = NULL;
304   trace->flags = _gst_trace_flags;
305
306   _gst_alloc_tracers = g_list_prepend (_gst_alloc_tracers, trace);
307
308   return trace;
309 }
310
311 /**
312  * gst_alloc_trace_list:
313  *
314  * Get a list of all registered alloc trace objects.
315  *
316  * Returns: a GList of GstAllocTrace objects.
317  */
318 const GList *
319 gst_alloc_trace_list (void)
320 {
321   return _gst_alloc_tracers;
322 }
323
324 /**
325  * gst_alloc_trace_live_all:
326  *
327  * Get the total number of live registered alloc trace objects.
328  *
329  * Returns: the total number of live registered alloc trace objects.
330  */
331 int
332 gst_alloc_trace_live_all (void)
333 {
334   GList *walk = _gst_alloc_tracers;
335   int num = 0;
336
337   while (walk) {
338     GstAllocTrace *trace = (GstAllocTrace *) walk->data;
339
340     num += trace->live;
341
342     walk = g_list_next (walk);
343   }
344
345   return num;
346 }
347
348 static gint
349 compare_func (GstAllocTrace * a, GstAllocTrace * b)
350 {
351   return strcmp (a->name, b->name);
352 }
353
354 static GList *
355 gst_alloc_trace_list_sorted (void)
356 {
357   GList *ret;
358
359   ret = g_list_sort (g_list_copy (_gst_alloc_tracers),
360       (GCompareFunc) compare_func);
361
362   return ret;
363 }
364
365 /**
366  * gst_alloc_trace_print_all:
367  *
368  * Print the status of all registered alloc trace objects.
369  */
370 void
371 gst_alloc_trace_print_all (void)
372 {
373   GList *orig, *walk;
374
375   orig = walk = gst_alloc_trace_list_sorted ();
376
377   while (walk) {
378     GstAllocTrace *trace = (GstAllocTrace *) walk->data;
379
380     gst_alloc_trace_print (trace);
381
382     walk = g_list_next (walk);
383   }
384
385   g_list_free (orig);
386 }
387
388 /**
389  * gst_alloc_trace_print_live:
390  *
391  * Print the status of all registered alloc trace objects, ignoring those
392  * without live objects.
393  */
394 void
395 gst_alloc_trace_print_live (void)
396 {
397   GList *orig, *walk;
398
399   orig = walk = gst_alloc_trace_list_sorted ();
400
401   while (walk) {
402     GstAllocTrace *trace = (GstAllocTrace *) walk->data;
403
404     if (trace->live)
405       gst_alloc_trace_print (trace);
406
407     walk = g_list_next (walk);
408   }
409
410   g_list_free (orig);
411 }
412
413 /**
414  * gst_alloc_trace_set_flags_all:
415  * @flags: the options to enable
416  *
417  * Enable the specified options on all registered alloc trace
418  * objects.
419  */
420 void
421 gst_alloc_trace_set_flags_all (GstAllocTraceFlags flags)
422 {
423   GList *walk = _gst_alloc_tracers;
424
425   while (walk) {
426     GstAllocTrace *trace = (GstAllocTrace *) walk->data;
427
428     GST_DEBUG ("setting flags %d on %p", (gint) flags, trace);
429     gst_alloc_trace_set_flags (trace, flags);
430
431     walk = g_list_next (walk);
432   }
433   _gst_trace_flags = flags;
434 }
435
436 /**
437  * gst_alloc_trace_get:
438  * @name: the name of the alloc trace object
439  *
440  * Get the named alloc trace object.
441  *
442  * Returns: a GstAllocTrace with the given name or NULL when
443  * no alloc tracer was registered with that name.
444  */
445 GstAllocTrace *
446 gst_alloc_trace_get (const gchar * name)
447 {
448   GList *walk = _gst_alloc_tracers;
449
450   g_return_val_if_fail (name, NULL);
451
452   while (walk) {
453     GstAllocTrace *trace = (GstAllocTrace *) walk->data;
454
455     if (!strcmp (trace->name, name))
456       return trace;
457
458     walk = g_list_next (walk);
459   }
460   return NULL;
461 }
462
463 /**
464  * gst_alloc_trace_print:
465  * @trace: the GstAllocTrace to print
466  *
467  * Print the status of the given GstAllocTrace.
468  */
469 void
470 gst_alloc_trace_print (const GstAllocTrace * trace)
471 {
472   GSList *mem_live;
473
474   g_return_if_fail (trace != NULL);
475
476   if (trace->flags & GST_ALLOC_TRACE_LIVE) {
477     g_print ("%-22.22s : %d\n", trace->name, trace->live);
478   } else {
479     g_print ("%-22.22s : (no live count)\n", trace->name);
480   }
481
482   if (trace->flags & GST_ALLOC_TRACE_MEM_LIVE) {
483     mem_live = trace->mem_live;
484
485     while (mem_live) {
486       gpointer data = mem_live->data;
487
488       if (G_IS_OBJECT (data)) {
489         g_print ("%-22.22s : %p\n", g_type_name (G_OBJECT_TYPE (data)), data);
490       } else {
491         g_print ("%-22.22s : %p\n", "", data);
492       }
493       mem_live = mem_live->next;
494     }
495   }
496 }
497
498 /**
499  * gst_alloc_trace_set_flags:
500  * @trace: the GstAllocTrace
501  * @flags: flags to set
502  *
503  * Enable the given features on the given GstAllocTrace object.
504  */
505 void
506 gst_alloc_trace_set_flags (GstAllocTrace * trace, GstAllocTraceFlags flags)
507 {
508   g_return_if_fail (trace != NULL);
509
510   trace->flags = flags;
511 }