xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/libcollector/tsd.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2    Contributed by Oracle.
3 
4    This file is part of GNU Binutils.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10 
11    This program 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
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
19    MA 02110-1301, USA.  */
20 
21 #include "config.h"
22 #include <pthread.h>
23 
24 #include "collector.h"
25 #include "libcol_util.h"
26 #include "tsd.h"
27 #include "memmgr.h"
28 
29 /* TprintfT(<level>,...) definitions.  Adjust per module as needed */
30 #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
31 #define DBG_LT1 1 // for configuration details, warnings
32 #define DBG_LT2 2
33 #define DBG_LT3 3
34 
35 /*
36  * Build our thread-specific-data support on pthread interfaces.
37  */
38 #define MAXNKEYS    64  /* hard-wired? really? well, it depends only on us and we have a sense for how many keys we will use */
39 static pthread_key_t tsd_pkeys[MAXNKEYS];
40 static size_t tsd_sizes[MAXNKEYS];
41 static unsigned tsd_nkeys = 0;
42 
43 int
__collector_tsd_init()44 __collector_tsd_init ()
45 {
46   return 0;
47 }
48 
49 void
__collector_tsd_fini()50 __collector_tsd_fini ()
51 {
52   Tprintf (DBG_LT1, "tsd_fini()\n");
53   while (tsd_nkeys)
54     {
55       tsd_nkeys--;
56       pthread_key_delete (tsd_pkeys[tsd_nkeys]);
57       tsd_sizes[tsd_nkeys] = 0; // should be unneeded
58     }
59 }
60 
61 int
__collector_tsd_allocate()62 __collector_tsd_allocate ()
63 {
64   return 0;
65 }
66 
67 void
__collector_tsd_release()68 __collector_tsd_release () { }
69 
70 static void
tsd_destructor(void * p)71 tsd_destructor (void *p)
72 {
73   if (p)
74     __collector_freeCSize (__collector_heap, p, *((size_t *) p));
75 }
76 
77 unsigned
__collector_tsd_create_key(size_t sz,void (* init)(void *),void (* fini)(void *))78 __collector_tsd_create_key (size_t sz, void (*init)(void*), void (*fini)(void*))
79 {
80   /*
81    * We no longer support init and fini arguments (and weren't using them anyhow).
82    * Our hard-wired MAXNKEYS presumably is considerably higher than the number of keys we use.
83    */
84   if (init || fini || (tsd_nkeys >= MAXNKEYS))
85     return COLLECTOR_TSD_INVALID_KEY;
86 
87   /*
88    * A pthread key has a value that is (void *).
89    * We don't know where it is stored, and can access its value only through {get|set}specific.
90    * But libcollector expects a pointer to memory that it can modify.
91    * So we have to allocate that memory and store the pointer.
92    *
93    * For now, we just have to register a destructor that will free the memory
94    * when the thread finishes.
95    */
96   if (pthread_key_create (&tsd_pkeys[tsd_nkeys], &tsd_destructor))
97    return COLLECTOR_TSD_INVALID_KEY;
98   tsd_sizes[tsd_nkeys] = sz;
99   tsd_nkeys++;
100   return (tsd_nkeys - 1);
101 }
102 
103 void *
__collector_tsd_get_by_key(unsigned key_index)104 __collector_tsd_get_by_key (unsigned key_index)
105 {
106   if (key_index == COLLECTOR_TSD_INVALID_KEY)
107     return NULL;
108   if (key_index < 0 || key_index >= tsd_nkeys)
109     return NULL;
110   pthread_key_t key = tsd_pkeys[key_index];
111   size_t sz = tsd_sizes[key_index];
112 
113   /*
114    * When we use __collector_freeCSize(), we need to know the
115    * size that had been allocated.  So, stick a header to the
116    * front of the allocation to hold the size.  The header could
117    * just be sizeof(size_t), but pad it to preserve alignment for
118    * the usable area.
119    */
120   size_t header = 8;
121   void *value = pthread_getspecific (key);
122 
123   // check whether we have allocated the memory
124   if (value == NULL)
125     {
126       // add room to record the size
127       value = __collector_allocCSize (__collector_heap, sz + header, 0);
128       if (value == NULL)
129 	{
130 	  // do we need to guard against trying to alloc each time?
131 	  return NULL;
132 	}
133       // write the size of the allocation
134       *((size_t *) value) = sz + header;
135       CALL_UTIL (memset)(((char *) value) + header, 0, sz);
136 
137       // record the allocation for future retrieval
138       if (pthread_setspecific (key, value))
139 	return NULL;
140     }
141   // return the pointer, skipping the header
142   return ((char *) value) +header;
143 }
144 
145 void
__collector_tsd_fork_child_cleanup()146 __collector_tsd_fork_child_cleanup ()
147 {
148   __collector_tsd_fini ();
149 }
150