]> git.cworth.org Git - vogl/blob - src/libbacktrace/mmap.c
Initial vogl checkin
[vogl] / src / libbacktrace / mmap.c
1 /* mmap.c -- Memory allocation with mmap.
2    Copyright (C) 2012-2014 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Google.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8
9     (1) Redistributions of source code must retain the above copyright
10     notice, this list of conditions and the following disclaimer. 
11
12     (2) Redistributions in binary form must reproduce the above copyright
13     notice, this list of conditions and the following disclaimer in
14     the documentation and/or other materials provided with the
15     distribution.  
16     
17     (3) The name of the author may not be used to
18     endorse or promote products derived from this software without
19     specific prior written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY OF SUCH DAMAGE.  */
32
33 #include "config.h"
34
35 #include <errno.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/mman.h>
41
42 #include "backtrace.h"
43 #include "internal.h"
44
45 /* Memory allocation on systems that provide anonymous mmap.  This
46    permits the backtrace functions to be invoked from a signal
47    handler, assuming that mmap is async-signal safe.  */
48
49 #ifndef MAP_ANONYMOUS
50 #define MAP_ANONYMOUS MAP_ANON
51 #endif
52
53 /* A list of free memory blocks.  */
54
55 struct backtrace_freelist_struct
56 {
57   /* Next on list.  */
58   struct backtrace_freelist_struct *next;
59   /* Size of this block, including this structure.  */
60   size_t size;
61 };
62
63 /* Free memory allocated by backtrace_alloc.  */
64
65 static void
66 backtrace_free_locked (struct backtrace_state *state, void *addr, size_t size)
67 {
68   /* Just leak small blocks.  We don't have to be perfect.  */
69   if (size >= sizeof (struct backtrace_freelist_struct))
70     {
71       struct backtrace_freelist_struct *p;
72
73       p = (struct backtrace_freelist_struct *) addr;
74       p->next = state->freelist;
75       p->size = size;
76       state->freelist = p;
77     }
78 }
79
80 /* Allocate memory like malloc.  */
81
82 void *
83 backtrace_alloc (struct backtrace_state *state,
84                  size_t size, backtrace_error_callback error_callback,
85                  void *data)
86 {
87   void *ret;
88   int locked;
89   struct backtrace_freelist_struct **pp;
90   size_t pagesize;
91   size_t asksize;
92   void *page;
93
94   ret = NULL;
95
96   /* If we can acquire the lock, then see if there is space on the
97      free list.  If we can't acquire the lock, drop straight into
98      using mmap.  __sync_lock_test_and_set returns the old state of
99      the lock, so we have acquired it if it returns 0.  */
100
101   if (!state->threaded)
102     locked = 1;
103   else
104     locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0;
105
106   if (locked)
107     {
108       for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next)
109         {
110           if ((*pp)->size >= size)
111             {
112               struct backtrace_freelist_struct *p;
113
114               p = *pp;
115               *pp = p->next;
116
117               /* Round for alignment; we assume that no type we care about
118                  is more than 8 bytes.  */
119               size = (size + 7) & ~ (size_t) 7;
120               if (size < p->size)
121                 backtrace_free_locked (state, (char *) p + size,
122                                        p->size - size);
123
124               ret = (void *) p;
125
126               break;
127             }
128         }
129
130       if (state->threaded)
131         __sync_lock_release (&state->lock_alloc);
132     }
133
134   if (ret == NULL)
135     {
136       /* Allocate a new page.  */
137
138       pagesize = getpagesize ();
139       asksize = (size + pagesize - 1) & ~ (pagesize - 1);
140       page = mmap (NULL, asksize, PROT_READ | PROT_WRITE,
141                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
142       if (page == NULL)
143         error_callback (data, "mmap", errno);
144       else
145         {
146           size = (size + 7) & ~ (size_t) 7;
147           if (size < asksize)
148             backtrace_free (state, (char *) page + size, asksize - size,
149                             error_callback, data);
150
151           ret = page;
152         }
153     }
154
155   return ret;
156 }
157
158 /* Allocate memory like strdup. */
159
160 char *
161 backtrace_strdup (struct backtrace_state *state, const char *str,
162                   backtrace_error_callback error_callback,
163                   void *data)
164 {
165    char *ret;
166
167    ret = NULL;
168
169    if (str)
170    {
171       size_t size;
172       void *mem;
173
174       size = strlen(str) + 1;
175       mem = backtrace_alloc (state, size, error_callback, data);
176       if (mem)
177       {
178          memcpy(mem, str, size);
179          ret = (char *)mem;
180       }
181    }
182    return ret;
183 }
184
185 /* Free memory allocated by backtrace_alloc.  */
186
187 void
188 backtrace_free (struct backtrace_state *state, void *addr, size_t size,
189                 backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
190                 void *data ATTRIBUTE_UNUSED)
191 {
192   int locked;
193
194   /* If we can acquire the lock, add the new space to the free list.
195      If we can't acquire the lock, just leak the memory.
196      __sync_lock_test_and_set returns the old state of the lock, so we
197      have acquired it if it returns 0.  */
198
199   if (!state->threaded)
200     locked = 1;
201   else
202     locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0;
203
204   if (locked)
205     {
206       backtrace_free_locked (state, addr, size);
207
208       if (state->threaded)
209         __sync_lock_release (&state->lock_alloc);
210     }
211 }
212
213 /* Grow VEC by SIZE bytes.  */
214
215 void *
216 backtrace_vector_grow (struct backtrace_state *state,size_t size,
217                        backtrace_error_callback error_callback,
218                        void *data, struct backtrace_vector *vec)
219 {
220   void *ret;
221
222   if (size > vec->alc)
223     {
224       size_t pagesize;
225       size_t alc;
226       void *base;
227
228       pagesize = getpagesize ();
229       alc = vec->size + size;
230       if (vec->size == 0)
231         alc = 16 * size;
232       else if (alc < pagesize)
233         {
234           alc *= 2;
235           if (alc > pagesize)
236             alc = pagesize;
237         }
238       else
239         alc = (alc + pagesize - 1) & ~ (pagesize - 1);
240       base = backtrace_alloc (state, alc, error_callback, data);
241       if (base == NULL)
242         return NULL;
243       if (vec->base != NULL)
244         {
245           memcpy (base, vec->base, vec->size);
246           backtrace_free (state, vec->base, vec->alc, error_callback, data);
247         }
248       vec->base = base;
249       vec->alc = alc - vec->size;
250     }
251
252   ret = (char *) vec->base + vec->size;
253   vec->size += size;
254   vec->alc -= size;
255   return ret;
256 }
257
258 /* Finish the current allocation on VEC.  */
259
260 void *
261 backtrace_vector_finish (
262   struct backtrace_state *state ATTRIBUTE_UNUSED,
263   struct backtrace_vector *vec,
264   backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
265   void *data ATTRIBUTE_UNUSED)
266 {
267   void *ret;
268
269   ret = vec->base;
270   vec->base = (char *) vec->base + vec->size;
271   vec->size = 0;
272   return ret;
273 }
274
275 /* Release any extra space allocated for VEC.  */
276
277 int
278 backtrace_vector_release (struct backtrace_state *state,
279                           struct backtrace_vector *vec,
280                           backtrace_error_callback error_callback,
281                           void *data)
282 {
283   size_t size;
284   size_t alc;
285   size_t aligned;
286
287   /* Make sure that the block that we free is aligned on an 8-byte
288      boundary.  */
289   size = vec->size;
290   alc = vec->alc;
291   aligned = (size + 7) & ~ (size_t) 7;
292   alc -= aligned - size;
293
294   backtrace_free (state, (char *) vec->base + aligned, alc,
295                   error_callback, data);
296   vec->alc = 0;
297   return 1;
298 }