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