regen config
[binutils-gdb.git] / gprofng / libcollector / heaptrace.c
1 /* Copyright (C) 2021-2023 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 /*
22 * Heap tracing events
23 */
24
25 #include "config.h"
26 #include <dlfcn.h>
27
28 #include "gp-defs.h"
29 #include "collector.h"
30 #include "gp-experiment.h"
31 #include "data_pckts.h"
32 #include "tsd.h"
33
34 /* define the packets to be written out */
35 typedef struct Heap_packet
36 { /* Malloc/free tracing packet */
37 Common_packet comm;
38 Heap_type mtype; /* subtype of packet */
39 Size_type size; /* size of malloc/realloc request */
40 Vaddr_type vaddr; /* vaddr given to free or returned from malloc/realloc */
41 Vaddr_type ovaddr; /* Previous vaddr given to realloc */
42 } Heap_packet;
43
44 static int init_heap_intf ();
45 static int open_experiment (const char *);
46 static int start_data_collection (void);
47 static int stop_data_collection (void);
48 static int close_experiment (void);
49 static int detach_experiment (void);
50
51 static ModuleInterface module_interface = {
52 SP_HEAPTRACE_FILE, /* description */
53 NULL, /* initInterface */
54 open_experiment, /* openExperiment */
55 start_data_collection, /* startDataCollection */
56 stop_data_collection, /* stopDataCollection */
57 close_experiment, /* closeExperiment */
58 detach_experiment /* detachExperiment (fork child) */
59 };
60
61 static CollectorInterface *collector_interface = NULL;
62 static int heap_mode = 0;
63 static CollectorModule heap_hndl = COLLECTOR_MODULE_ERR;
64 static unsigned heap_key = COLLECTOR_TSD_INVALID_KEY;
65
66 #define CHCK_REENTRANCE(x) ( !heap_mode || ((x) = collector_interface->getKey( heap_key )) == NULL || (*(x) != 0) )
67 #define PUSH_REENTRANCE(x) ((*(x))++)
68 #define POP_REENTRANCE(x) ((*(x))--)
69 #define gethrtime collector_interface->getHiResTime
70
71 static void *(*__real_malloc)(size_t) = NULL;
72 static void (*__real_free)(void *);
73 static void *(*__real_realloc)(void *, size_t);
74 static void *(*__real_memalign)(size_t, size_t);
75 static void *(*__real_calloc)(size_t, size_t);
76 static void *(*__real_valloc)(size_t);
77 static char *(*__real_strchr)(const char *, int);
78
79 void *__libc_malloc (size_t);
80 void __libc_free (void *);
81 void *__libc_realloc (void *, size_t);
82
83 static void
84 collector_memset (void *s, int c, size_t n)
85 {
86 unsigned char *s1 = s;
87 while (n--)
88 *s1++ = (unsigned char) c;
89 }
90
91 void
92 __collector_module_init (CollectorInterface *_collector_interface)
93 {
94 if (_collector_interface == NULL)
95 return;
96 collector_interface = _collector_interface;
97 Tprintf (0, "heaptrace: __collector_module_init\n");
98 heap_hndl = collector_interface->registerModule (&module_interface);
99
100 /* Initialize next module */
101 ModuleInitFunc next_init = (ModuleInitFunc) dlsym (RTLD_NEXT, "__collector_module_init");
102 if (next_init != NULL)
103 next_init (_collector_interface);
104 return;
105 }
106
107 static int
108 open_experiment (const char *exp)
109 {
110 if (collector_interface == NULL)
111 {
112 Tprintf (0, "heaptrace: collector_interface is null.\n");
113 return COL_ERROR_HEAPINIT;
114 }
115 if (heap_hndl == COLLECTOR_MODULE_ERR)
116 {
117 Tprintf (0, "heaptrace: handle create failed.\n");
118 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n",
119 SP_JCMD_CERROR, COL_ERROR_HEAPINIT);
120 return COL_ERROR_HEAPINIT;
121 }
122 TprintfT (0, "heaptrace: open_experiment %s\n", exp);
123 if (NULL_PTR (malloc))
124 init_heap_intf ();
125
126 const char *params = collector_interface->getParams ();
127 while (params)
128 {
129 if ((params[0] == 'H') && (params[1] == ':'))
130 {
131 params += 2;
132 break;
133 }
134 params = CALL_REAL (strchr)(params, ';');
135 if (params)
136 params++;
137 }
138 if (params == NULL) /* Heap data collection not specified */
139 return COL_ERROR_HEAPINIT;
140
141 heap_key = collector_interface->createKey (sizeof ( int), NULL, NULL);
142 if (heap_key == (unsigned) - 1)
143 {
144 Tprintf (0, "heaptrace: TSD key create failed.\n");
145 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n",
146 SP_JCMD_CERROR, COL_ERROR_HEAPINIT);
147 return COL_ERROR_HEAPINIT;
148 }
149 collector_interface->writeLog ("<profile name=\"%s\">\n", SP_JCMD_HEAPTRACE);
150 collector_interface->writeLog (" <profdata fname=\"%s\"/>\n",
151 module_interface.description);
152
153 /* Record Heap_packet description */
154 Heap_packet *pp = NULL;
155 collector_interface->writeLog (" <profpckt kind=\"%d\" uname=\"Heap tracing data\">\n", HEAP_PCKT);
156 collector_interface->writeLog (" <field name=\"LWPID\" uname=\"Lightweight process id\" offset=\"%d\" type=\"%s\"/>\n",
157 &pp->comm.lwp_id, sizeof (pp->comm.lwp_id) == 4 ? "INT32" : "INT64");
158 collector_interface->writeLog (" <field name=\"THRID\" uname=\"Thread number\" offset=\"%d\" type=\"%s\"/>\n",
159 &pp->comm.thr_id, sizeof (pp->comm.thr_id) == 4 ? "INT32" : "INT64");
160 collector_interface->writeLog (" <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" type=\"%s\"/>\n",
161 &pp->comm.cpu_id, sizeof (pp->comm.cpu_id) == 4 ? "INT32" : "INT64");
162 collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"High resolution timestamp\" offset=\"%d\" type=\"%s\"/>\n",
163 &pp->comm.tstamp, sizeof (pp->comm.tstamp) == 4 ? "INT32" : "INT64");
164 collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
165 &pp->comm.frinfo, sizeof (pp->comm.frinfo) == 4 ? "INT32" : "INT64");
166 collector_interface->writeLog (" <field name=\"HTYPE\" uname=\"Heap trace function type\" offset=\"%d\" type=\"%s\"/>\n",
167 &pp->mtype, sizeof (pp->mtype) == 4 ? "INT32" : "INT64");
168 collector_interface->writeLog (" <field name=\"HSIZE\" uname=\"Memory size\" offset=\"%d\" type=\"%s\"/>\n",
169 &pp->size, sizeof (pp->size) == 4 ? "UINT32" : "UINT64");
170 collector_interface->writeLog (" <field name=\"HVADDR\" uname=\"Memory address\" offset=\"%d\" type=\"%s\"/>\n",
171 &pp->vaddr, sizeof (pp->vaddr) == 4 ? "UINT32" : "UINT64");
172 collector_interface->writeLog (" <field name=\"HOVADDR\" uname=\"Previous memory address\" offset=\"%d\" type=\"%s\"/>\n",
173 &pp->ovaddr, sizeof (pp->ovaddr) == 4 ? "UINT32" : "UINT64");
174 collector_interface->writeLog (" </profpckt>\n");
175 collector_interface->writeLog ("</profile>\n");
176 return COL_ERROR_NONE;
177 }
178
179 static int
180 start_data_collection (void)
181 {
182 heap_mode = 1;
183 Tprintf (0, "heaptrace: start_data_collection\n");
184 return 0;
185 }
186
187 static int
188 stop_data_collection (void)
189 {
190 heap_mode = 0;
191 Tprintf (0, "heaptrace: stop_data_collection\n");
192 return 0;
193 }
194
195 static int
196 close_experiment (void)
197 {
198 heap_mode = 0;
199 heap_key = COLLECTOR_TSD_INVALID_KEY;
200 Tprintf (0, "heaptrace: close_experiment\n");
201 return 0;
202 }
203
204 static int
205 detach_experiment (void)
206 /* fork child. Clean up state but don't write to experiment */
207 {
208 heap_mode = 0;
209 heap_key = COLLECTOR_TSD_INVALID_KEY;
210 Tprintf (0, "heaptrace: detach_experiment\n");
211 return 0;
212 }
213
214 static int in_init_heap_intf = 0; // Flag that we are in init_heap_intf()
215
216 static int
217 init_heap_intf ()
218 {
219 void *dlflag;
220 in_init_heap_intf = 1;
221 __real_malloc = (void*(*)(size_t))dlsym (RTLD_NEXT, "malloc");
222 if (__real_malloc == NULL)
223 {
224 /* We are probably dlopened after libthread/libc,
225 * try to search in the previously loaded objects
226 */
227 __real_malloc = (void*(*)(size_t))dlsym (RTLD_DEFAULT, "malloc");
228 if (__real_malloc == NULL)
229 {
230 Tprintf (0, "heaptrace: ERROR: real malloc not found\n");
231 in_init_heap_intf = 0;
232 return 1;
233 }
234 Tprintf (DBG_LT1, "heaptrace: real malloc found with RTLD_DEFAULT\n");
235 dlflag = RTLD_DEFAULT;
236 }
237 else
238 {
239 Tprintf (DBG_LT1, "heaptrace: real malloc found with RTLD_NEXT\n");
240 dlflag = RTLD_NEXT;
241 }
242 __real_free = (void(*)(void *))dlsym (dlflag, "free");
243 __real_realloc = (void*(*)(void *, size_t))dlsym (dlflag, "realloc");
244 __real_memalign = (void*(*)(size_t, size_t))dlsym (dlflag, "memalign");
245 __real_calloc = (void*(*)(size_t, size_t))dlsym (dlflag, "calloc");
246 __real_valloc = (void*(*)(size_t))dlsym (dlflag, "valloc");
247 __real_strchr = (char*(*)(const char *, int))dlsym (dlflag, "strchr");
248 Tprintf (0, "heaptrace: init_heap_intf done\n");
249 in_init_heap_intf = 0;
250 return 0;
251 }
252
253 /*------------------------------------------------------------- malloc */
254
255 void *
256 malloc (size_t size)
257 {
258 void *ret;
259 int *guard;
260 Heap_packet hpacket;
261 /* Linux startup workaround */
262 if (!heap_mode)
263 {
264 void *ppp = (void *) __libc_malloc (size);
265 Tprintf (DBG_LT4, "heaptrace: LINUX malloc(%ld, %p)...\n", (long) size, ppp);
266 return ppp;
267 }
268 if (NULL_PTR (malloc))
269 init_heap_intf ();
270 if (CHCK_REENTRANCE (guard))
271 {
272 ret = (void *) CALL_REAL (malloc)(size);
273 Tprintf (DBG_LT4, "heaptrace: real malloc(%ld) = %p\n", (long) size, ret);
274 return ret;
275 }
276 PUSH_REENTRANCE (guard);
277
278 ret = (void *) CALL_REAL (malloc)(size);
279 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
280 hpacket.comm.tsize = sizeof ( Heap_packet);
281 hpacket.comm.tstamp = gethrtime ();
282 hpacket.mtype = MALLOC_TRACE;
283 hpacket.size = (Size_type) size;
284 hpacket.vaddr = (intptr_t) ret;
285 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
286 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
287 POP_REENTRANCE (guard);
288 return (void *) ret;
289 }
290
291 /*------------------------------------------------------------- free */
292
293 void
294 free (void *ptr)
295 {
296 int *guard;
297 Heap_packet hpacket;
298 /* Linux startup workaround */
299 if (!heap_mode)
300 {
301 // Tprintf(DBG_LT4,"heaptrace: LINUX free(%p)...\n",ptr);
302 __libc_free (ptr);
303 return;
304 }
305 if (NULL_PTR (malloc))
306 init_heap_intf ();
307 if (CHCK_REENTRANCE (guard))
308 {
309 CALL_REAL (free)(ptr);
310 return;
311 }
312 if (ptr == NULL)
313 return;
314 PUSH_REENTRANCE (guard);
315
316 /* Get a timestamp before 'free' to enforce consistency */
317 hrtime_t ts = gethrtime ();
318 CALL_REAL (free)(ptr);
319 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
320 hpacket.comm.tsize = sizeof ( Heap_packet);
321 hpacket.comm.tstamp = ts;
322 hpacket.mtype = FREE_TRACE;
323 hpacket.vaddr = (intptr_t) ptr;
324 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
325 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
326 POP_REENTRANCE (guard);
327 return;
328 }
329
330 /*------------------------------------------------------------- realloc */
331 void *
332 realloc (void *ptr, size_t size)
333 {
334 void *ret;
335 int *guard;
336 Heap_packet hpacket;
337
338 /* Linux startup workaround */
339 if (!heap_mode)
340 {
341 void * ppp = (void *) __libc_realloc (ptr, size);
342 Tprintf (DBG_LT4, "heaptrace: LINUX realloc(%ld, %p->%p)...\n",
343 (long) size, ptr, ppp);
344 return ppp;
345 }
346 if (NULL_PTR (realloc))
347 init_heap_intf ();
348 if (CHCK_REENTRANCE (guard))
349 {
350 ret = (void *) CALL_REAL (realloc)(ptr, size);
351 return ret;
352 }
353 PUSH_REENTRANCE (guard);
354 hrtime_t ts = gethrtime ();
355 ret = (void *) CALL_REAL (realloc)(ptr, size);
356 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
357 hpacket.comm.tsize = sizeof ( Heap_packet);
358 hpacket.comm.tstamp = ts;
359 hpacket.mtype = REALLOC_TRACE;
360 hpacket.size = (Size_type) size;
361 hpacket.vaddr = (intptr_t) ret;
362 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
363 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
364 POP_REENTRANCE (guard);
365 return (void *) ret;
366 }
367
368 /*------------------------------------------------------------- memalign */
369 void *
370 memalign (size_t align, size_t size)
371 {
372 void *ret;
373 int *guard;
374 Heap_packet hpacket;
375 if (NULL_PTR (memalign))
376 init_heap_intf ();
377 if (CHCK_REENTRANCE (guard))
378 {
379 ret = (void *) CALL_REAL (memalign)(align, size);
380 return ret;
381 }
382 PUSH_REENTRANCE (guard);
383 ret = (void *) CALL_REAL (memalign)(align, size);
384 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
385 hpacket.comm.tsize = sizeof ( Heap_packet);
386 hpacket.comm.tstamp = gethrtime ();
387 hpacket.mtype = MALLOC_TRACE;
388 hpacket.size = (Size_type) size;
389 hpacket.vaddr = (intptr_t) ret;
390 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
391 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
392 POP_REENTRANCE (guard);
393 return ret;
394 }
395
396 /*------------------------------------------------------------- valloc */
397
398 void *
399 valloc (size_t size)
400 {
401 void *ret;
402 int *guard;
403 Heap_packet hpacket;
404 if (NULL_PTR (memalign))
405 init_heap_intf ();
406 if (CHCK_REENTRANCE (guard))
407 {
408 ret = (void *) CALL_REAL (valloc)(size);
409 return ret;
410 }
411 PUSH_REENTRANCE (guard);
412 ret = (void *) CALL_REAL (valloc)(size);
413 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
414 hpacket.comm.tsize = sizeof ( Heap_packet);
415 hpacket.comm.tstamp = gethrtime ();
416 hpacket.mtype = MALLOC_TRACE;
417 hpacket.size = (Size_type) size;
418 hpacket.vaddr = (intptr_t) ret;
419 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
420 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
421 POP_REENTRANCE (guard);
422 return ret;
423 }
424
425 /*------------------------------------------------------------- calloc */
426 void *
427 calloc (size_t size, size_t esize)
428 {
429 void *ret;
430 int *guard;
431 Heap_packet hpacket;
432 if (NULL_PTR (calloc))
433 {
434 if (in_init_heap_intf != 0)
435 return NULL; // Terminate infinite loop
436 init_heap_intf ();
437 }
438 if (CHCK_REENTRANCE (guard))
439 {
440 ret = (void *) CALL_REAL (calloc)(size, esize);
441 return ret;
442 }
443 PUSH_REENTRANCE (guard);
444 ret = (void *) CALL_REAL (calloc)(size, esize);
445 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
446 hpacket.comm.tsize = sizeof ( Heap_packet);
447 hpacket.comm.tstamp = gethrtime ();
448 hpacket.mtype = MALLOC_TRACE;
449 hpacket.size = (Size_type) (size * esize);
450 hpacket.vaddr = (intptr_t) ret;
451 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
452 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
453 POP_REENTRANCE (guard);
454 return ret;
455 }
456
457 /* __collector_heap_record is used to record java allocations/deallocations.
458 * It uses the same facilities as regular heap tracing for now.
459 */
460 void
461 __collector_heap_record (int mtype, size_t size, void *vaddr)
462 {
463 int *guard;
464 Heap_packet hpacket;
465 if (CHCK_REENTRANCE (guard))
466 return;
467 PUSH_REENTRANCE (guard);
468 collector_memset (&hpacket, 0, sizeof ( Heap_packet));
469 hpacket.comm.tsize = sizeof ( Heap_packet);
470 hpacket.comm.tstamp = gethrtime ();
471 hpacket.mtype = mtype;
472 hpacket.size = (Size_type) size;
473 hpacket.vaddr = (intptr_t) vaddr;
474 hpacket.comm.frinfo = collector_interface->getFrameInfo (heap_hndl, hpacket.comm.tstamp, FRINFO_FROM_STACK, &hpacket);
475 collector_interface->writeDataRecord (heap_hndl, (Common_packet*) & hpacket);
476 POP_REENTRANCE (guard);
477 return;
478 }