<commit_before>// -*-Mode: C++;-*-
// $Id$
//
// The new memory allocator.  We mmap() a large, single region (6 Meg)
// per thread and dole out pieces via csprof_malloc().  Pieces are
// either freeable (CCT nodes) or not freeable (everything else).
// When memory gets low, we write out an epoch and reclaim the CCT
// nodes.
//

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "csprof-malloc.h"
#include "env.h"
#include "monitor.h"
#include "newmem.h"
#include "pmsg.h"
#include "sample_event.h"
#include "thread_data.h"

#define DEFAULT_MEMSIZE   (6 * 1024 * 1024)
#define BGP_MEMSIZE      (10 * 1024 * 1024)
#define MIN_LOW_MEMSIZE  (80 * 1024)
#define DEFAULT_PAGESIZE  4096

static size_t memsize = DEFAULT_MEMSIZE;
static size_t low_memsize = MIN_LOW_MEMSIZE;
static size_t pagesize = DEFAULT_PAGESIZE;
static int allow_extra_mmap = 1;

static long num_segments = 0;
static long num_reclaims = 0;
static long num_failures = 0;
static long total_freeable = 0;
static long total_non_freeable = 0;

static int out_of_mem_mesg = 0;

//------------------------------------------------------------------
// Internal functions
//------------------------------------------------------------------

static inline size_t
round_up(size_t size)
{
  return (size + 7) & ~7L;
}

static inline size_t
hpcrun_align_pagesize(size_t size)
{
  return ((size + pagesize - 1)/pagesize) * pagesize;
}

// Look up environ variables and pagesize.
static void
hpcrun_mem_init(void)
{
  static int init_done = 0;
  char *str;
  long ans;

  if (init_done)
    return;

#ifdef _SC_PAGESIZE
  if ((ans = sysconf(_SC_PAGESIZE)) > 0) {
    pagesize = ans;
  }
#endif

#ifdef HOST_SYSTEM_IBM_BLUEGENE
  memsize = BGP_MEMSIZE;
  allow_extra_mmap = 0;
#endif

  str = getenv(HPCRUN_MEMSIZE);
  if (str != NULL && sscanf(str, "%ld", &ans) == 1) {
    memsize = hpcrun_align_pagesize(ans);
  }

  str = getenv(HPCRUN_LOW_MEMSIZE);
  if (str != NULL && sscanf(str, "%ld", &ans) == 1) {
    low_memsize = ans;
  } else {
    low_memsize = memsize/25;
    if (low_memsize < MIN_LOW_MEMSIZE)
      low_memsize = MIN_LOW_MEMSIZE;
  }

  TMSG(MALLOC, "%s: pagesize = %ld, memsize = %ld, "
       "low memsize = %ld, extra mmap = %d",
       __func__, pagesize, memsize, low_memsize, allow_extra_mmap);
  init_done = 1;
}

//
// Returns: address of mmap-ed region, else NULL on failure.
//
// Note: we leak fds in the rare case of a process that creates many
// threads running on a system that doesn't allow MAP_ANON.
//
static void *
hpcrun_mmap_anon(size_t size)
{
  int prot, flags, fd;
  char *str;
  void *addr;

  size = hpcrun_align_pagesize(size);
  prot = PROT_READ | PROT_WRITE;
  fd = -1;

#if defined(MAP_ANON)
  flags = MAP_PRIVATE | MAP_ANON;
#elif defined(MAP_ANONYMOUS)
  flags = MAP_PRIVATE | MAP_ANONYMOUS;
#else
  flags = MAP_PRIVATE;
  fd = open("/dev/zero", O_RDWR);
  if (fd < 0) {
    str = strerror(errno);
    EMSG("%s: open /dev/null failed: %s", __func__, str);
    return NULL;
  }
#endif

  addr = mmap(NULL, size, prot, flags, fd, 0);
  if (addr == MAP_FAILED) {
    str = strerror(errno);
    EMSG("%s: mmap failed: %s", __func__, str);
    addr = NULL;
  }

  TMSG(MALLOC, "%s: size = %ld, fd = %d, addr = %p",
       __func__, size, fd, addr);
  return addr;
}

