1 /* Copyright (C) 2021 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
= (Vaddr_type
) ret
;
302 hpacket
.ovaddr
= (Vaddr_type
) 0;
303 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
304 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
305 POP_REENTRANCE (guard
);
309 /*------------------------------------------------------------- free */
316 /* Linux startup workaround */
319 // Tprintf(DBG_LT4,"heaptrace: LINUX free(%p)...\n",ptr);
323 if (NULL_PTR (malloc
))
325 if (CHCK_REENTRANCE (guard
))
327 CALL_REAL (free
)(ptr
);
332 PUSH_REENTRANCE (guard
);
334 /* Get a timestamp before 'free' to enforce consistency */
335 hrtime_t ts
= gethrtime ();
336 CALL_REAL (free
)(ptr
);
337 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
338 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
339 hpacket
.comm
.tstamp
= ts
;
340 hpacket
.mtype
= FREE_TRACE
;
341 hpacket
.size
= (Size_type
) 0;
342 hpacket
.vaddr
= (Vaddr_type
) ptr
;
343 hpacket
.ovaddr
= (Vaddr_type
) 0;
344 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
345 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
346 POP_REENTRANCE (guard
);
350 /*------------------------------------------------------------- realloc */
352 realloc (void *ptr
, size_t size
)
358 /* Linux startup workaround */
361 void * ppp
= (void *) __libc_realloc (ptr
, size
);
362 Tprintf (DBG_LT4
, "heaptrace: LINUX realloc(%ld, %p->%p)...\n",
363 (long) size
, ptr
, ppp
);
366 if (NULL_PTR (realloc
))
368 if (CHCK_REENTRANCE (guard
))
370 ret
= (void *) CALL_REAL (realloc
)(ptr
, size
);
373 PUSH_REENTRANCE (guard
);
374 hrtime_t ts
= gethrtime ();
375 ret
= (void *) CALL_REAL (realloc
)(ptr
, size
);
376 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
377 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
378 hpacket
.comm
.tstamp
= ts
;
379 hpacket
.mtype
= REALLOC_TRACE
;
380 hpacket
.size
= (Size_type
) size
;
381 hpacket
.vaddr
= (Vaddr_type
) ret
;
382 hpacket
.ovaddr
= (Vaddr_type
) ptr
;
383 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
384 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
385 POP_REENTRANCE (guard
);
389 /*------------------------------------------------------------- memalign */
391 memalign (size_t align
, size_t size
)
396 if (NULL_PTR (memalign
))
398 if (CHCK_REENTRANCE (guard
))
400 ret
= (void *) CALL_REAL (memalign
)(align
, size
);
403 PUSH_REENTRANCE (guard
);
404 ret
= (void *) CALL_REAL (memalign
)(align
, size
);
405 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
406 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
407 hpacket
.comm
.tstamp
= gethrtime ();
408 hpacket
.mtype
= MALLOC_TRACE
;
409 hpacket
.size
= (Size_type
) size
;
410 hpacket
.vaddr
= (Vaddr_type
) ret
;
411 hpacket
.ovaddr
= (Vaddr_type
) 0;
412 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
413 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
414 POP_REENTRANCE (guard
);
418 /*------------------------------------------------------------- valloc */
426 if (NULL_PTR (memalign
))
428 if (CHCK_REENTRANCE (guard
))
430 ret
= (void *) CALL_REAL (valloc
)(size
);
433 PUSH_REENTRANCE (guard
);
434 ret
= (void *) CALL_REAL (valloc
)(size
);
435 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
436 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
437 hpacket
.comm
.tstamp
= gethrtime ();
438 hpacket
.mtype
= MALLOC_TRACE
;
439 hpacket
.size
= (Size_type
) size
;
440 hpacket
.vaddr
= (Vaddr_type
) ret
;
441 hpacket
.ovaddr
= (Vaddr_type
) 0;
442 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
443 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
444 POP_REENTRANCE (guard
);
448 /*------------------------------------------------------------- calloc */
450 calloc (size_t size
, size_t esize
)
455 if (NULL_PTR (calloc
))
457 if (in_init_heap_intf
!= 0)
458 return NULL
; // Terminate infinite loop
461 if (CHCK_REENTRANCE (guard
))
463 ret
= (void *) CALL_REAL (calloc
)(size
, esize
);
466 PUSH_REENTRANCE (guard
);
467 ret
= (void *) CALL_REAL (calloc
)(size
, esize
);
468 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
469 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
470 hpacket
.comm
.tstamp
= gethrtime ();
471 hpacket
.mtype
= MALLOC_TRACE
;
472 hpacket
.size
= (Size_type
) (size
* esize
);
473 hpacket
.vaddr
= (Vaddr_type
) ret
;
474 hpacket
.ovaddr
= (Vaddr_type
) 0;
475 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
476 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
477 POP_REENTRANCE (guard
);
481 /* __collector_heap_record is used to record java allocations/deallocations.
482 * It uses the same facilities as regular heap tracing for now.
485 __collector_heap_record (int mtype
, size_t size
, void *vaddr
)
489 if (CHCK_REENTRANCE (guard
))
491 PUSH_REENTRANCE (guard
);
492 collector_memset (&hpacket
, 0, sizeof ( Heap_packet
));
493 hpacket
.comm
.tsize
= sizeof ( Heap_packet
);
494 hpacket
.comm
.tstamp
= gethrtime ();
495 hpacket
.mtype
= mtype
;
496 hpacket
.size
= (Size_type
) size
;
497 hpacket
.vaddr
= (Vaddr_type
) vaddr
;
498 hpacket
.ovaddr
= (Vaddr_type
) 0;
499 hpacket
.comm
.frinfo
= collector_interface
->getFrameInfo (heap_hndl
, hpacket
.comm
.tstamp
, FRINFO_FROM_STACK
, &hpacket
);
500 collector_interface
->writeDataRecord (heap_hndl
, (Common_packet
*) & hpacket
);
501 POP_REENTRANCE (guard
);