File memory_monitor_sr.cpp¶
File List > base > memory_monitor_sr.cpp
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.cpp - Implementation of memory monitor module
*/
#if !defined(WINDOWS)
#include <cassert>
#include <cstring>
#include <algorithm>
#include "memory_monitor_sr.hpp"
bool mmon_disabled = true;
namespace cubmem
{
std::atomic<uint64_t> m_stat_map[MMON_MAP_RESERVE_SIZE] = {};
memory_monitor::memory_monitor (const char *server_name)
: m_server_name {server_name},
m_stat_name_map {MMON_MAP_RESERVE_SIZE},
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
m_stat_alloc_count {},
m_stat_free_count {},
m_total_alloc_count {0},
m_total_free_count {0},
m_stat_memory_peak {},
m_total_memory_peak {0},
#endif
m_total_mem_usage {0},
m_meta_alloc_count {0},
m_magic_number {*reinterpret_cast <const int *> ("MMON")}
{
std::string filecopy (__FILE__);
std::string target ("/src/");
// Find the last occurrence of "src" in the path
size_t pos = filecopy.rfind (target);
if (pos != std::string::npos)
{
m_target_pos = pos + target.length ();
}
else
{
m_target_pos = 0;
}
}
size_t memory_monitor::get_allocated_size (char *ptr)
{
size_t allocated_size = 0;
allocated_size = malloc_usable_size ((void *)ptr);
if (allocated_size <= MMON_METAINFO_SIZE)
{
return allocated_size;
}
char *meta_ptr = get_metainfo_pos (ptr, allocated_size);
const MMON_METAINFO *metainfo = (const MMON_METAINFO *) meta_ptr;
if (metainfo->magic_number == m_magic_number)
{
allocated_size = (size_t) metainfo->allocated_size - MMON_METAINFO_SIZE;
}
return allocated_size;
}
#if !defined (NDEBUG) && (MMON_DEBUG_LEVEL > 1)
/* This section is for tracking errors of memory monitoring modules.
*
* The m_error_tracking_map uses the pointer address of metainfo as the key
* and MMON_DEBUG_INFO containing necessary debugging information as the value,
* and operates as follows:
*
* add_stat(): If the key exists, it checks the current usage status (is_exist)
* for that key. If the is_exist flag is set, it classifies it as
* an error case.
* If the key does not exist, it inserts the current information into
* MMON_DEBUG_INFO and adds it to the map.
*
* sub_stat(): If the key exists, it unset the is_exist flag for that key.
* If the key does not exist, it checks if the magic number matches.
* This is because even when memory allocated normally at the outside of the scope
* can come inside to the sub_stat() and go through this error check routine.
* If the magic number also matches, it considers the metadata to be corrupted.
*
* There are three cases of tracking error:
* 1. Tracking hole
* 2. Memory double allocation (unreachable)
* 3. Metainfo corrupted
* */
void memory_monitor::check_add_stat_tracking_error_is_exist (MMON_METAINFO *metainfo, const char *file, int line)
{
intptr_t ptr_key = reinterpret_cast <intptr_t> (metainfo);
auto debug_search = m_error_tracking_map.find (ptr_key);
if (debug_search != m_error_tracking_map.end ())
{
if (debug_search->second.is_exist)
{
// Case1. Reveal tracking hole
// This case catch the tracking holes that can occur during tracking.
// These holes can occur in the following situation:
// cub_alloc() -> free() -> cub_alloc()
// In sub_stat(), called from cub_free(), the is_exist flag
// should be disabled, but if default free() is called,
// the memory is deallocated without unset the flag.
// If cub_alloc() is then called to reuse that memory, it is considered
// an error in memory tracking.
fprintf (stderr, "metainfo pointer %p is already allocated by %s:%d "
"but %s:%d is the allocation request of this round\n", metainfo,
debug_search->second.filename, debug_search->second.line, file, line);
fflush (stderr);
assert (false);
}
else
{
sprintf (debug_search->second.filename, "%s", file);
debug_search->second.line = line;
debug_search->second.is_exist = true;
}
}
else
{
MMON_DEBUG_INFO debug_info;
sprintf (debug_info.filename, "%s", file);
debug_info.line = line;
debug_info.is_exist = true;
std::pair<tbb::concurrent_unordered_map<intptr_t, MMON_DEBUG_INFO>::iterator, bool> debug_insert;
debug_insert = m_error_tracking_map.insert (std::pair <intptr_t, MMON_DEBUG_INFO> (ptr_key, debug_info));
if (!debug_insert.second)
{
// Case2. Double allocation (unreachable)
// This case is not reached in normal memory allocation situations and
// indicates a problem in the default allocation mechanism,
// such as malloc(), if it is reached.
fprintf (stderr, "double memory allocation is occurred\n");
fflush (stderr);
assert (false);
}
}
}
void memory_monitor::check_sub_stat_tracking_error_is_exist (MMON_METAINFO *metainfo)
{
intptr_t ptr_key = reinterpret_cast <intptr_t> (metainfo);
auto debug_search = m_error_tracking_map.find (ptr_key);
if (debug_search != m_error_tracking_map.end())
{
if (debug_search->second.is_exist)
{
debug_search->second.is_exist = false;
}
else
{
if (metainfo->magic_number == m_magic_number)
{
// Case3. Metainfo corrupted
// This case indicates that the metadata information owned
// by the memory_monitor class for tracking has been corrupted.
// This can occur in two scenarios:
//
// - Overflow occurring in the memory allocated through cub_alloc(),
// corrupting the metadata space.
// - Memory allocated through cub_alloc() is deallocated without
// erasing the metadata information via default free(), and then
// deallocated through cub_free after being reallocated through basic
// allocation functions like malloc().
//
// In the second scenario, even if a different size is allocated compared
// to the first allocation, the position of the pointer storing the metadata
// information may not change. The position of the metadata is determined by
// malloc_usable_size(), which can return a larger value than the size
// requested by the user. This is because the OS allocates memory in chunks
// rather than exactly as much as the user requested. Thus, even if the user
// receives the same chunk through the basic allocation function,
// the amount of memory usable by the user can be larger, indicating
// potential corruption of the metadata space.
fprintf (stderr, "Metainfo is corrupted by some reason.\n");
fflush (stderr);
assert (false);
}
}
}
}
#endif
void memory_monitor::aggregate_server_info (MMON_SERVER_INFO &server_info)
{
strncpy (server_info.server_name, m_server_name.c_str (), m_server_name.size () + 1);
server_info.total_mem_usage = m_total_mem_usage.load ();
server_info.total_metainfo_mem_usage = m_meta_alloc_count * MMON_METAINFO_SIZE;
server_info.num_stat = m_stat_name_map.size ();
for (const auto &[stat_name, stat_id] : m_stat_name_map)
{
// m_stat_map[stat_id] means memory usage
server_info.stat_info.emplace_back (stat_name, m_stat_map[stat_id].load ());
}
// This funciton is for sorting the vector in descending order by memory usage
const auto &comp = [] (const auto &stat_pair1, const auto &stat_pair2)
{
return stat_pair1.second > stat_pair2.second;
};
std::sort (server_info.stat_info.begin (), server_info.stat_info.end (), comp);
}
void memory_monitor::finalize_dump ()
{
double mem_usage_ratio = 0.0;
FILE *outfile_fp = fopen ("finalize_dump.txt", "w+");
MMON_SERVER_INFO server_info;
aggregate_server_info (server_info);
fprintf (outfile_fp, "====================cubrid memmon====================\n");
fprintf (outfile_fp, "Server Name: %s\n", server_info.server_name);
fprintf (outfile_fp, "Total Memory Usage(Bytes): %lu\n\n", server_info.total_mem_usage);
fprintf (outfile_fp, "Total Metainfo Memory Usage(Bytes): %lu\n\n",
server_info.total_metainfo_mem_usage);
fprintf (outfile_fp, "-----------------------------------------------------\n");
fprintf (outfile_fp, "\t%-100s | %17s(%s)\n", "File Name", "Memory Usage", "Ratio");
for (const auto &[stat_name, mem_usage] : server_info.stat_info)
{
if (server_info.total_mem_usage != 0)
{
mem_usage_ratio = mem_usage / (double) server_info.total_mem_usage;
mem_usage_ratio *= 100;
}
fprintf (outfile_fp, "\t%-100s | %17lu(%3d%%)\n", stat_name.c_str (), mem_usage,
(int)mem_usage_ratio);
}
fprintf (outfile_fp, "-----------------------------------------------------\n");
fflush (outfile_fp);
fclose (outfile_fp);
}
#if (MMON_DEBUG_LEVEL == 1) || (MMON_DEBUG_LEVEL == 3)
void memory_monitor::print_debug_result ()
{
double mem_usage_ratio = 0.0;
MMON_SERVER_INFO server_info;
std::vector<uint64_t> stat_alloc_count_vector;
std::vector<uint64_t> stat_free_count_vector;
std::vector<uint64_t> stat_peak_vector;
char filename[MMON_MAX_NAME_LENGTH * 2];
FILE *outfile_fp = NULL;
(void) envvar_logdir_file (filename, MMON_MAX_NAME_LENGTH * 2, "/server/mmon_debug_result.txt");
outfile_fp = fopen (filename, "w+");
if (outfile_fp == NULL)
{
fprintf (stdout, "fopen() error. Creating mmon_debug_result.txt failed\n");
fflush (stdout);
return;
}
auto MMON_CONVERT_TO_KB_SIZE = [] (uint64_t size)
{
return ((size) / 1024);
};
// get stat information of memory_monitor like aggregate_server_info ()
strncpy (server_info.server_name, m_server_name.c_str (), m_server_name.size () + 1);
server_info.total_mem_usage = m_total_mem_usage.load ();
server_info.total_metainfo_mem_usage = m_meta_alloc_count * MMON_METAINFO_SIZE;
server_info.num_stat = m_stat_name_map.size ();
for (const auto &[stat_name, stat_id] : m_stat_name_map)
{
// m_stat_map[stat_id] means memory usage
server_info.stat_info.emplace_back (stat_name, m_stat_map[stat_id].load ());
// save debug result with the same order of server_info.stat_info
stat_alloc_count_vector.emplace_back (m_stat_alloc_count[stat_id].load ());
stat_free_count_vector.emplace_back (m_stat_free_count[stat_id].load ());
stat_peak_vector.emplace_back (m_stat_memory_peak[stat_id]);
}
fprintf (outfile_fp, "====================cubrid memmon====================\n");
fprintf (outfile_fp, "Server Name: %s\n", server_info.server_name);
fprintf (outfile_fp, "Total Memory Usage(KB): %lu\n\n", MMON_CONVERT_TO_KB_SIZE (server_info.total_mem_usage));
fprintf (outfile_fp, "Total Metainfo Memory Usage(KB): %lu\n\n",
MMON_CONVERT_TO_KB_SIZE (server_info.total_metainfo_mem_usage));
fprintf (outfile_fp, "Total Alloc Count: %lu\n", m_total_alloc_count.load ());
fprintf (outfile_fp, "Total Dealloc Count: %lu\n", m_total_free_count.load ());
fprintf (outfile_fp, "Total Peak Memory(KB): %lu\n", MMON_CONVERT_TO_KB_SIZE (m_total_memory_peak));
fprintf (outfile_fp, "-----------------------------------------------------\n");
fprintf (outfile_fp, "\t%-50s | %10s | %10s | %10s\n", "File Name", "Alloc Cnt", "Free Cnt", "Peak Mem");
int cnt = 0;
for (const auto &[stat_name, mem_usage] : server_info.stat_info)
{
fprintf (outfile_fp, "\t%-50s | %10lu | %10lu | %10lu\n",stat_name.c_str (),
stat_alloc_count_vector[cnt], stat_free_count_vector[cnt],
MMON_CONVERT_TO_KB_SIZE (stat_peak_vector[cnt]));
cnt++;
}
fprintf (outfile_fp, "-----------------------------------------------------\n");
fflush (outfile_fp);
fclose (outfile_fp);
}
#endif
}
#endif // !WINDOWS