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