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_module.h"
30 #include "gp-experiment.h"
31 #include "data_pckts.h"
34 /* TprintfT(<level>,...) definitions. Adjust per module as needed */
35 #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
36 #define DBG_LT1 1 // for configuration details, warnings
41 /* define the packets to be written out */
42 typedef struct Heap_packet
43 { /* Malloc/free tracing packet */
45 Heap_type mtype
; /* subtype of packet */
46 Size_type size
; /* size of malloc/realloc request */
47 Vaddr_type vaddr
; /* vaddr given to free or returned from malloc/realloc */
48 Vaddr_type ovaddr
; /* Previous vaddr given to realloc */
51 static int init_heap_intf ();
52 static int open_experiment (const char *);
53 static int start_data_collection (void);
54 static int stop_data_collection (void);
55 static int close_experiment (void);
56 static int detach_experiment (void);
58 static ModuleInterface module_interface
= {
59 SP_HEAPTRACE_FILE
, /* description */
60 NULL
, /* initInterface */
61 open_experiment
, /* openExperiment */
62 start_data_collection
, /* startDataCollection */
63 stop_data_collection
, /* stopDataCollection */
64 close_experiment
, /* closeExperiment */
65 detach_experiment
/* detachExperiment (fork child) */
68 static CollectorInterface
*collector_interface
= NULL
;
69 static int heap_mode
= 0;
70 static CollectorModule heap_hndl
= COLLECTOR_MODULE_ERR
;
71 static unsigned heap_key
= COLLECTOR_TSD_INVALID_KEY
;
73 #define CHCK_REENTRANCE(x) ( !heap_mode || ((x) = collector_interface->getKey( heap_key )) == NULL || (*(x) != 0) )
74 #define PUSH_REENTRANCE(x) ((*(x))++)
75 #define POP_REENTRANCE(x) ((*(x))--)
76 #define CALL_REAL(x) (__real_##x)
77 #define NULL_PTR(x) (__real_##x == NULL)
78 #define gethrtime collector_interface->getHiResTime
81 #define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ )
82 #define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ )
88 static void *(*__real_malloc
)(size_t) = NULL
;
89 static void (*__real_free
)(void *);
90 static void *(*__real_realloc
)(void *, size_t);
91 static void *(*__real_memalign
)(size_t, size_t);
92 static void *(*__real_calloc
)(size_t, size_t);
93 static void *(*__real_valloc
)(size_t);
94 static char *(*__real_strchr
)(const char *, int);
96 void *__libc_malloc (size_t);
97 void __libc_free (void *);
98 void *__libc_realloc (void *, size_t);
101 collector_memset (void *s
, int c
, size_t n
)
103 unsigned char *s1
= s
;
105 *s1
++ = (unsigned char) c
;
109 __collector_module_init (CollectorInterface
*_collector_interface
)
111 if (_collector_interface
== NULL
)
113 collector_interface
= _collector_interface
;
114 Tprintf (0, "heaptrace: __collector_module_init\n");
115 heap_hndl
= collector_interface
->registerModule (&module_interface
);
117 /* Initialize next module */
118 ModuleInitFunc next_init
= (ModuleInitFunc
) dlsym (RTLD_NEXT
, "__collector_module_init");
119 if (next_init
!= NULL
)
120 next_init (_collector_interface
);
125 open_experiment (const char *exp
)
127 if (collector_interface
== NULL
)
129 Tprintf (0, "heaptrace: collector_interface is null.\n");
130 return COL_ERROR_HEAPINIT
;
132 if (heap_hndl
== COLLECTOR_MODULE_ERR
)
134 Tprintf (0, "heaptrace: handle create failed.\n");
135 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n",
136 SP_JCMD_CERROR
, COL_ERROR_HEAPINIT
);
137 return COL_ERROR_HEAPINIT
;
139 TprintfT (0, "heaptrace: open_experiment %s\n", exp
);
140 if (NULL_PTR (malloc
))
143 const char *params
= collector_interface
->getParams ();
146 if ((params
[0] == 'H') && (params
[1] == ':'))
151 params
= CALL_REAL (strchr
)(params
, ';');
155 if (params
== NULL
) /* Heap data collection not specified */
156 return COL_ERROR_HEAPINIT
;
158 heap_key
= collector_interface
->createKey (sizeof ( int), NULL
, NULL
);
159 if (heap_key
== (unsigned) - 1)
161 Tprintf (0, "heaptrace: TSD key create failed.\n");
162 collector_interface
->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n",
163 SP_JCMD_CERROR
, COL_ERROR_HEAPINIT
);
164 return COL_ERROR_HEAPINIT
;
166 collector_interface
->writeLog ("<profile name=\"%s\">\n", SP_JCMD_HEAPTRACE
);
167 collector_interface
->writeLog (" <profdata fname=\"%s\"/>\n",
168 module_interface
.description
);
170 /* Record Heap_packet description */
171 Heap_packet
*pp
= NULL
;
172 collector_interface
->writeLog (" <profpckt kind=\"%d\" uname=\"Heap tracing data\">\n", HEAP_PCKT
);
173 collector_interface
->writeLog (" <field name=\"LWPID\" uname=\"Lightweight process id\" offset=\"%d\" type=\"%s\"/>\n",
174 &pp
->comm
.lwp_id
, sizeof (pp
->comm
.lwp_id
) == 4 ? "INT32" : "INT64");
175 collector_interface
->writeLog (" <field name=\"THRID\" uname=\"Thread number\" offset=\"%d\" type=\"%s\"/>\n",
176 &pp
->comm
.thr_id
, sizeof (pp
->comm
.thr_id
) == 4 ? "INT32" : "INT64");
177 collector_interface
->writeLog (" <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" type=\"%s\"/>\n",
178 &pp
->comm
.cpu_id
, sizeof (pp
->comm
.cpu_id
) == 4 ? "INT32" : "INT64");
179 collector_interface
->writeLog (" <field name=\"TSTAMP\" uname=\"High resolution timestamp\" offset=\"%d\" type=\"%s\"/>\n",
180 &pp
->comm
.tstamp
, sizeof (pp
->comm
.tstamp
) == 4 ? "INT32" : "INT64");
181 collector_interface
->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
182 &pp
->comm
.frinfo
, sizeof (pp
->comm
.frinfo
) == 4 ? "INT32" : "INT64");
183 collector_interface
->writeLog (" <field name=\"HTYPE\" uname=\"Heap trace function type\" offset=\"%d\" type=\"%s\"/>\n",
184 &pp
->mtype
, sizeof (pp
->mtype
) == 4 ? "INT32" : "INT64");
185 collector_interface
->writeLog (" <field name=\"HSIZE\" uname=\"Memory size\" offset=\"%d\" type=\"%s\"/>\n",
186 &pp
->size
, sizeof (pp
->size
) == 4 ? "UINT32" : "UINT64");
187 collector_interface
->writeLog (" <field name=\"HVADDR\" uname=\"Memory address\" offset=\"%d\" type=\"%s\"/>\n",
188 &pp
->vaddr
, sizeof (pp
->vaddr
) == 4 ? "UINT32" : "UINT64");
189 collector_interface
->writeLog (" <field name=\"HOVADDR\" uname=\"Previous memory address\" offset=\"%d\" type=\"%s\"/>\n",
190 &pp
->ovaddr
, sizeof (pp
->ovaddr
) == 4 ? "UINT32" : "UINT64");
191 collector_interface
->writeLog (" </profpckt>\n");
192 collector_interface
->writeLog ("</profile>\n");
193 return COL_ERROR_NONE
;
197 start_data_collection (void)
200 Tprintf (0, "heaptrace: start_data_collection\n");
205 stop_data_collection (void)
208 Tprintf (0, "heaptrace: stop_data_collection\n");
213 close_experiment (void)
216 heap_key
= COLLECTOR_TSD_INVALID_KEY
;
217 Tprintf (0, "heaptrace: close_experiment\n");
222 detach_experiment (void)
223 /* fork child. Clean up state but don't write to experiment */
226 heap_key
= COLLECTOR_TSD_INVALID_KEY
;
227 Tprintf (0, "heaptrace: detach_experiment\n");
231 static int in_init_heap_intf
= 0; // Flag that we are in init_heap_intf()
237 in_init_heap_intf
= 1;
238 __real_malloc
= (void*(*)(size_t))dlsym (RTLD_NEXT
, "malloc");
239 if (__real_malloc
== NULL
)
241 /* We are probably dlopened after libthread/libc,
242 * try to search in the previously loaded objects
244 __real_malloc
= (void*(*)(size_t))dlsym (RTLD_DEFAULT
, "malloc");
245 if (__real_malloc
== NULL
)
247 Tprintf (0, "heaptrace: ERROR: real malloc not found\n");
248 in_init_heap_intf
= 0;
251 Tprintf (DBG_LT1
, "heaptrace: real malloc found with RTLD_DEFAULT\n");
252 dlflag
= RTLD_DEFAULT
;
256 Tprintf (DBG_LT1
, "heaptrace: real malloc found with RTLD_NEXT\n");
259 __real_free
= (void(*)(void *))dlsym (dlflag
, "free");
260 __real_realloc
= (void*(*)(void *, size_t))dlsym (dlflag
, "realloc");
261 __real_memalign
= (void*(*)(size_t, size_t))dlsym (dlflag
, "memalign");
262 __real_calloc
= (void*(*)(size_t, size_t))dlsym (dlflag
, "calloc");
263 __real_valloc
= (void*(*)(size_t))dlsym (dlflag
, "valloc");
264 __real_strchr
= (char*(*)(const char *, int))dlsym (dlflag
, "strchr");
265 Tprintf (0, "heaptrace: init_heap_intf done\n");
266 in_init_heap_intf
= 0;
270 /*------------------------------------------------------------- malloc */
278 /* Linux startup workaround */
281 void *ppp
= (void *) __libc_malloc (size
);
282 Tprintf (DBG_LT4
, "heaptrace: LINUX malloc(%ld, %p)...\n", (long) size
, ppp
);
285 if (NULL_PTR (malloc
))
287 if (CHCK_REENTRANCE (guard
))
289 ret
= (void *) CALL_REAL (malloc
)(size
);
290 Tprintf (DBG_LT4
, "heaptrace: real malloc(%ld) = %p\n", (long) size
, ret
);
293 PUSH_REENTRANCE (guard
);
295 ret
= (void *) CALL_REAL (malloc
)(size
);
296 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
297 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
298 hpacket
.comm
.tstamp
= gethrtime ();
299 hpacket
.mtype
= MALLOC_TRACE
;
300 hpacket
.size
= (Size_type
) size
;
301 hpacket
.vaddr
= (intptr_t) ret
;
302 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
303 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
304 POP_REENTRANCE (guard
);
308 /*------------------------------------------------------------- free */
315 /* Linux startup workaround */
318 // Tprintf(DBG_LT4,"heaptrace: LINUX free(%p)...\n",ptr);
322 if (NULL_PTR (malloc
))
324 if (CHCK_REENTRANCE (guard
))
326 CALL_REAL (free
)(ptr
);
331 PUSH_REENTRANCE (guard
);
333 /* Get a timestamp before 'free' to enforce consistency */
334 hrtime_t ts
= gethrtime ();
335 CALL_REAL (free
)(ptr
);
336 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
337 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
338 hpacket
.comm
.tstamp
= ts
;
339 hpacket
.mtype
= FREE_TRACE
;
340 hpacket
.vaddr
= (intptr_t) ptr
;
341 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
342 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
343 POP_REENTRANCE (guard
);
347 /*------------------------------------------------------------- realloc */
349 realloc (void *ptr
, size_t size
)
355 /* Linux startup workaround */
358 void * ppp
= (void *) __libc_realloc (ptr
, size
);
359 Tprintf (DBG_LT4
, "heaptrace: LINUX realloc(%ld, %p->%p)...\n",
360 (long) size
, ptr
, ppp
);
363 if (NULL_PTR (realloc
))
365 if (CHCK_REENTRANCE (guard
))
367 ret
= (void *) CALL_REAL (realloc
)(ptr
, size
);
370 PUSH_REENTRANCE (guard
);
371 hrtime_t ts
= gethrtime ();
372 ret
= (void *) CALL_REAL (realloc
)(ptr
, size
);
373 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
374 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
375 hpacket
.comm
.tstamp
= ts
;
376 hpacket
.mtype
= REALLOC_TRACE
;
377 hpacket
.size
= (Size_type
) size
;
378 hpacket
.vaddr
= (intptr_t) ret
;
379 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
380 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
381 POP_REENTRANCE (guard
);
385 /*------------------------------------------------------------- memalign */
387 memalign (size_t align
, size_t size
)
392 if (NULL_PTR (memalign
))
394 if (CHCK_REENTRANCE (guard
))
396 ret
= (void *) CALL_REAL (memalign
)(align
, size
);
399 PUSH_REENTRANCE (guard
);
400 ret
= (void *) CALL_REAL (memalign
)(align
, size
);
401 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
402 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
403 hpacket
.comm
.tstamp
= gethrtime ();
404 hpacket
.mtype
= MALLOC_TRACE
;
405 hpacket
.size
= (Size_type
) size
;
406 hpacket
.vaddr
= (intptr_t) ret
;
407 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
408 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
409 POP_REENTRANCE (guard
);
413 /*------------------------------------------------------------- valloc */
421 if (NULL_PTR (memalign
))
423 if (CHCK_REENTRANCE (guard
))
425 ret
= (void *) CALL_REAL (valloc
)(size
);
428 PUSH_REENTRANCE (guard
);
429 ret
= (void *) CALL_REAL (valloc
)(size
);
430 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
431 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
432 hpacket
.comm
.tstamp
= gethrtime ();
433 hpacket
.mtype
= MALLOC_TRACE
;
434 hpacket
.size
= (Size_type
) size
;
435 hpacket
.vaddr
= (intptr_t) ret
;
436 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
437 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
438 POP_REENTRANCE (guard
);
442 /*------------------------------------------------------------- calloc */
444 calloc (size_t size
, size_t esize
)
449 if (NULL_PTR (calloc
))
451 if (in_init_heap_intf
!= 0)
452 return NULL
; // Terminate infinite loop
455 if (CHCK_REENTRANCE (guard
))
457 ret
= (void *) CALL_REAL (calloc
)(size
, esize
);
460 PUSH_REENTRANCE (guard
);
461 ret
= (void *) CALL_REAL (calloc
)(size
, esize
);
462 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
463 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
464 hpacket
.comm
.tstamp
= gethrtime ();
465 hpacket
.mtype
= MALLOC_TRACE
;
466 hpacket
.size
= (Size_type
) (size
* esize
);
467 hpacket
.vaddr
= (intptr_t) ret
;
468 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
469 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
470 POP_REENTRANCE (guard
);
474 /* __collector_heap_record is used to record java allocations/deallocations.
475 * It uses the same facilities as regular heap tracing for now.
478 __collector_heap_record (int mtype
, size_t size
, void *vaddr
)
482 if (CHCK_REENTRANCE (guard
))
484 PUSH_REENTRANCE (guard
);
485 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
486 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
487 hpacket
.comm
.tstamp
= gethrtime ();
488 hpacket
.mtype
= mtype
;
489 hpacket
.size
= (Size_type
) size
;
490 hpacket
.vaddr
= (intptr_t) vaddr
;
491 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
492 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
493 POP_REENTRANCE (guard
);