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