Line data Source code
1 : /* Memory handling for libdw.
2 : Copyright (C) 2003, 2004, 2006 Red Hat, Inc.
3 : This file is part of elfutils.
4 : Written by Ulrich Drepper <drepper@redhat.com>, 2003.
5 :
6 : This file is free software; you can redistribute it and/or modify
7 : it under the terms of either
8 :
9 : * the GNU Lesser General Public License as published by the Free
10 : Software Foundation; either version 3 of the License, or (at
11 : your option) any later version
12 :
13 : or
14 :
15 : * the GNU General Public License as published by the Free
16 : Software Foundation; either version 2 of the License, or (at
17 : your option) any later version
18 :
19 : or both in parallel, as here.
20 :
21 : elfutils is distributed in the hope that it will be useful, but
22 : WITHOUT ANY WARRANTY; without even the implied warranty of
23 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 : General Public License for more details.
25 :
26 : You should have received copies of the GNU General Public License and
27 : the GNU Lesser General Public License along with this program. If
28 : not, see <http://www.gnu.org/licenses/>. */
29 :
30 : #ifdef HAVE_CONFIG_H
31 : # include <config.h>
32 : #endif
33 :
34 : #include <errno.h>
35 : #include <stdlib.h>
36 : #include "libdwP.h"
37 : #include "system.h"
38 : #include "atomics.h"
39 : #if USE_VG_ANNOTATIONS == 1
40 : #include <helgrind.h>
41 : #else
42 : #define ANNOTATE_HAPPENS_BEFORE(X)
43 : #define ANNOTATE_HAPPENS_AFTER(X)
44 : #endif
45 :
46 : #define THREAD_ID_UNSET ((size_t) -1)
47 : static __thread size_t thread_id = THREAD_ID_UNSET;
48 : static atomic_size_t next_id = ATOMIC_VAR_INIT(0);
49 :
50 : struct libdw_memblock *
51 2146990 : __libdw_alloc_tail (Dwarf *dbg)
52 : {
53 2146990 : if (thread_id == THREAD_ID_UNSET)
54 402 : thread_id = atomic_fetch_add (&next_id, 1);
55 :
56 2146990 : pthread_rwlock_rdlock (&dbg->mem_rwl);
57 2146990 : if (thread_id >= dbg->mem_stacks)
58 : {
59 540 : pthread_rwlock_unlock (&dbg->mem_rwl);
60 540 : pthread_rwlock_wrlock (&dbg->mem_rwl);
61 :
62 : /* Another thread may have already reallocated. In theory using an
63 : atomic would be faster, but given that this only happens once per
64 : thread per Dwarf, some minor slowdown should be fine. */
65 540 : if (thread_id >= dbg->mem_stacks)
66 : {
67 540 : dbg->mem_tails = realloc (dbg->mem_tails, (thread_id+1)
68 : * sizeof (struct libdw_memblock *));
69 540 : if (dbg->mem_tails == NULL)
70 : {
71 0 : pthread_rwlock_unlock (&dbg->mem_rwl);
72 0 : dbg->oom_handler();
73 : }
74 1087 : for (size_t i = dbg->mem_stacks; i <= thread_id; i++)
75 547 : dbg->mem_tails[i] = NULL;
76 540 : dbg->mem_stacks = thread_id + 1;
77 540 : ANNOTATE_HAPPENS_BEFORE (&dbg->mem_tails);
78 : }
79 :
80 540 : pthread_rwlock_unlock (&dbg->mem_rwl);
81 540 : pthread_rwlock_rdlock (&dbg->mem_rwl);
82 : }
83 :
84 : /* At this point, we have an entry in the tail array. */
85 2146990 : ANNOTATE_HAPPENS_AFTER (&dbg->mem_tails);
86 2146990 : struct libdw_memblock *result = dbg->mem_tails[thread_id];
87 2146990 : if (result == NULL)
88 : {
89 540 : result = malloc (dbg->mem_default_size);
90 1080 : result->size = dbg->mem_default_size
91 540 : - offsetof (struct libdw_memblock, mem);
92 540 : result->remaining = result->size;
93 540 : result->prev = NULL;
94 540 : dbg->mem_tails[thread_id] = result;
95 : }
96 2146990 : pthread_rwlock_unlock (&dbg->mem_rwl);
97 2146990 : return result;
98 : }
99 :
100 : /* Can only be called after a allocation for this thread has already
101 : been done, to possibly undo it. */
102 : struct libdw_memblock *
103 0 : __libdw_thread_tail (Dwarf *dbg)
104 : {
105 0 : struct libdw_memblock *result;
106 0 : pthread_rwlock_rdlock (&dbg->mem_rwl);
107 0 : result = dbg->mem_tails[thread_id];
108 0 : pthread_rwlock_unlock (&dbg->mem_rwl);
109 0 : return result;
110 : }
111 :
112 : void *
113 18439 : __libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
114 : {
115 18439 : size_t size = MAX (dbg->mem_default_size,
116 : (align - 1 +
117 : 2 * minsize + offsetof (struct libdw_memblock, mem)));
118 18439 : struct libdw_memblock *newp = malloc (size);
119 18439 : if (newp == NULL)
120 0 : dbg->oom_handler ();
121 :
122 18439 : uintptr_t result = ((uintptr_t) newp->mem + align - 1) & ~(align - 1);
123 :
124 18439 : newp->size = size - offsetof (struct libdw_memblock, mem);
125 18439 : newp->remaining = (uintptr_t) newp + size - (result + minsize);
126 :
127 18439 : pthread_rwlock_rdlock (&dbg->mem_rwl);
128 18439 : newp->prev = dbg->mem_tails[thread_id];
129 18439 : dbg->mem_tails[thread_id] = newp;
130 18439 : pthread_rwlock_unlock (&dbg->mem_rwl);
131 :
132 18439 : return (void *) result;
133 : }
134 :
135 :
136 : Dwarf_OOM
137 0 : dwarf_new_oom_handler (Dwarf *dbg, Dwarf_OOM handler)
138 : {
139 0 : Dwarf_OOM old = dbg->oom_handler;
140 0 : dbg->oom_handler = handler;
141 0 : return old;
142 : }
143 :
144 :
145 : void
146 : __attribute ((noreturn)) attribute_hidden
147 0 : __libdw_oom (void)
148 : {
149 0 : while (1)
150 0 : error (EXIT_FAILURE, ENOMEM, "libdw");
151 : }
|