HPCToolkit
mem.c
Go to the documentation of this file.
1 // -*-Mode: C++;-*- // technically C99
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //
48 // The new memory allocator. We mmap() a large, single region (4 Meg)
49 // per thread and dole out pieces via hpcrun_malloc(). Pieces are
50 // either freeable (CCT nodes) or not freeable (everything else).
51 // When memory gets low, we write out an epoch and reclaim the CCT
52 // nodes.
53 //
54 
55 #include <sys/mman.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58 
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 
66 // no redefinition of hpcrun_malloc and friends inside mem.c
67 #define _IN_MEM_C 1
68 # include "hpcrun-malloc.h"
69 #undef _IN_MEM_C
70 
71 #include "env.h"
72 #include "newmem.h"
73 #include "sample_event.h"
74 #include "thread_data.h"
75 
76 #include <messages/messages.h>
77 
78 #define DEFAULT_MEMSIZE (4 * 1024 * 1024)
79 #define MIN_LOW_MEMSIZE (80 * 1024)
80 #define DEFAULT_PAGESIZE 4096
81 
82 static size_t memsize = DEFAULT_MEMSIZE;
83 static size_t low_memsize = MIN_LOW_MEMSIZE;
84 static size_t pagesize = DEFAULT_PAGESIZE;
85 static int allow_extra_mmap = 1;
86 
87 static long num_segments = 0;
88 static long total_allocation = 0;
89 static long num_reclaims = 0;
90 static long num_failures = 0;
91 static long total_freeable = 0;
92 static long total_non_freeable = 0;
93 
94 static int out_of_mem_mesg = 0;
95 
96 //------------------------------------------------------------------
97 // Internal functions
98 //------------------------------------------------------------------
99 
100 static inline size_t
101 round_up(size_t size)
102 {
103  return (size + 7) & ~7L;
104 }
105 
106 static inline size_t
108 {
109  return ((size + pagesize - 1)/pagesize) * pagesize;
110 }
111 
112 // Look up environ variables and pagesize.
113 static void
115 {
116  static int init_done = 0;
117  char *str;
118  long ans;
119 
120  if (init_done)
121  return;
122 
123 #ifdef _SC_PAGESIZE
124  if ((ans = sysconf(_SC_PAGESIZE)) > 0) {
125  pagesize = ans;
126  }
127 #endif
128 
129  str = getenv(HPCRUN_MEMSIZE);
130  if (str != NULL && sscanf(str, "%ld", &ans) == 1) {
132  }
133 
134  str = getenv(HPCRUN_LOW_MEMSIZE);
135  if (str != NULL && sscanf(str, "%ld", &ans) == 1) {
136  low_memsize = ans;
137  } else {
138  low_memsize = memsize/40;
141  }
142 
143  TMSG(MALLOC, "%s: pagesize = %ld, memsize = %ld, "
144  "low memsize = %ld, extra mmap = %d",
146  init_done = 1;
147 }
148 
149 //
150 // Returns: address of mmap-ed region, else NULL on failure.
151 //
152 // Note: we leak fds in the rare case of a process that creates many
153 // threads running on a system that doesn't allow MAP_ANON.
154 //
155 static void *
156 hpcrun_mmap_anon(size_t size)
157 {
158  int prot, flags, fd;
159  char *str;
160  void *addr;
161 
162  size = hpcrun_align_pagesize(size);
163  prot = PROT_READ | PROT_WRITE;
164  fd = -1;
165 
166 #if defined(MAP_ANON)
167  flags = MAP_PRIVATE | MAP_ANON;
168 #elif defined(MAP_ANONYMOUS)
169  flags = MAP_PRIVATE | MAP_ANONYMOUS;
170 #else
171  flags = MAP_PRIVATE;
172  fd = open("/dev/zero", O_RDWR);
173  if (fd < 0) {
174  str = strerror(errno);
175  EMSG("%s: open /dev/null failed: %s", __func__, str);
176  return NULL;
177  }
178 #endif
179 
180  addr = mmap(NULL, size, prot, flags, fd, 0);
181  if (addr == MAP_FAILED) {
182  str = strerror(errno);
183  EMSG("%s: mmap failed: %s", __func__, str);
184  addr = NULL;
185  } else {
186  num_segments++;
187  total_allocation += size;
188  }
189 
190  TMSG(MALLOC, "%s: size = %ld, fd = %d, addr = %p",
191  __func__, size, fd, addr);
192  return addr;
193 }
194 
195 //------------------------------------------------------------------
196 // External functions
197 //------------------------------------------------------------------
198 
199 //
200 // memstore layout:
201 //
202 // +------------------+------------+----------------+
203 // | freeable (cct) | | non-freeable |
204 // +------------------+------------+----------------+
205 // mi_start mi_low mi_high
206 //
207 
208 // After fork(), the parent's memstores are still allocated in the
209 // child, so don't reset num_segments.
210 // FIXME: it's not clear which stats should be reset here.
211 void
213 {
214  num_reclaims = 0;
215  num_failures = 0;
216  total_freeable = 0;
217  total_non_freeable = 0;
218  out_of_mem_mesg = 0;
219 }
220 
221 // Allocate space and init a thread's memstore.
222 // If failure, shutdown sampling and leave old memstore in place.
223 void
225 {
226  void *addr;
227 
228  hpcrun_mem_init();
229 
230  // If in the child after fork(), then continue to use the parent's
231  // memstore if it looks ok, else mmap a new one. Note: we can't
232  // reset the memstore to empty unless we delete everything that was
233  // created via hpcrun_malloc() (cct, uw_recipe_map, ...).
234  if (is_child && mi->mi_start != NULL
235  && mi->mi_start <= mi->mi_low && mi->mi_low <= mi->mi_high
236  && mi->mi_high <= mi->mi_start + mi->mi_size) {
237  return;
238  }
239 
240  addr = hpcrun_mmap_anon(memsize);
241  if (addr == NULL) {
242  if (! out_of_mem_mesg) {
243  EMSG("%s: out of memory, shutting down sampling", __func__);
244  out_of_mem_mesg = 1;
245  }
247  return;
248  }
249 
250  mi->mi_start = addr;
251  mi->mi_size = memsize;
252  mi->mi_low = mi->mi_start;
253  mi->mi_high = mi->mi_start + memsize;
254 
255  TMSG(MALLOC, "new memstore: [%p, %p)", mi->mi_start, mi->mi_high);
256 }
257 
258 // Reclaim the freeable CCT memory at the low end.
259 void
261 {
262  hpcrun_meminfo_t *mi = &TD_GET(memstore);
263 
264  mi->mi_low = mi->mi_start;
265  TD_GET(mem_low) = 0;
266  num_reclaims++;
267  TMSG(MALLOC, "%s: %d", __func__, num_reclaims);
268 }
269 
270 //
271 // Returns: address of non-freeable region at the high end,
272 // else NULL on failure.
273 //
274 void *
275 hpcrun_malloc(size_t size)
276 {
278  void *addr;
279 
280  // Lush wants to ask for 0 bytes and get back NULL.
281  if (size == 0) {
282  return NULL;
283  }
284 
285  mi = &TD_GET(memstore);
286  size = round_up(size);
287 
288  // For a large request that doesn't fit within the existing
289  // memstore, mmap a separate region for it.
290  if (size > memsize/5 && allow_extra_mmap
291  && (mi->mi_start == NULL || size > mi->mi_high - mi->mi_low)) {
292  addr = hpcrun_mmap_anon(size);
293  if (addr == NULL) {
294  if (! out_of_mem_mesg) {
295  EMSG("%s: out of memory, shutting down sampling", __func__);
296  out_of_mem_mesg = 1;
297  }
299  num_failures++;
300  return NULL;
301  }
302  TMSG(MALLOC, "%s: size = %ld, addr = %p", __func__, size, addr);
303  total_non_freeable += size;
304  return addr;
305  }
306 
307  // See if we need to allocate a new memstore.
308  if (mi->mi_start == NULL
309  || mi->mi_high - mi->mi_low < low_memsize
310  || mi->mi_high - mi->mi_low < size) {
311  if (allow_extra_mmap) {
312  hpcrun_make_memstore(mi, 0);
313  } else {
314  if (! out_of_mem_mesg) {
315  EMSG("%s: out of memory, shutting down sampling", __func__);
316  out_of_mem_mesg = 1;
317  }
319  }
320  }
321 
322  // There is no memstore, for some reason.
323  if (mi->mi_start == NULL) {
324  TMSG(MALLOC, "%s: size = %ld, failure: no memstore",
325  __func__, size);
326  num_failures++;
327  return NULL;
328  }
329 
330  // Not enough space in existing memstore.
331  addr = mi->mi_high - size;
332  if (addr <= mi->mi_low) {
333  TMSG(MALLOC, "%s: size = %ld, failure: out of memory",
334  __func__, size);
335  num_failures++;
336  return NULL;
337  }
338 
339  // Success
340  mi->mi_high = addr;
341  total_non_freeable += size;
342  TMSG(MALLOC, "%s: size = %ld, addr = %p", __func__, size, addr);
343  return addr;
344 }
345 
346 //
347 // Returns: address of freeable region at the high end,
348 // else NULL on failure.
349 //
350 void *
352 {
353  return hpcrun_malloc(size);
354 
355  // For now, don't bother with freeable memory.
356 #if 0
357  hpcrun_meminfo_t *mi = &TD_GET(memstore);
358  void *addr, *ans;
359 
360  size = round_up(size);
361  addr = mi->mi_low + size;
362 
363  // Recoverable out of memory.
364  if (addr >= mi->mi_high) {
365  TD_GET(mem_low) = 1;
366  TMSG(MALLOC, "%s: size = %ld, failure: temporary out of memory",
367  __func__, size);
368  num_failures++;
369  return NULL;
370  }
371 
372  // Low on memory.
373  if (addr + low_memsize > mi->mi_high) {
374  TD_GET(mem_low) = 1;
375  TMSG(MALLOC, "%s: low on memory, setting epoch flush flag", __func__);
376  }
377 
378  ans = mi->mi_low;
379  mi->mi_low = addr;
380  total_freeable += size;
381  TMSG(MALLOC, "%s: size = %ld, addr = %p", __func__, size, addr);
382  return ans;
383 #endif
384 }
385 
386 void
388 {
389  double meg = 1024.0 * 1024.0;
390 
391  AMSG("MEMORY: segment size: %.1f meg, num segments: %ld, "
392  "total allocation: %.1f meg, reclaims: %ld",
394 
395  AMSG("MEMORY: total freeable: %.1f meg, total non-freeable: %.1f meg, "
396  "malloc failures: %ld",
398 }
static void hpcrun_mem_init(void)
Definition: mem.c:114
#define DEFAULT_PAGESIZE
Definition: mem.c:80
const char * HPCRUN_MEMSIZE
Definition: env.c:56
#define MIN_LOW_MEMSIZE
Definition: mem.c:79
static long total_non_freeable
Definition: mem.c:92
static int allow_extra_mmap
Definition: mem.c:85
void * mi_start
Definition: newmem.h:58
static long total_allocation
Definition: mem.c:88
static long total_freeable
Definition: mem.c:91
void hpcrun_memory_reinit(void)
Definition: mem.c:212
static int out_of_mem_mesg
Definition: mem.c:94
static size_t memsize
Definition: mem.c:82
static long num_failures
Definition: mem.c:90
#define EMSG
Definition: messages.h:70
static size_t round_up(size_t size)
Definition: mem.c:101
static void * hpcrun_mmap_anon(size_t size)
Definition: mem.c:156
#define DEFAULT_MEMSIZE
Definition: mem.c:78
static long num_reclaims
Definition: mem.c:89
#define TD_GET(field)
Definition: thread_data.h:256
void hpcrun_make_memstore(hpcrun_meminfo_t *mi, int is_child)
Definition: mem.c:224
#define TMSG(f,...)
Definition: messages.h:93
static size_t pagesize
Definition: mem.c:84
static size_t hpcrun_align_pagesize(size_t size)
Definition: mem.c:107
#define AMSG
Definition: messages.h:71
#define NULL
Definition: ElfHelper.cpp:85
static long num_segments
Definition: mem.c:87
static void hpcrun_disable_sampling(void)
Definition: sample_event.h:79
const char * HPCRUN_LOW_MEMSIZE
Definition: env.c:57
cct_addr_t * addr
Definition: cct.c:130
void * hpcrun_malloc_freeable(size_t size)
Definition: mem.c:351
static MachInsn * mi
Definition: x86ISAXed.cpp:91
void hpcrun_reclaim_freeable_mem(void)
Definition: mem.c:260
void * hpcrun_malloc(size_t size)
Definition: mem.c:275
static size_t low_memsize
Definition: mem.c:83
long mi_size
Definition: newmem.h:61
void * mi_low
Definition: newmem.h:59
void * mi_high
Definition: newmem.h:60
void hpcrun_memory_summary(void)
Definition: mem.c:387