Skip to content

File memory_monitor_sr.hpp

File List > base > memory_monitor_sr.hpp

Go to the documentation of this file

/*
 *
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * memory_monitor_sr.hpp - Declaration of APIs and structures, classes
 *                         for memory monitoring module
 */

#ifndef _MEMORY_MONITOR_SR_HPP_
#define _MEMORY_MONITOR_SR_HPP_

#if !defined(WINDOWS)
#include <cstdio>
#include <atomic>

#include "concurrent_unordered_map.h"
#include "memory_monitor_common.hpp"

#ifndef MMON_DEBUG_LEVEL
/* 0: not debug
 * 1: debug alloc count
 * 2: debug tracking error
 * 3: debug all
 */
#define MMON_DEBUG_LEVEL 0
#endif

#if defined(__SVR4)
extern "C" size_t malloc_usable_size (void *);
#elif defined(__APPLE__)
#include <malloc/malloc.h>

#ifndef HAVE_USR_INCLUDE_MALLOC_H
#define HAVE_USR_INCLUDE_MALLOC_H
#endif
#elif defined(__linux__)
#include <malloc.h>

#ifndef HAVE_USR_INCLUDE_MALLOC_H
#define HAVE_USR_INCLUDE_MALLOC_H
#endif
#endif

#define MMON_MAX_NAME_LENGTH 255

typedef struct mmon_metainfo MMON_METAINFO;
struct mmon_metainfo
{
  uint64_t allocated_size;
  int stat_id;
  int magic_number;
};

#if !defined (NDEBUG) && (MMON_DEBUG_LEVEL > 1)
typedef struct mmon_debug_info MMON_DEBUG_INFO;
struct mmon_debug_info
{
  char filename[255];
  int line;
  bool is_exist;
};
#endif

extern bool mmon_disabled;

namespace cubmem
{
  // IMPORTANT!!
  // This meta size is related with allocation byte align
  // Don't adjust it freely
  // 8 byte size + 4 byte stat + 4 byte magicnumber
  static constexpr int MMON_METAINFO_SIZE = 16;

  // Because of performance optimization, we have to reserve the size of the map(or mapping array).
  // We think that the number of entry of map(or mapping array) cannot over 8K.
  static constexpr int MMON_MAP_RESERVE_SIZE = 8192;

  extern std::atomic<uint64_t> m_stat_map[MMON_MAP_RESERVE_SIZE];

  class memory_monitor
  {
    public:
      memory_monitor (const char *server_name);
      ~memory_monitor () {}

      memory_monitor (const memory_monitor &) = delete;
      memory_monitor (memory_monitor &&) = delete;

      memory_monitor &operator = (const memory_monitor &) = delete;
      memory_monitor &operator = (memory_monitor &&) = delete;

    public:
      size_t get_allocated_size (char *ptr);
      inline int get_target_pos ();
      inline void add_stat (char *ptr, const size_t size, const char *file, const int line);
      inline void sub_stat (char *ptr);
      void aggregate_server_info (MMON_SERVER_INFO &server_info);
      void finalize_dump ();
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
      void print_debug_result ();
#endif

    private:
      inline char *get_metainfo_pos (char *ptr, size_t size);
      inline void make_stat_name (char *buf, const char *file, const int line);
#if !defined (NDEBUG) && (MMON_DEBUG_LEVEL > 1)
      void check_add_stat_tracking_error_is_exist (MMON_METAINFO *metainfo, const char *file, int line);
      void check_sub_stat_tracking_error_is_exist (MMON_METAINFO *metainfo);
#endif

    private:
      std::string m_server_name;
      // Entries of m_stat_name_map and m_stat_map will not be deleted
      tbb::concurrent_unordered_map <std::string, int> m_stat_name_map;        // key: stat name, value: stat id
#if !defined (NDEBUG) && (MMON_DEBUG_LEVEL > 1)
      tbb::concurrent_unordered_map <intptr_t, MMON_DEBUG_INFO> m_error_tracking_map;
#endif
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
      std::atomic <uint64_t> m_stat_alloc_count[MMON_MAP_RESERVE_SIZE];
      std::atomic <uint64_t> m_stat_free_count[MMON_MAP_RESERVE_SIZE];
      std::atomic <uint64_t> m_total_alloc_count;
      std::atomic <uint64_t> m_total_free_count;
      uint64_t m_stat_memory_peak[MMON_MAP_RESERVE_SIZE];
      uint64_t m_total_memory_peak;
#endif
      std::atomic <uint64_t> m_total_mem_usage;
      std::atomic <int> m_meta_alloc_count;                         // for checking occupancy of memory used by metainfo space
      int m_target_pos;
      // Magic number is for checking an allocated memory which is out-of-scope of memory_monitor.
      // It's because memory_monitor starts to manage information about heap memory allocation
      // not "right after cubrid server starts" but "after some allocations are occurred because of
      // memory_monitor has some dependencies to start (e.g. system parameter, error file initialize, etc..).
      // And memory_monitor also can't manage some allocations after it is started like allocations at C++ containers(STL),
      // and some C++ allocations occurred at header files.
      const int m_magic_number;
  };