//------------------------------------------------------------------
// External functions
//------------------------------------------------------------------

//
// memstore layout:
//
//   +------------------+------------+----------------+
//   |  freeable (cct)  |            |  non-freeable  |
//   +------------------+------------+----------------+
//   mi_start           mi_low       mi_high
//

// Allocate space and init a thread's memstore.
void
hpcrun_make_memstore(hpcrun_meminfo_t *mi)
{
  hpcrun_mem_init();

  mi->mi_start = hpcrun_mmap_anon(memsize);
  if (mi->mi_start == NULL) {
    EMSG("%s: mmap failed, shutting down", __func__);
    monitor_real_abort();
  }

  mi->mi_size = memsize;
  mi->mi_low = mi->mi_start;
  mi->mi_high = mi->mi_start + memsize;
  num_segments++;

  TMSG(MALLOC, "new memstore: [%p, %p)", mi->mi_start, mi->mi_high);
}

// Reclaim the freeable CCT memory at the low end.
void
hpcrun_reclaim_freeable_mem(void)
{
  hpcrun_meminfo_t *mi = &TD_GET(memstore);

  mi->mi_low = mi->mi_start;
  num_reclaims++;
}

//
// Returns: address of non-freeable region at the high end,
// else NULL on failure.
//
void *
csprof_malloc(size_t size)
{
  hpcrun_meminfo_t *mi = &TD_GET(memstore);
  void *addr;

  // Non-recoverable out of memory.
  if (mi->mi_high < mi->mi_start + low_memsize) {<commit_msg>csprof_malloc(): add special case for (size == 0).  [This is useful for a BalancedTree with node-data size 0.]<commit_after>// -*-Mode: C++;-*-
// $Id$
//
// The new memory allocator.  We mmap() a large, single region (6 Meg)
// per thread and dole out pieces via csprof_malloc().  Pieces are
// either freeable (CCT nodes) or not freeable (everything else).
// When memory gets low, we write out an epoch and reclaim the CCT
// nodes.
//

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "csprof-malloc.h"
#include "env.h"
#include "monitor.h"
#include "newmem.h"
#include "pmsg.h"
#include "sample_event.h"
#include "thread_data.h"

#define DEFAULT_MEMSIZE   (6 * 1024 * 1024)
#define BGP_MEMSIZE      (10 * 1024 * 1024)
#define MIN_LOW_MEMSIZE  (80 * 1024)
#define DEFAULT_PAGESIZE  4096

static size_t memsize = DEFAULT_MEMSIZE;
static size_t low_memsize = MIN_LOW_MEMSIZE;
static size_t pagesize = DEFAULT_PAGESIZE;
static int allow_extra_mmap = 1;

static long num_segments = 0;
static long num_reclaims = 0;
static long num_failures = 0;
static long total_freeable = 0;
static long total_non_freeable = 0;

static int out_of_mem_mesg = 0;

//------------------------------------------------------------------
// Internal functions
//------------------------------------------------------------------

static inline size_t
round_up(size_t size)
{
  return (size + 7) & ~7L;
}

static inline size_t
hpcrun_align_pagesize(size_t size)
{
  return ((size + pagesize - 1)/pagesize) * pagesize;
}

// Look up environ variables and pagesize.
static void
hpcrun_mem_init(void)
{
  static int init_done = 0;
  char *str;
  long ans;

  if (init_done)
    return;

#ifdef _SC_PAGESIZE
  if ((ans = sysconf(_SC_PAGESIZE)) > 0) {
    pagesize = ans;
  }
#endif

#ifdef HOST_SYSTEM_IBM_BLUEGENE
  memsize = BGP_MEMSIZE;
  allow_extra_mmap = 0;
#endif

  str = getenv(HPCRUN_MEMSIZE);
  if (str != NULL && sscanf(str, "%ld", &ans) == 1) {
    memsize = hpcrun_align_pagesize(ans);
  }

  str = getenv(HPCRUN_LOW_MEMSIZE);
  if (str != NULL && sscanf(str, "%ld", &ans) == 1) {
    low_memsize = ans;
  } else {
    low_memsize = memsize/25;
    if (low_memsize < MIN_LOW_MEMSIZE)
      low_memsize = MIN_LOW_MEMSIZE;
  }

  TMSG(MALLOC, "%s: pagesize = %ld, memsize = %ld, "
       "low memsize = %ld, extra mmap = %d",
       __func__, pagesize, memsize, low_memsize, allow_extra_mmap);
  init_done = 1;
}

//
// Returns: address of mmap-ed region, else NULL on failure.
//
// Note: we leak fds in the rare case of a process that creates many
// threads running on a system that doesn't allow MAP_ANON.
//
static void *
hpcrun_mmap_anon(size_t size)
{
  int prot, flags, fd;
  char *str;
  void *addr;

  size = hpcrun_align_pagesize(size);
  prot = PROT_READ | PROT_WRITE;
  fd = -1;

#if defined(MAP_ANON)
  flags = MAP_PRIVATE | MAP_ANON;
#elif defined(MAP_ANONYMOUS)
  flags = MAP_PRIVATE | MAP_ANONYMOUS;
#else
  flags = MAP_PRIVATE;
  fd = open("/dev/zero", O_RDWR);
  if (fd < 0) {
    str = strerror(errno);
    EMSG("%s: open /dev/null failed: %s", __func__, str);
    return NULL;
  }
#endif

  addr = mmap(NULL, size, prot, flags, fd, 0);
  if (addr == MAP_FAILED) {
    str = strerror(errno);
    EMSG("%s: mmap failed: %s", __func__, str);
    addr = NULL;
  }

  TMSG(MALLOC, "%s: size = %ld, fd = %d, addr = %p",
       __func__, size, fd, addr);
  return addr;
}

//------------------------------------------------------------------
// External functions
//------------------------------------------------------------------

//
// memstore layout:
//
//   +------------------+------------+----------------+
//   |  freeable (cct)  |            |  non-freeable  |
//   +------------------+------------+----------------+
//   mi_start           mi_low       mi_high
//

// Allocate space and init a thread's memstore.
void
hpcrun_make_memstore(hpcrun_meminfo_t *mi)
{
  hpcrun_mem_init();

  mi->mi_start = hpcrun_mmap_anon(memsize);
  if (mi->mi_start == NULL) {
    EMSG("%s: mmap failed, shutting down", __func__);
    monitor_real_abort();
  }

  mi->mi_size = memsize;
  mi->mi_low = mi->mi_start;
  mi->mi_high = mi->mi_start + memsize;
  num_segments++;

  TMSG(MALLOC, "new memstore: [%p, %p)", mi->mi_start, mi->mi_high);
}

// Reclaim the freeable CCT memory at the low end.
void
hpcrun_reclaim_freeable_mem(void)
{
  hpcrun_meminfo_t *mi = &TD_GET(memstore);

  mi->mi_low = mi->mi_start;
  num_reclaims++;
}

//
// Returns: address of non-freeable region at the high end,
// else NULL on failure.
//
void *
csprof_malloc(size_t size)
{
  // Special case
  if (size == 0) {
    return NULL;
  }

  hpcrun_meminfo_t *mi = &TD_GET(memstore);
  void *addr;

  // Non-recoverable out of memory.
  if (mi->mi_high < mi->mi_start + low_memsize) {
{'fraction_non_alphanumeric': 0.20424870466321243, 'fraction_numerical': 0.010880829015544042, 'mean_word_length': 2.4916787264833573, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4413130', 'n_tokens_mistral': 3685, 'n_tokens_neox': 3532, 'n_words': 931}