1 /* Copyright (C) 2021-2023 Free Software Foundation, Inc.
4 This file is part of GNU Binutils.
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)
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.
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. */
29 #include "collector.h"
30 #include "gp-experiment.h"
31 #include "data_pckts.h"
34 /* define the packets to be written out */
35 typedef struct Heap_packet
36 { /* Malloc/free tracing packet */
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 */
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);
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) */
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
;
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
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);
79 void *__libc_malloc (size_t);
80 void __libc_free (void *);
81 void *__libc_realloc (void *, size_t);
84 collector_memset (void *s
, int c
, size_t n
)
86 unsigned char *s1
= s
;
88 *s1
++ = (unsigned char) c
;
92 __collector_module_init (CollectorInterface
*_collector_interface
)
94 if (_collector_interface
== NULL
)
96 collector_interface
= _collector_interface
;
97 Tprintf (0, "heaptrace: __collector_module_init\n");
98 heap_hndl
= collector_interface
->registerModule (&module_interface
);
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
);
108 open_experiment (const char *exp
)
110 if (collector_interface
== NULL
)
112 Tprintf (0, "heaptrace: collector_interface is null.\n");
113 return COL_ERROR_HEAPINIT
;
115 if (heap_hndl
== COLLECTOR_MODULE_ERR
)
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
;
122 TprintfT (0, "heaptrace: open_experiment %s\n", exp
);
123 if (NULL_PTR (malloc
))
126 const char *params
= collector_interface
->getParams ();
129 if ((params
[0] == 'H') && (params
[1] == ':'))
134 params
= CALL_REAL (strchr
)(params
, ';');
138 if (params
== NULL
) /* Heap data collection not specified */
139 return COL_ERROR_HEAPINIT
;
141 heap_key
= collector_interface
->createKey (sizeof ( int), NULL
, NULL
);
142 if (heap_key
== (unsigned) - 1)
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
;
149 collector_interface
->writeLog ("<profile name=\"%s\">\n", SP_JCMD_HEAPTRACE
);
150 collector_interface
->writeLog (" <profdata fname=\"%s\"/>\n",
151 module_interface
.description
);
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
;
180 start_data_collection (void)
183 Tprintf (0, "heaptrace: start_data_collection\n");
188 stop_data_collection (void)
191 Tprintf (0, "heaptrace: stop_data_collection\n");
196 close_experiment (void)
199 heap_key
= COLLECTOR_TSD_INVALID_KEY
;
200 Tprintf (0, "heaptrace: close_experiment\n");
205 detach_experiment (void)
206 /* fork child. Clean up state but don't write to experiment */
209 heap_key
= COLLECTOR_TSD_INVALID_KEY
;
210 Tprintf (0, "heaptrace: detach_experiment\n");
214 static int in_init_heap_intf
= 0; // Flag that we are in init_heap_intf()
220 in_init_heap_intf
= 1;
221 __real_malloc
= (void*(*)(size_t))dlsym (RTLD_NEXT
, "malloc");
222 if (__real_malloc
== NULL
)
224 /* We are probably dlopened after libthread/libc,
225 * try to search in the previously loaded objects
227 __real_malloc
= (void*(*)(size_t))dlsym (RTLD_DEFAULT
, "malloc");
228 if (__real_malloc
== NULL
)
230 Tprintf (0, "heaptrace: ERROR: real malloc not found\n");
231 in_init_heap_intf
= 0;
234 Tprintf (DBG_LT1
, "heaptrace: real malloc found with RTLD_DEFAULT\n");
235 dlflag
= RTLD_DEFAULT
;
239 Tprintf (DBG_LT1
, "heaptrace: real malloc found with RTLD_NEXT\n");
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;
253 /*------------------------------------------------------------- malloc */
261 /* Linux startup workaround */
264 void *ppp
= (void *) __libc_malloc (size
);
265 Tprintf (DBG_LT4
, "heaptrace: LINUX malloc(%ld, %p)...\n", (long) size
, ppp
);
268 if (NULL_PTR (malloc
))
270 if (CHCK_REENTRANCE (guard
))
272 ret
= (void *) CALL_REAL (malloc
)(size
);
273 Tprintf (DBG_LT4
, "heaptrace: real malloc(%ld) = %p\n", (long) size
, ret
);
276 PUSH_REENTRANCE (guard
);
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
);
291 /*------------------------------------------------------------- free */
298 /* Linux startup workaround */
301 // Tprintf(DBG_LT4,"heaptrace: LINUX free(%p)...\n",ptr);
305 if (NULL_PTR (malloc
))
307 if (CHCK_REENTRANCE (guard
))
309 CALL_REAL (free
)(ptr
);
314 PUSH_REENTRANCE (guard
);
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
);
330 /*------------------------------------------------------------- realloc */
332 realloc (void *ptr
, size_t size
)
338 /* Linux startup workaround */
341 void * ppp
= (void *) __libc_realloc (ptr
, size
);
342 Tprintf (DBG_LT4
, "heaptrace: LINUX realloc(%ld, %p->%p)...\n",
343 (long) size
, ptr
, ppp
);
346 if (NULL_PTR (realloc
))
348 if (CHCK_REENTRANCE (guard
))
350 ret
= (void *) CALL_REAL (realloc
)(ptr
, size
);
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
);
368 /*------------------------------------------------------------- memalign */
370 memalign (size_t align
, size_t size
)
375 if (NULL_PTR (memalign
))
377 if (CHCK_REENTRANCE (guard
))
379 ret
= (void *) CALL_REAL (memalign
)(align
, size
);
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
);
396 /*------------------------------------------------------------- valloc */
404 if (NULL_PTR (memalign
))
406 if (CHCK_REENTRANCE (guard
))
408 ret
= (void *) CALL_REAL (valloc
)(size
);
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
);
425 /*------------------------------------------------------------- calloc */
427 calloc (size_t size
, size_t esize
)
432 if (NULL_PTR (calloc
))
434 if (in_init_heap_intf
!= 0)
435 return NULL
; // Terminate infinite loop
438 if (CHCK_REENTRANCE (guard
))
440 ret
= (void *) CALL_REAL (calloc
)(size
, esize
);
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
);
457 /* __collector_heap_record is used to record java allocations/deallocations.
458 * It uses the same facilities as regular heap tracing for now.
461 __collector_heap_record (int mtype
, size_t size
, void *vaddr
)
465 if (CHCK_REENTRANCE (guard
))
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
);