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 */ 42typedef 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 51static int init_heap_intf (); 52static int open_experiment (const char *); 53static int start_data_collection (void); 54static int stop_data_collection (void); 55static int close_experiment (void); 56static int detach_experiment (void); 57 58static 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 68static CollectorInterface *collector_interface = NULL; 69static int heap_mode = 0; 70static CollectorModule heap_hndl = COLLECTOR_MODULE_ERR; 71static 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 88static void *(*__real_malloc)(size_t) = NULL; 89static void (*__real_free)(void *); 90static void *(*__real_realloc)(void *, size_t); 91static void *(*__real_memalign)(size_t, size_t); 92static void *(*__real_calloc)(size_t, size_t); 93static void *(*__real_valloc)(size_t); 94static char *(*__real_strchr)(const char *, int); 95 96void *__libc_malloc (size_t); 97void __libc_free (void *); 98void *__libc_realloc (void *, size_t); 99 100static void 101collector_memset (void *s, int c, size_t n) 102{ 103 unsigned char *s1 = s; 104 while (n--) 105 *s1++ = (unsigned char) c; 106} 107 108void 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 124static int 125open_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 196static int 197start_data_collection (void) 198{ 199 heap_mode = 1; 200 Tprintf (0, "heaptrace: start_data_collection\n"); 201 return 0; 202} 203 204static int 205stop_data_collection (void) 206{ 207 heap_mode = 0; 208 Tprintf (0, "heaptrace: stop_data_collection\n"); 209 return 0; 210} 211 212static int 213close_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 221static int 222detach_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 231static int in_init_heap_intf = 0; // Flag that we are in init_heap_intf() 232 233static int 234init_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 272void * 273malloc (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 310void 311free (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 */ 348void * 349realloc (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 */ 386void * 387memalign (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 415void * 416valloc (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 */ 443void * 444calloc (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 */ 477void 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} 496