90b98cb041e68695d0bafb0834295abcd2f799ed
[platform/upstream/gstreamer.git] / gst / debug / efence.c
1 /*
2  * GStreamer
3  * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
4  * Copyright (C) 2002 David A. Schleef <ds@schleef.org>
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <gst/gst.h>
26
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30
31 #include "efence.h"
32
33 #ifndef MAP_ANONYMOUS
34 #ifdef MAP_ANON
35 #define MAP_ANONYMOUS MAP_ANON
36 #else
37 /* assume we don't need it */
38 #define MAP_ANONYMOUS 0
39 #endif
40 #endif
41
42 GST_DEBUG_CATEGORY_STATIC (gst_efence_debug);
43 #define GST_CAT_DEFAULT  gst_efence_debug
44
45 static const GstElementDetails plugin_details =
46 GST_ELEMENT_DETAILS ("Electric Fence",
47     "Testing",
48     "This element converts a stream of normal GStreamer buffers into a "
49     "stream of buffers that are allocated in such a way that out-of-bounds "
50     "access to data in the buffer is more likely to cause segmentation "
51     "faults.  This allocation method is very similar to the debugging tool "
52     "\"Electric Fence\".",
53     "David A. Schleef <ds@schleef.org>");
54
55 /* Filter signals and args */
56 enum
57 {
58   /* FILL ME */
59   LAST_SIGNAL
60 };
61
62 enum
63 {
64   ARG_0,
65   ARG_FENCE_TOP
66 };
67
68 static GstStaticPadTemplate gst_efence_sink_factory =
69 GST_STATIC_PAD_TEMPLATE ("sink",
70     GST_PAD_SINK,
71     GST_PAD_ALWAYS,
72     GST_STATIC_CAPS_ANY);
73
74 static GstStaticPadTemplate gst_efence_src_factory =
75 GST_STATIC_PAD_TEMPLATE ("src",
76     GST_PAD_SRC,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS_ANY);
79
80 static void gst_efence_base_init (gpointer g_class);
81 static void gst_efence_class_init (GstEFenceClass * klass);
82 static void gst_efence_init (GstEFence * filter);
83
84 static void gst_efence_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86 static void gst_efence_get_property (GObject * object, guint prop_id,
87     GValue * value, GParamSpec * pspec);
88
89 static GstFlowReturn gst_efence_chain (GstPad * pad, GstBuffer * buf);
90 static GstFlowReturn gst_efence_getrange (GstPad * pad, guint64 offset,
91     guint length, GstBuffer ** buffer);
92 static gboolean gst_efence_checkgetrange (GstPad * pad);
93 static gboolean gst_efence_activate_src_pull (GstPad * pad, gboolean active);
94
95 static GstElementClass *parent_class = NULL;
96
97 typedef struct _GstFencedBuffer GstFencedBuffer;
98 struct _GstFencedBuffer
99 {
100   GstBuffer buffer;
101   void *region;
102   unsigned int length;
103 };
104
105 GType gst_fenced_buffer_get_type (void);
106 static void gst_fenced_buffer_finalize (GstFencedBuffer * buf);
107 static GstFencedBuffer *gst_fenced_buffer_copy (const GstBuffer * buffer);
108 static void *gst_fenced_buffer_alloc (GstBuffer * buffer, unsigned int length,
109     gboolean fence_top);
110 static GstFlowReturn gst_efence_buffer_alloc (GstPad * pad, guint64 offset,
111     guint size, GstCaps * caps, GstBuffer ** buf);
112
113 #define GST_TYPE_FENCED_BUFFER (gst_fenced_buffer_get_type())
114
115 #define GST_IS_FENCED_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FENCED_BUFFER))
116 #define GST_FENCED_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FENCED_BUFFER, GstFencedBuffer))
117
118 GType
119 gst_gst_efence_get_type (void)
120 {
121   static GType plugin_type = 0;
122
123   if (!plugin_type) {
124     static const GTypeInfo plugin_info = {
125       sizeof (GstEFenceClass),
126       gst_efence_base_init,
127       NULL,
128       (GClassInitFunc) gst_efence_class_init,
129       NULL,
130       NULL,
131       sizeof (GstEFence),
132       0,
133       (GInstanceInitFunc) gst_efence_init,
134     };
135
136     plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
137         "GstEFence", &plugin_info, 0);
138   }
139   return plugin_type;
140 }
141
142 static void
143 gst_efence_base_init (gpointer g_class)
144 {
145   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
146
147   gst_element_class_add_pad_template (element_class,
148       gst_static_pad_template_get (&gst_efence_sink_factory));
149   gst_element_class_add_pad_template (element_class,
150       gst_static_pad_template_get (&gst_efence_src_factory));
151   gst_element_class_set_details (element_class, &plugin_details);
152 }
153
154 /* initialize the plugin's class */
155 static void
156 gst_efence_class_init (GstEFenceClass * klass)
157 {
158   GObjectClass *gobject_class;
159   GstElementClass *gstelement_class;
160
161   gobject_class = (GObjectClass *) klass;
162   gstelement_class = (GstElementClass *) klass;
163
164   parent_class = g_type_class_peek_parent (klass);
165
166   gobject_class->set_property = gst_efence_set_property;
167   gobject_class->get_property = gst_efence_get_property;
168
169   g_object_class_install_property (gobject_class, ARG_FENCE_TOP,
170       g_param_spec_boolean ("fence_top", "Fence Top",
171           "Align buffers with top of fenced region", TRUE, G_PARAM_READWRITE));
172 }
173
174 /* initialize the new element
175  * instantiate pads and add them to element
176  * set functions
177  * initialize structure
178  */
179 static void
180 gst_efence_init (GstEFence * filter)
181 {
182   filter->sinkpad =
183       gst_pad_new_from_static_template (&gst_efence_sink_factory, "sink");
184   gst_pad_set_getcaps_function (filter->sinkpad,
185       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
186   gst_pad_set_setcaps_function (filter->sinkpad,
187       GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
188   gst_pad_set_chain_function (filter->sinkpad,
189       GST_DEBUG_FUNCPTR (gst_efence_chain));
190   gst_pad_set_bufferalloc_function (filter->sinkpad,
191       GST_DEBUG_FUNCPTR (gst_efence_buffer_alloc));
192   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
193
194   filter->srcpad =
195       gst_pad_new_from_static_template (&gst_efence_src_factory, "src");
196   gst_pad_set_getcaps_function (filter->srcpad,
197       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
198   gst_pad_set_setcaps_function (filter->srcpad,
199       GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
200   gst_pad_set_checkgetrange_function (filter->srcpad,
201       GST_DEBUG_FUNCPTR (gst_efence_checkgetrange));
202   gst_pad_set_getrange_function (filter->srcpad,
203       GST_DEBUG_FUNCPTR (gst_efence_getrange));
204   gst_pad_set_activatepull_function (filter->srcpad,
205       GST_DEBUG_FUNCPTR (gst_efence_activate_src_pull));
206
207   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
208
209   filter->fence_top = TRUE;
210 }
211
212 /* chain function
213  * this function does the actual processing
214  */
215
216 static GstFlowReturn
217 gst_efence_chain (GstPad * pad, GstBuffer * buffer)
218 {
219   GstEFence *efence;
220   GstBuffer *copy;
221
222   efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
223   g_return_val_if_fail (GST_IS_EFENCE (efence), GST_FLOW_ERROR);
224
225   if (GST_IS_FENCED_BUFFER (buffer)) {
226     GST_DEBUG_OBJECT (efence, "Passing on existing fenced buffer with caps %"
227         GST_PTR_FORMAT, GST_BUFFER_CAPS (buffer));
228     return gst_pad_push (efence->srcpad, buffer);
229   }
230
231   copy = (GstBuffer *) gst_fenced_buffer_copy (buffer);
232
233   GST_DEBUG_OBJECT (efence, "Pushing newly fenced buffer with caps %"
234       GST_PTR_FORMAT ", data=%p, size=%u", GST_BUFFER_CAPS (copy),
235       GST_BUFFER_DATA (copy), GST_BUFFER_SIZE (copy));
236
237   gst_buffer_unref (buffer);
238
239   return gst_pad_push (efence->srcpad, copy);
240 }
241
242 static GstFlowReturn
243 gst_efence_getrange (GstPad * pad, guint64 offset,
244     guint length, GstBuffer ** buffer)
245 {
246   GstEFence *efence;
247   GstFlowReturn ret;
248   GstBuffer *ownbuf;
249   GstPad *peer;
250
251   efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
252
253   peer = gst_pad_get_peer (efence->sinkpad);
254   if (!peer)
255     return GST_FLOW_NOT_LINKED;
256
257   if ((ret = gst_pad_get_range (peer, offset, length, buffer)) != GST_FLOW_OK)
258     goto beach;
259
260   ownbuf = (GstBuffer *) gst_fenced_buffer_copy (*buffer);
261   gst_buffer_unref ((GstBuffer *) * buffer);
262   *buffer = ownbuf;
263
264 beach:
265   gst_object_unref (peer);
266   return ret;
267 }
268
269 static gboolean
270 gst_efence_checkgetrange (GstPad * pad)
271 {
272   GstEFence *efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
273
274   return gst_pad_check_pull_range (efence->sinkpad);
275 }
276
277 static gboolean
278 gst_efence_activate_src_pull (GstPad * pad, gboolean active)
279 {
280   GstEFence *efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
281
282   return gst_pad_activate_pull (efence->sinkpad, active);
283 }
284
285 static GstFlowReturn
286 gst_efence_buffer_alloc (GstPad * pad, guint64 offset,
287     guint size, GstCaps * caps, GstBuffer ** buf)
288 {
289   GstBuffer *buffer;
290   GstEFence *efence;
291
292   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
293   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
294
295   efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
296
297   buffer = (GstBuffer *) gst_mini_object_new (GST_TYPE_FENCED_BUFFER);
298
299   GST_BUFFER_DATA (buffer) = gst_fenced_buffer_alloc (buffer, size,
300       efence->fence_top);
301   GST_BUFFER_SIZE (buffer) = size;
302   GST_BUFFER_OFFSET (buffer) = offset;
303
304   if (caps)
305     gst_buffer_set_caps (buffer, caps);
306
307   *buf = buffer;
308
309   GST_DEBUG_OBJECT (efence, "Allocated buffer of size %u, caps: %"
310       GST_PTR_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_CAPS (buffer));
311
312   return GST_FLOW_OK;
313 }
314
315 static void
316 gst_efence_set_property (GObject * object, guint prop_id,
317     const GValue * value, GParamSpec * pspec)
318 {
319   GstEFence *filter;
320
321   g_return_if_fail (GST_IS_EFENCE (object));
322   filter = GST_EFENCE (object);
323
324   switch (prop_id) {
325     case ARG_FENCE_TOP:
326       filter->fence_top = g_value_get_boolean (value);
327       break;
328     default:
329       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
330       break;
331   }
332 }
333
334 static void
335 gst_efence_get_property (GObject * object, guint prop_id,
336     GValue * value, GParamSpec * pspec)
337 {
338   GstEFence *filter;
339
340   g_return_if_fail (GST_IS_EFENCE (object));
341   filter = GST_EFENCE (object);
342
343   switch (prop_id) {
344     case ARG_FENCE_TOP:
345       g_value_set_boolean (value, filter->fence_top);
346       break;
347     default:
348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349       break;
350   }
351 }
352
353 /* entry point to initialize the plug-in
354  * initialize the plug-in itself
355  * register the element factories and pad templates
356  * register the features
357  */
358 static gboolean
359 plugin_init (GstPlugin * plugin)
360 {
361   if (!gst_element_register (plugin, "efence", GST_RANK_NONE, GST_TYPE_EFENCE))
362     return FALSE;
363
364   GST_DEBUG_CATEGORY_INIT (gst_efence_debug, "efence", 0,
365       "Debug output from the efence element");
366
367   /* plugin initialisation succeeded */
368   return TRUE;
369 }
370
371 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
372     GST_VERSION_MINOR,
373     "efence",
374     "This element converts a stream of normal GStreamer buffers into a "
375     "stream of buffers that are allocated in such a way that out-of-bounds "
376     "access to data in the buffer is more likely to cause segmentation "
377     "faults.  This allocation method is very similar to the debugging tool "
378     "\"Electric Fence\".",
379     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
380
381
382 static GstBufferClass *fenced_buffer_parent_class = NULL;
383
384 static void
385 gst_fenced_buffer_finalize (GstFencedBuffer * buffer)
386 {
387   GstFencedBuffer *fenced_buffer;
388
389   GST_DEBUG ("free buffer=%p", buffer);
390
391   fenced_buffer = GST_FENCED_BUFFER (buffer);
392
393   /* free our data */
394   if (GST_BUFFER_DATA (buffer)) {
395     GST_DEBUG ("free region %p %d", fenced_buffer->region,
396         fenced_buffer->length);
397     munmap (fenced_buffer->region, fenced_buffer->length);
398   }
399
400   GST_MINI_OBJECT_CLASS (fenced_buffer_parent_class)->
401       finalize (GST_MINI_OBJECT (buffer));
402 }
403
404 static GstFencedBuffer *
405 gst_fenced_buffer_copy (const GstBuffer * buffer)
406 {
407   GstBuffer *copy;
408   void *ptr;
409   guint mask;
410
411   g_return_val_if_fail (buffer != NULL, NULL);
412
413   /* create a fresh new buffer */
414   copy = (GstBuffer *) gst_mini_object_new (GST_TYPE_FENCED_BUFFER);
415
416   /* we simply copy everything from our parent */
417   ptr = gst_fenced_buffer_alloc (GST_BUFFER (copy),
418       GST_BUFFER_SIZE (buffer), TRUE);
419   memcpy (ptr, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
420
421   /* copy relevant flags */
422   mask = GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_IN_CAPS |
423       GST_BUFFER_FLAG_DELTA_UNIT;
424   GST_MINI_OBJECT (copy)->flags |= GST_MINI_OBJECT (buffer)->flags & mask;
425
426   GST_BUFFER_DATA (copy) = ptr;
427   GST_BUFFER_SIZE (copy) = GST_BUFFER_SIZE (buffer);
428   GST_BUFFER_TIMESTAMP (copy) = GST_BUFFER_TIMESTAMP (buffer);
429   GST_BUFFER_DURATION (copy) = GST_BUFFER_DURATION (buffer);
430   GST_BUFFER_OFFSET (copy) = GST_BUFFER_OFFSET (buffer);
431   GST_BUFFER_OFFSET_END (copy) = GST_BUFFER_OFFSET_END (buffer);
432
433   if (GST_BUFFER_CAPS (buffer))
434     GST_BUFFER_CAPS (copy) = gst_caps_ref (GST_BUFFER_CAPS (buffer));
435   else
436     GST_BUFFER_CAPS (copy) = NULL;
437
438   GST_DEBUG ("Copied buffer %p with ts %" GST_TIME_FORMAT
439       ", caps: %" GST_PTR_FORMAT, buffer,
440       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (copy)), GST_BUFFER_CAPS (copy));
441
442   return GST_FENCED_BUFFER (copy);
443 }
444
445 void *
446 gst_fenced_buffer_alloc (GstBuffer * buffer, unsigned int length,
447     gboolean fence_top)
448 {
449   int alloc_size;
450   void *region;
451   GstFencedBuffer *fenced_buffer = (GstFencedBuffer *) buffer;
452   int page_size;
453
454   GST_DEBUG ("buffer=%p length=%d fence_top=%d", buffer, length, fence_top);
455
456   if (length == 0)
457     return NULL;
458
459 #ifdef _SC_PAGESIZE
460   page_size = sysconf (_SC_PAGESIZE);
461 #else
462   page_size = getpagesize ();
463 #endif
464
465   /* Allocate a complete page, and one on either side */
466   alloc_size = ((length - 1) & ~(page_size - 1)) + page_size;
467   alloc_size += 2 * page_size;
468
469   region = mmap (NULL, alloc_size, PROT_READ | PROT_WRITE,
470       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
471   if (region == MAP_FAILED) {
472     g_warning ("mmap failed");
473     return NULL;
474   }
475 #if 0
476   munmap (region, page_size);
477   munmap (region + alloc_size - page_size, page_size);
478
479   fenced_buffer->region = region + page_size;
480   fenced_buffer->length = alloc_size - page_size;
481 #else
482   mprotect (region, page_size, PROT_NONE);
483   mprotect ((char *) region + alloc_size - page_size, page_size, PROT_NONE);
484
485   fenced_buffer->region = region;
486   fenced_buffer->length = alloc_size;
487 #endif
488
489   GST_DEBUG ("new region %p %d", fenced_buffer->region, fenced_buffer->length);
490
491   if (fence_top) {
492     int offset;
493
494     /* Align to top of region, but force alignment to 4 bytes */
495     offset = alloc_size - page_size - length;
496     offset &= ~0x3;
497     return (void *) ((char *) region + offset);
498   } else {
499     return (void *) ((char *) region + page_size);
500   }
501 }
502
503 static void
504 gst_fenced_buffer_class_init (gpointer g_class, gpointer class_data)
505 {
506   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
507
508   fenced_buffer_parent_class = g_type_class_peek_parent (g_class);
509
510   mini_object_class->finalize =
511       (GstMiniObjectFinalizeFunction) gst_fenced_buffer_finalize;
512   mini_object_class->copy = (GstMiniObjectCopyFunction) gst_fenced_buffer_copy;
513 }
514
515 static void
516 gst_fenced_buffer_init (GTypeInstance * instance, gpointer g_class)
517 {
518 }
519
520 GType
521 gst_fenced_buffer_get_type (void)
522 {
523   static GType fenced_buf_type = 0;
524
525   if (G_UNLIKELY (!fenced_buf_type)) {
526     static const GTypeInfo fenced_buf_info = {
527       sizeof (GstBufferClass),
528       NULL,
529       NULL,
530       (GClassInitFunc) gst_fenced_buffer_class_init,
531       NULL,
532       NULL,
533       sizeof (GstFencedBuffer),
534       0,
535       (GInstanceInitFunc) gst_fenced_buffer_init,
536     };
537
538     fenced_buf_type = g_type_register_static (GST_TYPE_BUFFER,
539         "GstFencedBuffer", &fenced_buf_info, 0);
540   }
541   return fenced_buf_type;
542 }