Newer
Older
libkc / modules / include / kc_memory.h
/**
 * @file   kc_memory.h
 * @brief  KC メモリ管理モジュール
 * @copyright  2003 - 2023  Nomura Kei
 * @depends
 *	kc.h
 *	kc_memory_mark.h
 *  kc_memory_entry.h
 *  kc_memory_listener.h
 *
 * 利用例)
 * #include <kc_memory.h>
 * int main(void)
 * {
 *    // プログラム終了時に発生しているメモリリーク情報を出力します。
 *    // 引数が true の場合、メモリ確保/解放情報を出力します。
 *    KcMemory_start(true);
 *    ...(省略)...
 *    return 0;
 * }
 */
#ifndef KC_MEMORY_H
#define KC_MEMORY_H

#include <threads.h>

#include <kc.h>
#include <kc_memory_mark.h>
#include <kc_memory_entry.h>
#include <kc_memory_listener.h>

#ifdef __cplusplus
extern "C"
{
	namespace kc
	{
		using namespace std;
#endif

// =============================================================================
//  KcMemoryManager
// =============================================================================

/** メモリ管理で扱うバッファサイズ			*/
#define KC_MEMORY_MAX_BUFFER_SIZE (4096)

		/**
		 * メモリ管理を開始します。
		 *
		 * 本関数を実行することで、次の動作が実施されます。
		 * (1) メモリ確保失敗時に、エラー情報を出力します。
		 * (2) プログラム終了時に、解放されていないメモリを出力します。
		 * (3) detail が true の場合、メモリ確保/解放時に出力します。
		 * ※出力先は、いずれも stderr となります。
		 *
		 * @param detail true/false (メモリ確保/解放の情報を出力する/出力しない)
		 */
		void KcMemory_start(bool detail);

		/**
		 * 現在確保去れているメモリ一覧を出力します。
		 */
		void KcMemory_dump(void);

		/**
		 * メモリの確保、解放を管理します。
		 */
		typedef struct KcMemoryManager_
		{
			/**
			 * メモリ確保、解放、エラー発生時に通知するためのリスナを登録します。
			 * @code
			 * 例)
			 * void Listener_allocate(const KcMemoryEntry* entry) {
			 *   printf("メモリ確保! : %s:%d\n", entry->file, entry->line);
			 * }
			 * void Listener_free(const KcMemoryEntry* entry) {
			 *   printf("メモリ解放! : %s:%d\n", entry->file, entry->line);
			 * }
			 * void Listener_error(const KcMemoryEntry* entry, const char* msg) {
			 *   printf("エラー! : %s", msg);
			 * }
			 *
			 * int main (void) {
			 *   KcMemoryListener listener = {
			 *     .allocate = Listener_allocate,
			 *     .free = Listener_free,
			 *     .error = Listener_error
			 *   };
			 *   kc_memory_manager->set_listener(&listener);
			 *   ...(省略)...
			 *   return 0;
			 * }
			 * @endcode
			 *
			 * @param listener 登録するリスナ
			 * @return true/false (リスナ登録成功/リスナ登録失敗)
			 */
			bool (*set_listener)(KcMemoryListener *listener);

			/**
			 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
			 * ハンドラの戻りが false の場合、呼び出しを終了します。
			 *
			 * @param handler ハンドラ
			 * @param info    ハンドラの第二引数に渡される付加情報
			 * @return true/false (実行成功/実行失敗)
			 */
			bool (*entries)(bool (*handler)(const KcMemoryEntry *entry, void *info), void *info);

			/**
			 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
			 * ハンドラの戻り値が true の場合、該当するメモリを解放します。
			 * new で確保されたメモリが解放される際、デストラクタは呼び出されないため注意が必要です。
			 *
			 * @param handler ハンドラ
			 * @param info    ハンドラの第二引数に渡される付加情報
			 * @return true 固定
			 */
			bool (*freeif)(bool (*handler)(const KcMemoryEntry *entry, void *info), void *info);

			/**
			 * 管理しているメモリ情報のダンプデータを引数に指定されたハンドラを実行します。
			 *
			 * @param handler ハンドラ
			 * @param byte    ダンプするバイト数
			 * @param binary  true の場合、バイナリの16進数表現がダンプデータに追加されます。
			 * @param ascii   true の場合、ASCIIがダンプデータに追加されます。
			 * @param column  カラム数
			 */
			void (*dump)(bool (*handler)(const char *data),
						 int bytes, bool binary, bool ascii, int column);

			/**
			 * 指定されたサイズのメモリを確保します。
			 *
			 * @param size  確保するメモリサイズ
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void *(*malloc)(size_t size, const char *file, const char *func, int line);

			/**
			 * アライメント指定付きで、指定されたサイズのメモリを確保します。
			 *
			 * @param alignemnt アライメント
			 * @param size  確保するメモリサイズ
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line);

			/**
			 * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。
			 * 確保したメモリ領域の内容は、0x00 で初期化されます。
			 *
			 * @param nmemb  確保するメモリ要素数
			 * @param size   1要素あたりのメモリサイズ
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void *(*calloc)(size_t nmemb, size_t size, const char *file, const char *func, int line);

			/**
			 * 指定されたポインタが指すメモリサイズを変更します。
			 *
			 * @param ptr   メモリサイズを変更するポインタ
			 * @param size  変更後のメモリサイズ
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void *(*realloc)(void *ptr, size_t size, const char *file, const char *func, int line);

			/**
			 * 指定されたメモリを解放します。
			 *
			 * @param ptr 解放するメモリへのポインタ
			 */
			void (*free)(void *ptr);

			// =========================================================================
			//  内部利用関数
			// =========================================================================

			/**
			 * [内部利用関数]
			 * KcMemoryManager を初期化します。
			 * 本関数は、KcMemoryManager の各関数が実行される際に呼び出され、
			 * 一度だけ初期化処理を実施します。
			 */
			void (*_init)(void);

			bool (*_add)(KcMemoryEntry *entry);
			bool (*_remove)(KcMemoryEntry *entry);
			void *(*_allocate)(size_t alignment, size_t size,
							   KcMemoryMark mark, const char *file, const char *func, int line);
			void *(*_reallocate)(void *ptr, size_t size,
								 KcMemoryMark mark, const char *file, const char *func, int line);
			void *(*_reallocate_managed_ptr)(void *ptr, size_t size,
											 KcMemoryMark mark, const char *file, const char *func, int line);
			void *(*_reallocate_invalid_ptr)(void *ptr, size_t size,
											 KcMemoryMark mark, const char *file, const char *func, int line);
			void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size,
											   KcMemoryMark mark, const char *file, const char *func, int line);
			void (*_deallocate)(void *ptr, KcMemoryMark expected_mark);

			// =========================================================================
			//  内部利用変数
			// =========================================================================
			KcMemoryListener _listener;				 //!< リスナ
			KcMemoryEntry _head;					 //!< 管理メモリの先頭
			KcMemoryEntry _tail;					 //!< 管理メモリの末尾
			KcMemoryEntry _error;					 //!< エラー発生時一時利用
			char _tmpbuf[KC_MEMORY_MAX_BUFFER_SIZE]; //!< 一時利用のためのバッファ
			mtx_t *_mutex;							 //!< 同期実行利用のための Mutex

		} KcMemoryManager;

		/**
		 * KcMemoryManager の唯一のインスタンス。
		 */
		extern KcMemoryManager *const kc_memory_manager;

#ifdef KC_MEMORY_ENABLED
#define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__)
#define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__)
#define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__)
#define free(ptr) kc_memory_manager->free(ptr)
#else
#include <stdlib.h>
#endif

#ifdef __cplusplus
	} // namespace kc
} // extern "C"
#endif
#endif // KC_MEMORY_H