gprofng: adjust GPROFNG_VARIANT
[binutils-gdb.git] / gprofng / libcollector / heaptrace.c
1 /* Copyright (C) 2021 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_module.h"
30 #include "gp-experiment.h"
31 #include "data_pckts.h"
32 #include "tsd.h"
33
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
37 #define DBG_LT2 2
38 #define DBG_LT3 3
39 #define DBG_LT4 4
40
41 /* define the packets to be written out */
42 typedef struct Heap_packet
43 { /* Malloc/free tracing packet */
44 Common_packet comm;
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 */
49 } Heap_packet;
50
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);
57
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) */
66 };
67
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;
72
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
79
80 #ifdef DEBUG
81 #define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ )
82 #define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ )
83 #else
84 #define Tprintf(...)
85 #define TprintfT(...)
86 #endif
87
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);
95
96 void *__libc_malloc (size_t);
97 void __libc_free (void *);
98 void *__libc_realloc (void *, size_t);
99
100 static void
101 collector_memset (void *s, int c, size_t n)
102 {
103 unsigned char *s1 = s;
104 while (n--)
105 *s1++ = (unsigned char) c;
106 }
107
108 void
109 __collector_module_init (CollectorInterface *_collector_interface)
110 {
111 if (_collector_interface == NULL)
112 return;
113 collector_interface = _collector_interface;
114 Tprintf (0, "heaptrace: __collector_module_init\n");
115 heap_hndl = collector_interface->registerModule (&module_interface);
116
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);
121 return;
122 }
123
124 static int
125 open_experiment (const char *exp)
126 {
127 if (collector_interface == NULL)
128 {
129 Tprintf (0, "heaptrace: collector_interface is null.\n");
130 return COL_ERROR_HEAPINIT;
131 }
132 if (heap_hndl == COLLECTOR_MODULE_ERR)
133 {
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;
138 }
139 TprintfT (0, "heaptrace: open_experiment %s\n", exp);
140 if (NULL_PTR (malloc))
141 init_heap_intf ();
142
143 const char *params = collector_interface->getParams ();
144 while (params)
145 {
146 if ((params[0] == 'H') && (params[1] == ':'))
147 {
148 params += 2;
149 break;
150 }
151 params = CALL_REAL (strchr)(params, ';');
152 if (params)
153 params++;
154 }
155 if (params == NULL) /* Heap data collection not specified */
156 return COL_ERROR_HEAPINIT;
157
158 heap_key = collector_interface->createKey (sizeof ( int), NULL, NULL);
159 if (heap_key == (unsigned) - 1)
160 {
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;
165 }
166 collector_interface->writeLog ("<profile name=\"%s\">\n", SP_JCMD_HEAPTRACE);
167 collector_interface->writeLog (" <profdata fname=\"%s\"/>\n",
168 module_interface.description);
169
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;
194 }
195
196 static int
197 start_data_collection (void)
198 {
199 heap_mode = 1;
200 Tprintf (0, "heaptrace: start_data_collection\n");
201 return 0;
202 }
203
204 static int
205 stop_data_collection (void)
206 {
207 heap_mode = 0;
208 Tprintf (0, "heaptrace: stop_data_collection\n");
209 return 0;
210 }
211
212 static int
213 close_experiment (void)
214 {
215 heap_mode = 0;
216 heap_key = COLLECTOR_TSD_INVALID_KEY;
217 Tprintf (0, "heaptrace: close_experiment\n");
218 return 0;
219 }
220
221 static int
222 detach_experiment (void)
223 /* fork child. Clean up state but don't write to experiment */
224 {
225 heap_mode = 0;
226 heap_key = COLLECTOR_TSD_INVALID_KEY;
227 Tprintf (0, "heaptrace: detach_experiment\n");
228 return 0;
229 }
230
231 static int in_init_heap_intf = 0; // Flag that we are in init_heap_intf()
232
233 static int
234 init_heap_intf ()
235 {
236 void *dlflag;
237 in_init_heap_intf = 1;
238 __real_malloc = (void*(*)(size_t))dlsym (RTLD_NEXT, "malloc");
239 if (__real_malloc == NULL)
240 {
241 /* We are probably dlopened after libthread/libc,
242 * try to search in the previously loaded objects
243 */
244 __real_malloc = (void*(*)(size_t))dlsym (RTLD_DEFAULT, "malloc");
245 if (__real_malloc == NULL)
246 {
247 Tprintf (0, "heaptrace: ERROR: real malloc not found\n");
248 in_init_heap_intf = 0;
249 return 1;
250 }
251 Tprintf (DBG_LT1, "heaptrace: real malloc found with RTLD_DEFAULT\n");
252 dlflag = RTLD_DEFAULT;
253 }
254 else
255 {
256 Tprintf (DBG_LT1, "heaptrace: real malloc found with RTLD_NEXT\n");
257 dlflag = RTLD_NEXT;
258 }
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;
267 return 0;
268 }
269
270 /*------------------------------------------------------------- malloc */
271
272 void *
273 malloc (size_t size)
274 {
275 void *ret;
276 int *guard;
277 Heap_packet hpacket;
278 /* Linux startup workaround */
279 if (!heap_mode)
280 {
281 void *ppp = (void *) __libc_malloc (size);
282 Tprintf (DBG_LT4, "heaptrace: LINUX malloc(%ld, %p)...\n", (long) size, ppp);
283 return ppp;
284 }
285 if (NULL_PTR (malloc))
286 init_heap_intf ();
287 if (CHCK_REENTRANCE (guard))
288 {
289 ret = (void *) CALL_REAL (malloc)(size);
290 Tprintf (DBG_LT4, "heaptrace: real malloc(%ld) = %p\n", (long) size, ret);
291 return ret;
292 }
293 PUSH_REENTRANCE (guard);
294
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);
305 return (void *) ret;
306 }
307
308 /*------------------------------------------------------------- free */
309
310 void
311 free (void *ptr)
312 {
313 int *guard;
314 Heap_packet hpacket;
315 /* Linux startup workaround */
316 if (!heap_mode)
317 {
318 // Tprintf(DBG_LT4,"heaptrace: LINUX free(%p)...\n",ptr);
319 __libc_free (ptr);
320 return;
321 }
322 if (NULL_PTR (malloc))
323 init_heap_intf ();
324 if (CHCK_REENTRANCE (guard))
325 {
326 CALL_REAL (free)(ptr);
327 return;
328 }
329 if (ptr == NULL)
330 return;
331 PUSH_REENTRANCE (guard);
332
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);
344 return;
345 }
346
347 /*------------------------------------------------------------- realloc */
348 void *
349 realloc (void *ptr, size_t size)
350 {
351 void *ret;
352 int *guard;
353 Heap_packet hpacket;
354
355 /* Linux startup workaround */
356 if (!heap_mode)
357 {
358 void * ppp = (void *) __libc_realloc (ptr, size);
359 Tprintf (DBG_LT4, "heaptrace: LINUX realloc(%ld, %p->%p)...\n",
360 (long) size, ptr, ppp);
361 return ppp;
362 }
363 if (NULL_PTR (realloc))
364 init_heap_intf ();
365 if (CHCK_REENTRANCE (guard))
366 {
367 ret = (void *) CALL_REAL (realloc)(ptr, size);
368 return ret;
369 }
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);
382 return (void *) ret;
383 }
384
385 /*------------------------------------------------------------- memalign */
386 void *
387 memalign (size_t align, size_t size)
388 {
389 void *ret;
390 int *guard;
391 Heap_packet hpacket;
392 if (NULL_PTR (memalign))
393 init_heap_intf ();
394 if (CHCK_REENTRANCE (guard))
395 {
396 ret = (void *) CALL_REAL (memalign)(align, size);
397 return ret;
398 }
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);
410 return ret;
411 }
412
413 /*------------------------------------------------------------- valloc */
414
415 void *
416 valloc (size_t size)
417 {
418 void *ret;
419 int *guard;
420 Heap_packet hpacket;
421 if (NULL_PTR (memalign))
422 init_heap_intf ();
423 if (CHCK_REENTRANCE (guard))
424 {
425 ret = (void *) CALL_REAL (valloc)(size);
426 return ret;
427 }
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);
439 return ret;
440 }
441
442 /*------------------------------------------------------------- calloc */
443 void *
444 calloc (size_t size, size_t esize)
445 {
446 void *ret;
447 int *guard;
448 Heap_packet hpacket;
449 if (NULL_PTR (calloc))
450 {
451 if (in_init_heap_intf != 0)
452 return NULL; // Terminate infinite loop
453 init_heap_intf ();
454 }
455 if (CHCK_REENTRANCE (guard))
456 {
457 ret = (void *) CALL_REAL (calloc)(size, esize);
458 return ret;
459 }
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);
471 return ret;
472 }
473
474 /* __collector_heap_record is used to record java allocations/deallocations.
475 * It uses the same facilities as regular heap tracing for now.
476 */
477 void
478 __collector_heap_record (int mtype, size_t size, void *vaddr)
479 {
480 int *guard;
481 Heap_packet hpacket;
482 if (CHCK_REENTRANCE (guard))
483 return;
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);
494 return;
495 }