  extern memory_monitor *mmon_Gl;

  inline int memory_monitor::get_target_pos ()
  {
    return m_target_pos;
  }

  inline void memory_monitor::make_stat_name (char *buf, const char *file, const int line)
  {
    assert (strlen (file + m_target_pos) + std::to_string (line).length() + 1 <= MMON_MAX_NAME_LENGTH);
    snprintf (buf, MMON_MAX_NAME_LENGTH, "%s:%d", file + m_target_pos, line);
  }

  inline char *memory_monitor::get_metainfo_pos (char *ptr, size_t size)
  {
    return ptr + size - MMON_METAINFO_SIZE;
  }

  inline void memory_monitor::add_stat (char *ptr, const size_t size, const char *file, const int line)
  {
    char stat_name[MMON_MAX_NAME_LENGTH];

    // size should not be 0 because of MMON_METAINFO_SIZE
    assert (size > 0);

    MMON_METAINFO *metainfo = (MMON_METAINFO *) get_metainfo_pos (ptr, size);

    metainfo->allocated_size = (uint64_t) size;
    m_total_mem_usage += metainfo->allocated_size;
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
    // check total allocated memory peak
    if (m_total_mem_usage.load () > m_total_memory_peak)
      {
    m_total_memory_peak = m_total_mem_usage.load ();
      }
#endif

    make_stat_name (stat_name, file, line);

retry:
    const auto search = m_stat_name_map.find (stat_name);
    if (search != m_stat_name_map.end ())
      {
    metainfo->stat_id = search->second;
      }
    else
      {
    std::pair<tbb::concurrent_unordered_map<std::string, int>::iterator, bool> insert_success;
    metainfo->stat_id = m_stat_name_map.size ();
    assert (metainfo->stat_id < MMON_MAP_RESERVE_SIZE);
    // stat_id starts with 0
    insert_success = m_stat_name_map.insert (std::pair <std::string, int> (stat_name, metainfo->stat_id));
    if (!insert_success.second)
      {
        goto retry;
      }
      }
    m_stat_map[metainfo->stat_id] += metainfo->allocated_size;
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
    // check stat allocated memory peak
    uint64_t stat_size = m_stat_map[metainfo->stat_id].load ();
    if (stat_size > m_stat_memory_peak[metainfo->stat_id])
      {
    m_stat_memory_peak[metainfo->stat_id] = stat_size;
      }
    // add alloc count
    m_stat_alloc_count[metainfo->stat_id]++;
    m_total_alloc_count++;
#endif

    // put meta info into the allocated chunk
    metainfo->magic_number = m_magic_number;
    m_meta_alloc_count++;
#if !defined (NDEBUG) && (MMON_DEBUG_LEVEL > 1)
    check_add_stat_tracking_error_is_exist (metainfo, file, line);
#endif
  }

  inline void memory_monitor::sub_stat (char *ptr)
  {
    size_t allocated_size = malloc_usable_size ((void *)ptr);

    if (allocated_size >= MMON_METAINFO_SIZE)
      {
    MMON_METAINFO *metainfo = (MMON_METAINFO *) get_metainfo_pos (ptr, allocated_size);

#if !defined (NDEBUG) && (MMON_DEBUG_LEVEL > 1)
    check_sub_stat_tracking_error_is_exist (metainfo);
#endif
    if (metainfo->magic_number == m_magic_number)
      {
        assert (metainfo->stat_id >= 0 && metainfo->stat_id < MMON_MAP_RESERVE_SIZE);
        assert (m_total_mem_usage >= metainfo->allocated_size && m_stat_map[metainfo->stat_id] >= metainfo->allocated_size);
        assert (metainfo->allocated_size == allocated_size);

        m_total_mem_usage -= metainfo->allocated_size;
        m_stat_map[metainfo->stat_id] -= metainfo->allocated_size;

        metainfo->magic_number = 0;
        m_meta_alloc_count--;
        assert (m_meta_alloc_count >= 0);
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
        m_stat_free_count[metainfo->stat_id]++;
        m_total_free_count++;
#endif
      }
      }
  }
} //namespace cubmem

extern int mmon_initialize (const char *server_name);
extern void mmon_finalize ();
extern size_t mmon_get_allocated_size (char *ptr);
extern void mmon_aggregate_server_info (MMON_SERVER_INFO &server_info);

inline bool mmon_is_memory_monitor_enabled ()
{
  return (!mmon_disabled);
}

inline void mmon_add_stat (char *ptr, const size_t size, const char *file, const int line)
{
  cubmem::mmon_Gl->add_stat (ptr, size, file, line);
}

inline void mmon_sub_stat (char *ptr)
{
  assert (ptr != NULL);

  cubmem::mmon_Gl->sub_stat (ptr);
}
#endif // !WINDOWS
#endif // _MEMORY_MONITOR_SR_HPP_