571ab178cef928ec0a423bb1ff03360e47dcbc2d
[platform/upstream/gst-plugins-good.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 static GstElementDetails plugin_details = {
43   "Electric Fence",
44   "Testing/EFence",
45   "This element converts a stream of normal GStreamer buffers into a "
46 "stream of buffers that are allocated in such a way that out-of-bounds "
47 "access to data in the buffer is more likely to cause segmentation "
48 "faults.  This allocation method is very similar to the debugging tool "
49 "\"Electric Fence\".",
50   "David A. Schleef <ds@schleef.org>",
51 };
52
53 /* Filter signals and args */
54 enum {
55   /* FILL ME */
56   LAST_SIGNAL
57 };
58
59 enum {
60   ARG_0,
61   ARG_FENCE_TOP
62 };
63
64 static GstStaticPadTemplate gst_efence_sink_factory =
65 GST_STATIC_PAD_TEMPLATE (
66   "sink",
67   GST_PAD_SINK,
68   GST_PAD_ALWAYS,
69   GST_STATIC_CAPS_ANY
70 );
71
72 static GstStaticPadTemplate gst_efence_src_factory =
73 GST_STATIC_PAD_TEMPLATE (
74   "src",
75   GST_PAD_SRC,
76   GST_PAD_ALWAYS,
77   GST_STATIC_CAPS_ANY
78 );
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,
86                                                  GParamSpec *pspec);
87 static void     gst_efence_get_property(GObject *object, guint prop_id,
88                                                  GValue *value,
89                                                  GParamSpec *pspec);
90
91 static void     gst_efence_chain        (GstPad *pad, GstData *_data);
92
93 static GstElementClass *parent_class = NULL;
94
95 typedef struct _GstFencedBuffer GstFencedBuffer;
96 struct _GstFencedBuffer {
97   GstBuffer buffer;
98   void *region;
99   unsigned int length;
100 };
101
102 void gst_fenced_buffer_default_free (GstData *data);
103 GstData * gst_fenced_buffer_default_copy (const GstData *data);
104 void *gst_fenced_buffer_alloc(GstBuffer *buffer, unsigned int length,
105     gboolean fence_top);
106 static GstBuffer * gst_efence_buffer_alloc (GstPad *pad, guint64 offset, guint size);
107
108 GstBuffer *gst_fenced_buffer_new(void);
109
110 GType
111 gst_gst_efence_get_type (void)
112 {
113   static GType plugin_type = 0;
114
115   if (!plugin_type)
116   {
117     static const GTypeInfo plugin_info =
118     {
119       sizeof (GstEFenceClass),
120       gst_efence_base_init,
121       NULL,
122       (GClassInitFunc) gst_efence_class_init,
123       NULL,
124       NULL,
125       sizeof (GstEFence),
126       0,
127       (GInstanceInitFunc) gst_efence_init,
128     };
129     plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
130                                           "GstEFence",
131                                           &plugin_info, 0);
132   }
133   return plugin_type;
134 }
135
136 static void
137 gst_efence_base_init (gpointer g_class)
138 {
139   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
140
141   gst_element_class_add_pad_template (element_class,
142       gst_static_pad_template_get(&gst_efence_sink_factory));
143   gst_element_class_add_pad_template (element_class,
144       gst_static_pad_template_get(&gst_efence_src_factory));
145   gst_element_class_set_details (element_class, &plugin_details);
146 }
147
148 /* initialize the plugin's class */
149 static void
150 gst_efence_class_init (GstEFenceClass *klass)
151 {
152   GObjectClass *gobject_class;
153   GstElementClass *gstelement_class;
154
155   gobject_class = (GObjectClass*) klass;
156   gstelement_class = (GstElementClass*) klass;
157
158   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
159
160   g_object_class_install_property (gobject_class, ARG_FENCE_TOP,
161     g_param_spec_boolean ("fence_top", "Fence Top", "Align buffers with top of fenced region",
162                           TRUE, G_PARAM_READWRITE));
163
164   gobject_class->set_property = gst_efence_set_property;
165   gobject_class->get_property = gst_efence_get_property;
166 }
167
168 /* initialize the new element
169  * instantiate pads and add them to element
170  * set functions
171  * initialize structure
172  */
173 static void
174 gst_efence_init (GstEFence *filter)
175 {
176   filter->sinkpad = gst_pad_new_from_template (
177       gst_static_pad_template_get(&gst_efence_sink_factory), "sink");
178   gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
179   gst_pad_set_link_function (filter->sinkpad, gst_pad_proxy_pad_link);
180   filter->srcpad = gst_pad_new_from_template (
181       gst_static_pad_template_get(&gst_efence_src_factory), "src");
182   gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps);
183   gst_pad_set_link_function (filter->srcpad, gst_pad_proxy_pad_link);
184
185   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
186   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
187   gst_pad_set_chain_function (filter->sinkpad, gst_efence_chain);
188   gst_pad_set_bufferalloc_function (filter->sinkpad, gst_efence_buffer_alloc);
189
190   filter->fence_top = TRUE;
191 }
192
193 /* chain function
194  * this function does the actual processing
195  */
196
197 static void
198 gst_efence_chain (GstPad *pad, GstData *_data)
199 {
200   GstBuffer *buffer = GST_BUFFER (_data);
201   GstEFence *efence;
202   GstBuffer *copy;
203   void *ptr;
204
205   GST_DEBUG ("gst_efence_chain");
206
207   g_return_if_fail (GST_IS_PAD (pad));
208   g_return_if_fail (buffer != NULL);
209
210   efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
211   g_return_if_fail (GST_IS_EFENCE (efence));
212
213   if (GST_DATA_FREE_FUNC (_data) == gst_fenced_buffer_default_free) {
214     gst_pad_push (efence->srcpad, _data);
215     return;
216   }
217
218   copy = gst_fenced_buffer_new();
219
220   ptr = gst_fenced_buffer_alloc(copy, GST_BUFFER_SIZE(buffer),
221       efence->fence_top);
222   memcpy(ptr, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
223
224   GST_BUFFER_DATA (copy)         = ptr;
225   GST_BUFFER_SIZE (copy)         = GST_BUFFER_SIZE (buffer);
226   GST_BUFFER_MAXSIZE (copy)      = GST_BUFFER_SIZE (buffer);
227   GST_BUFFER_TIMESTAMP (copy)    = GST_BUFFER_TIMESTAMP (buffer);
228   GST_BUFFER_DURATION (copy)     = GST_BUFFER_DURATION (buffer);
229   GST_BUFFER_OFFSET (copy)       = GST_BUFFER_OFFSET (buffer);
230   GST_BUFFER_FREE_DATA_FUNC (copy) = NULL;
231   GST_BUFFER_PRIVATE (copy)      = NULL;
232
233   gst_buffer_unref(buffer);
234   gst_pad_push (efence->srcpad, GST_DATA (copy));
235 }
236
237 static GstBuffer *
238 gst_efence_buffer_alloc (GstPad *pad, guint64 offset, guint size)
239 {
240   GstBuffer *buffer;
241   GstEFence *efence;
242
243   efence = GST_EFENCE (GST_OBJECT_PARENT (pad));
244
245   buffer = gst_fenced_buffer_new ();
246
247   GST_BUFFER_DATA (buffer) = gst_fenced_buffer_alloc(buffer, size,
248       efence->fence_top);
249   GST_BUFFER_SIZE (buffer) = size;
250
251   return buffer;
252 }
253
254 static void
255 gst_efence_set_property (GObject *object, guint prop_id,
256                                   const GValue *value, GParamSpec *pspec)
257 {
258   GstEFence *filter;
259
260   g_return_if_fail (GST_IS_EFENCE (object));
261   filter = GST_EFENCE (object);
262
263   switch (prop_id)
264   {
265   case ARG_FENCE_TOP:
266     filter->fence_top = g_value_get_boolean (value);
267     break;
268   default:
269     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
270     break;
271   }
272 }
273
274 static void
275 gst_efence_get_property (GObject *object, guint prop_id,
276                                   GValue *value, GParamSpec *pspec)
277 {
278   GstEFence *filter;
279
280   g_return_if_fail (GST_IS_EFENCE (object));
281   filter = GST_EFENCE (object);
282
283   switch (prop_id) {
284   case ARG_FENCE_TOP:
285     g_value_set_boolean (value, filter->fence_top);
286     break;
287   default:
288     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
289     break;
290   }
291 }
292
293 /* entry point to initialize the plug-in
294  * initialize the plug-in itself
295  * register the element factories and pad templates
296  * register the features
297  */
298 static gboolean
299 plugin_init (GstPlugin *plugin)
300 {
301   if (!gst_element_register (plugin, "efence", GST_RANK_NONE, GST_TYPE_EFENCE))
302     return FALSE;
303   
304   /* plugin initialisation succeeded */
305   return TRUE;
306 }
307
308 GST_PLUGIN_DEFINE (
309   GST_VERSION_MAJOR,
310   GST_VERSION_MINOR,
311   "efence",
312   "This element converts a stream of normal GStreamer buffers into a "
313   "stream of buffers that are allocated in such a way that out-of-bounds "
314   "access to data in the buffer is more likely to cause segmentation "
315   "faults.  This allocation method is very similar to the debugging tool "
316   "\"Electric Fence\".",
317   plugin_init,
318   VERSION,
319   "LGPL",
320   GST_PACKAGE,
321   GST_ORIGIN)
322
323 GstBuffer *gst_fenced_buffer_new(void)
324 {
325   GstBuffer *newbuf;
326
327   newbuf = (GstBuffer *) g_new0(GstFencedBuffer,1);
328
329   gst_data_init (GST_DATA (newbuf), _gst_buffer_type, 0,
330                   gst_fenced_buffer_default_free,
331                   gst_fenced_buffer_default_copy);
332
333   GST_BUFFER_DATA (newbuf)         = NULL;
334   GST_BUFFER_SIZE (newbuf)         = 0;
335   GST_BUFFER_MAXSIZE (newbuf)      = GST_BUFFER_MAXSIZE_NONE;
336   GST_BUFFER_TIMESTAMP (newbuf)    = GST_CLOCK_TIME_NONE;
337   GST_BUFFER_DURATION (newbuf)     = GST_CLOCK_TIME_NONE;
338   GST_BUFFER_OFFSET (newbuf)       = GST_BUFFER_OFFSET_NONE;
339   GST_BUFFER_FREE_DATA_FUNC (newbuf) = NULL;
340   GST_BUFFER_PRIVATE (newbuf)      = NULL;
341
342   GST_DEBUG ("new buffer=%p", newbuf);
343
344   return newbuf;
345 }
346
347 void gst_fenced_buffer_default_free (GstData *data)
348 {
349   GstFencedBuffer *fenced_buffer;
350   GstBuffer *buffer = GST_BUFFER (data);
351
352   GST_DEBUG ("free buffer=%p", data);
353
354   g_return_if_fail (data != NULL);
355
356   fenced_buffer = (GstFencedBuffer *) data;
357
358   /* free our data */
359   if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE) &&
360       GST_BUFFER_DATA (buffer)) {
361     GST_DEBUG ("free region %p %d", fenced_buffer->region,
362         fenced_buffer->length);
363     munmap(fenced_buffer->region, fenced_buffer->length);
364   }else{
365     GST_DEBUG ("not freeing region %p %d %p", fenced_buffer->region,
366         GST_BUFFER_FLAGS(buffer), GST_BUFFER_DATA(buffer));
367   }
368
369   /* set to safe values */
370   GST_BUFFER_DATA (buffer) = NULL;
371   GST_BUFFER_SIZE (buffer) = 0;
372   
373   g_free (buffer);
374 }
375
376 GstData* gst_fenced_buffer_default_copy (const GstData *data)
377
378   GstBuffer *buffer = GST_BUFFER (data);
379   GstData *copy;
380   void *ptr;
381   
382   g_return_val_if_fail (buffer != NULL, NULL);
383   
384   /* create a fresh new buffer */
385   copy = (GstData *) g_new0(GstFencedBuffer,1);
386   
387   gst_data_init (copy, _gst_buffer_type, 0,
388                   gst_fenced_buffer_default_free,
389                   gst_fenced_buffer_default_copy);
390   
391   /* we simply copy everything from our parent */
392   ptr = gst_fenced_buffer_alloc(GST_BUFFER(copy),
393       GST_BUFFER_SIZE(buffer), TRUE);
394   memcpy(ptr, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
395
396   GST_BUFFER_SIZE (copy)         = GST_BUFFER_SIZE (buffer);
397   GST_BUFFER_MAXSIZE (copy)      = GST_BUFFER_SIZE (buffer);
398   GST_BUFFER_TIMESTAMP (copy)    = GST_BUFFER_TIMESTAMP (buffer);
399   GST_BUFFER_DURATION (copy)     = GST_BUFFER_DURATION (buffer);
400   GST_BUFFER_OFFSET (copy)       = GST_BUFFER_OFFSET (buffer);
401   GST_BUFFER_FREE_DATA_FUNC (copy) = NULL;
402   GST_BUFFER_PRIVATE (copy)      = NULL;
403   
404   return copy;
405 }
406
407 void *gst_fenced_buffer_alloc(GstBuffer *buffer, unsigned int length,
408     gboolean fence_top)
409 {
410   int alloc_size;
411   void *region;
412   GstFencedBuffer *fenced_buffer = (GstFencedBuffer *) buffer;
413   int page_size;
414
415   GST_DEBUG ("buffer=%p length=%d fence_top=%d", buffer, length, fence_top);
416
417   if(length==0)return NULL;
418
419 #ifdef _SC_PAGESIZE
420   page_size = sysconf(_SC_PAGESIZE);
421 #else
422   page_size = getpagesize();
423 #endif
424
425   alloc_size = ((length - 1) & ~(page_size - 1)) + page_size;
426   alloc_size += 2*page_size;
427
428   region = mmap(NULL, alloc_size, PROT_READ|PROT_WRITE,
429       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
430   if(region == MAP_FAILED){
431     g_warning("mmap failed");
432     return NULL;
433   }
434
435   munmap(region, page_size);
436   munmap(region + alloc_size - page_size, page_size);
437
438   fenced_buffer->region = region;
439   fenced_buffer->length = alloc_size;
440
441   GST_DEBUG ("new region %p %d", fenced_buffer->region,
442       fenced_buffer->length);
443   
444   if(fence_top){
445     int offset;
446     /* Align to top of region, but force alignment to 4 bytes */
447     offset = alloc_size - page_size - length;
448     offset &= ~0x3;
449     return region + offset;
450   }else{
451     return region + page_size;
452   }
453 }
454