drd/tests/swapcontext: Improve the portability of this test further
[valgrind.git] / coregrind / m_aspacemgr / aspacemgr-segnames.c
blobcfd7ffa33046ceab2826983c86e4a00bb02f3f07
1 /* -*- mode: C; c-basic-offset: 3; -*- */
3 /*--------------------------------------------------------------------*/
4 /*--- Segment name management aspacemgr-segnames.c ---*/
5 /*--------------------------------------------------------------------*/
7 /*
8 This file is part of Valgrind, a dynamic binary instrumentation
9 framework.
11 Copyright (C) 2015-2017 Florian Krohm
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, see <http://www.gnu.org/licenses/>.
26 The GNU General Public License is contained in the file COPYING.
29 /* Segment names are stored in a string table.
31 The string table is organised into slots of varying length. Slots are
32 adjacent and there are no holes between slots.
33 A slot consists of two parts:
35 (1) a fixed size overhead of length 4 bytes
36 (2) a variable size payload of up to 65535 bytes
38 The segment name is stored in the payload area. Therefore:
39 a segment name cannot be longer than 65535 bytes including the '\0'
40 terminator. This looks like a reasonable limitation.
42 Overall slot layout:
44 | 4 bytes | max 65535 bytes |
45 +-----------------------------+-------------------------+
46 | overhead | payload |
47 +-----------------------------+-------------------------+
48 ^ ^
49 | |
50 -4 +----- seg->fnIdx
52 Each slot is uniquely identified by an index which points to the first
53 byte of the payload area. It is this value that is stored in seg->fnIdx.
54 Note, that this value is at least 4.
56 A slot either holds a string or it is free. The status of a slot is
57 identified by the leftmost bit in the overhead field, the so called F-bit.
58 F-bit == 1 means that slot is free; otherwise it is occupied and holds a
59 string.
61 Slot containing a string (segment name):
63 bits | 1 | 15 | 16 |
64 +---+--------------+----------+-------------------------+
65 | 0 | refcount | slotsize | the string including \0 |
66 +---+--------------+----------+-------------------------+
67 ^ ^ ^
68 | | |
69 -4 -2 +----- seg->fnIdx
71 Segment names are reference counted. 15 bits are available which allows
72 for up to 32767 references. If the string is referenced more than 32767
73 times, the reference count will be frozen and the slot can never
74 become free. I'm not unduly concerned.
75 Two bytes are reserved to hold the size of the slot. Well, it's actually
76 the size of the payload aread (i.e. the size of the slot minus the
77 overhead). Ah well -- the name sticks.
78 With two bytes to store the size, the payload area can be at most 65535
79 bytes large.
81 A free slot looks like this:
83 bits | 1 | 31 | 16 |
84 +---+-------------------------+----------+--------------+
85 | 1 | index of next free slot | slotsize | .. unused .. |
86 +---+-------------------------+----------+--------------+
87 ^ ^
88 | |
89 -4 +----- seg->fnIdx
91 Free slots are chained together in a singly linked list. An index of
92 zero indicates the end of the chain. Note that zero cannot conflict
93 with an index into the string table as the minimum index is at least
94 four (see above).
96 The typical way to traverse the segment names is:
98 for (ix = overhead; (size = get_slotsize(ix)) != 0; ix += size + overhead) {
99 if (is_freeslot(ix))
100 do this
101 else
102 do that
105 Important detail: there is a sentinel at the end of the list, namely a
106 slot with a zero-sized payload area.
108 Whenever a new segment name needs to be stashed away, the list of free
109 slots is traversed and the first slot which is large enough is being taken
110 (first fit). There will be no splitting of slots, as that complicates
111 matters and without slot coalescing would lead to memory fragmentation.
112 So we leave it as is until a use case comes up that needs something better.
115 #include "pub_core_basics.h" // types
116 #include "priv_aspacemgr.h"
118 // A few constants.
119 enum {
120 refcount_size = sizeof(UShort),
121 slotsize_size = sizeof(UShort),
122 overhead = refcount_size + slotsize_size,
123 max_refcount = 0x7fff, // 2 bytes - F-bit
124 max_slotsize = 0xffff, // 2 bytes
125 max_slotindex = 0x7fffffff, // 4 bytes - F-bit
126 fbit_mask_value = 0x80,
127 end_of_chain = 0
130 static const UInt fbit_mask = fbit_mask_value;
132 /* The old segname implementation allowed for 1000 names on Android and
133 6000 names on other platforms. Each name was allowed to be 1000 characters
134 long. That was very wasteful. */
135 #define VG_TABLE_SIZE 1000000
137 /* String table for segment names */
139 static HChar segnames[VG_TABLE_SIZE]; /* her majesty, the string table */
140 static SizeT segnames_used = 0; /* number of bytes used */
141 static UInt num_segnames = 0; /* number of names in string table */
142 static UInt num_slots = 0; /* number of slots in string table */
143 static UInt freeslot_chain = end_of_chain;
145 static Bool
146 is_freeslot(UInt ix)
148 aspacem_assert(ix >= overhead && ix <= segnames_used);
149 return (segnames[ix - 4] & fbit_mask) != 0;
152 static void
153 put_slotindex(UInt ix, UInt slotindex)
155 aspacem_assert(ix >= overhead && ix <= segnames_used);
156 if (slotindex != 0)
157 aspacem_assert(slotindex >= overhead && slotindex <= segnames_used);
159 slotindex |= fbit_mask << 24;
160 segnames[ix - 1] = slotindex & 0xFF; slotindex >>= 8;
161 segnames[ix - 2] = slotindex & 0xFF; slotindex >>= 8;
162 segnames[ix - 3] = slotindex & 0xFF; slotindex >>= 8;
163 segnames[ix - 4] = slotindex & 0xFF;
166 static UInt
167 get_slotindex(UInt ix)
169 aspacem_assert(ix >= overhead && ix <= segnames_used);
170 aspacem_assert(is_freeslot(ix));
172 // Avoid unexpected sign extension
173 const UChar *unames = (const UChar *)segnames;
175 UInt slotindex = 0;
176 slotindex |= unames[ix - 4]; slotindex <<= 8;
177 slotindex |= unames[ix - 3]; slotindex <<= 8;
178 slotindex |= unames[ix - 2]; slotindex <<= 8;
179 slotindex |= unames[ix - 1];
181 return slotindex & max_slotindex; // removes the F-bit
184 static void
185 put_slotsize(UInt ix, UInt size)
187 aspacem_assert(ix >= overhead && ix <= segnames_used);
188 aspacem_assert(size <= max_slotsize);
189 segnames[ix - 1] = size & 0xff;
190 segnames[ix - 2] = size >> 8;
193 static UInt
194 get_slotsize(UInt ix)
196 aspacem_assert(ix >= overhead && ix <= segnames_used);
198 // Avoid unexpected sign extension
199 const UChar *unames = (const UChar *)segnames;
200 if (is_freeslot(ix))
201 return (unames[ix] << 8) | unames[ix+1];
202 else
203 return (unames[ix - 2] << 8) | unames[ix - 1];
206 static void
207 put_refcount(UInt ix, UInt rc)
209 aspacem_assert(ix >= overhead && ix <= segnames_used);
210 aspacem_assert(rc <= max_refcount);
211 // rc <= max_refcount ensures that the F-bit is zero
212 segnames[ix - 3] = rc & 0xff;
213 segnames[ix - 4] = rc >> 8;
216 static UInt
217 get_refcount(UInt ix)
219 aspacem_assert(ix >= overhead && ix <= segnames_used);
220 // must not be a free slot
221 aspacem_assert(! is_freeslot(ix));
223 // Avoid unexpected sign extension
224 const UChar *unames = (const UChar *)segnames;
225 return (unames[ix - 4] << 8) | unames[ix - 3];
228 static void
229 inc_refcount(UInt ix)
231 aspacem_assert(ix >= overhead && ix <= segnames_used);
232 UInt rc = get_refcount(ix);
233 if (rc != max_refcount)
234 put_refcount(ix, rc + 1);
237 static void
238 dec_refcount(UInt ix)
240 aspacem_assert(ix >= overhead && ix <= segnames_used);
241 UInt rc = get_refcount(ix);
242 aspacem_assert(rc > 0);
243 if (rc != max_refcount) {
244 --rc;
245 if (rc != 0) {
246 put_refcount(ix, rc);
247 } else {
248 UInt size = get_slotsize(ix);
249 /* Chain this slot in the freelist */
250 put_slotindex(ix, freeslot_chain);
251 put_slotsize(ix + slotsize_size, size);
252 freeslot_chain = ix;
253 --num_segnames;
254 if (0) VG_(am_show_nsegments)(0, "AFTER DECREASE rc -> 0");
259 static void
260 put_sentinel(UInt ix)
262 aspacem_assert(ix >= overhead && ix <= segnames_used);
264 put_refcount(ix, 0);
265 put_slotsize(ix, 0);
269 /* Searches the string table to find an index for the given name.
270 If none is found, an index is allocated and the name stored.
271 If running ouf of memory, return -1. */
273 ML_(am_allocate_segname)(const HChar *name)
275 UInt len, ix, size, next_freeslot;
277 aspacem_assert(name);
279 if (0) VG_(debugLog)(0, "aspacem", "allocate_segname %s\n", name);
281 len = VG_(strlen)(name);
283 /* First see if we already have the name. */
284 for (ix = overhead; (size = get_slotsize(ix)) != 0; ix += size + overhead) {
285 if (is_freeslot(ix)) continue;
286 if (VG_(strcmp)(name, segnames + ix) == 0) {
287 inc_refcount(ix);
288 return ix;
292 /* Is there a free slot in the string table from a previously "freed"
293 segment name ? */
294 Int prev;
295 for (prev = -1, ix = freeslot_chain; ix != end_of_chain;
296 prev = ix, ix = next_freeslot) {
297 next_freeslot = get_slotindex(ix); // next in chain
298 size = get_slotsize(ix);
300 if (size >= len + 1) {
301 /* Note, if the size of the slot is a lot larger than the length
302 of the string we're about to store in it, we could split the
303 slot into two. But that complicates matters and as we're not
304 doing any coalescing of adjacent free slots this could lead to
305 fragmentation. */
306 if (prev == -1)
307 freeslot_chain = next_freeslot;
308 else
309 put_slotindex(prev, next_freeslot);
310 put_refcount(ix, 0);
311 put_slotsize(ix, size);
312 VG_(strcpy)(segnames + ix, name);
313 ++num_segnames;
314 return ix;
318 /* We need to add a new name. */
320 /* Note, that we need at least two bytes in the payload. The reason is
321 that the payload area will be used to store the size of the slot when
322 the slot is on the freelist. */
323 if (len == 0) len = 1;
325 /* Is there enough room in the string table? The OVERHEAD is for the
326 sentinel following the payload of new slot. */
327 SizeT need = len + 1 + overhead;
328 if (need > (sizeof segnames) - segnames_used) {
329 return -1;
332 ++num_segnames;
333 ++num_slots;
335 /* copy it in */
336 ix = segnames_used;
337 put_refcount(ix, 0);
338 put_slotsize(ix, len + 1);
339 VG_(strcpy)(segnames + ix, name);
340 segnames_used += need;
342 /* Add sentinel at end of segment name list */
343 put_sentinel(segnames_used);
345 return ix;
348 /* Debugging output */
349 void
350 ML_(am_show_segnames)(Int logLevel, const HChar *prefix)
352 UInt size, ix, i;
354 VG_(debugLog)(logLevel, "aspacem", "%u segment names in %u slots\n",
355 num_segnames, num_slots);
357 if (freeslot_chain == end_of_chain)
358 VG_(debugLog)(logLevel, "aspacem", "freelist is empty\n");
359 else
360 VG_(debugLog)(logLevel, "aspacem", "freelist begins at %u\n",
361 freeslot_chain);
362 for (i = 0, ix = overhead; (size = get_slotsize(ix)) != 0;
363 ix += size + overhead, ++i) {
364 if (is_freeslot(ix))
365 VG_(debugLog)(logLevel, "aspacem",
366 "(%u,%u,0) [free slot: size=%u next=%u]\n", i, ix,
367 get_slotsize(ix), get_slotindex(ix));
368 else
369 VG_(debugLog)(logLevel, "aspacem",
370 "(%u,%u,%u) %s\n", i, ix, get_refcount(ix),
371 segnames + ix);
375 /* Returns a sequence number for the fnIdx position in segnames.
376 Used in aspacemgr debug output to associate a segment with
377 a segment name. */
379 ML_(am_segname_get_seqnr)(Int fnIdx)
381 SizeT ix, size;
382 Int seqnr = -1;
384 if (fnIdx == -1) return -1; // shortcut
386 for (ix = overhead; (size = get_slotsize(ix)) != 0; ix += size + overhead) {
387 seqnr++;
388 if (ix == fnIdx)
389 return seqnr;
392 // We should always find the given index; something's busted
393 aspacem_assert(0);
394 return -1;
397 /* Initialise the string table for segment names. It contains an empty
398 string which is not referenced. */
399 void
400 ML_(am_segnames_init)(void)
402 aspacem_assert(sizeof segnames >= overhead);
404 segnames_used = overhead;
405 put_sentinel(segnames_used);
408 /* Increase reference count of segment name identified by IX. */
409 void
410 ML_(am_inc_refcount)(Int ix)
412 if (ix != -1)
413 inc_refcount(ix);
416 /* Decrease reference count of segment name identified by IX. */
417 void
418 ML_(am_dec_refcount)(Int ix)
420 if (ix != -1)
421 dec_refcount(ix);
424 Bool
425 ML_(am_sane_segname)(Int ix)
427 return ix == -1 || (ix >= overhead && ix < segnames_used);
430 const HChar *
431 ML_(am_get_segname)(Int ix)
433 return (ix == -1) ? NULL : segnames + ix;
436 /*--------------------------------------------------------------------*/
437 /*--- end ---*/
438 /*--------------------------------------------------------------------*/