diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/modules/Makefile b/modules/Makefile index de1cfcf..7c1975e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = main +SUBDIRS = libkc main USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/modules/Makefile b/modules/Makefile index de1cfcf..7c1975e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = main +SUBDIRS = libkc main USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/modules/libkc/Makefile b/modules/libkc/Makefile index efd29fb..c51bae2 100644 --- a/modules/libkc/Makefile +++ b/modules/libkc/Makefile @@ -11,7 +11,7 @@ TOPDIR ?= ../.. RULEDIR ?= $(TOPDIR)/mk NAME = libkc -TARGET = $(NAME).so +TARGET = $(NAME).a SUBDIRS = USE_SO_VERSION = diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/modules/Makefile b/modules/Makefile index de1cfcf..7c1975e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = main +SUBDIRS = libkc main USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/modules/libkc/Makefile b/modules/libkc/Makefile index efd29fb..c51bae2 100644 --- a/modules/libkc/Makefile +++ b/modules/libkc/Makefile @@ -11,7 +11,7 @@ TOPDIR ?= ../.. RULEDIR ?= $(TOPDIR)/mk NAME = libkc -TARGET = $(NAME).so +TARGET = $(NAME).a SUBDIRS = USE_SO_VERSION = diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index 5f628ed..5a9aedc 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -5,6 +5,12 @@ #ifndef KC_H #define KC_H +// ============================================================================= +// 共通定義 +// ============================================================================= +#define UNUSED_VARIABLE(val) (void)(val) + + #if defined(__cplusplus) && (__cplusplus >= 201103L) // ============================================================================= // C++11 @@ -28,11 +34,6 @@ #error "suuports C11/C++1 or later" -// ============================================================================= -// 共通定義 -// ============================================================================= -#define UNUSED_VARIABLE(val) (void)(val) +#endif // C++11, C11, ERROR - -#endif // C11 #endif // KC_H diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/modules/Makefile b/modules/Makefile index de1cfcf..7c1975e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = main +SUBDIRS = libkc main USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/modules/libkc/Makefile b/modules/libkc/Makefile index efd29fb..c51bae2 100644 --- a/modules/libkc/Makefile +++ b/modules/libkc/Makefile @@ -11,7 +11,7 @@ TOPDIR ?= ../.. RULEDIR ?= $(TOPDIR)/mk NAME = libkc -TARGET = $(NAME).so +TARGET = $(NAME).a SUBDIRS = USE_SO_VERSION = diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index 5f628ed..5a9aedc 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -5,6 +5,12 @@ #ifndef KC_H #define KC_H +// ============================================================================= +// 共通定義 +// ============================================================================= +#define UNUSED_VARIABLE(val) (void)(val) + + #if defined(__cplusplus) && (__cplusplus >= 201103L) // ============================================================================= // C++11 @@ -28,11 +34,6 @@ #error "suuports C11/C++1 or later" -// ============================================================================= -// 共通定義 -// ============================================================================= -#define UNUSED_VARIABLE(val) (void)(val) +#endif // C++11, C11, ERROR - -#endif // C11 #endif // KC_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index e54c14f..0408072 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -17,29 +17,34 @@ #ifdef KC_MEMORY_ENABLED // メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) +#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kc_memory_free (ptr) #else #include -#endif // !KC_MEMORY_ENABLED +#endif // !KC_MEMORY_ENABLED // 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) +#define KC_MEMORY_MARK_HEAD (0x55AA5A00) #define KC_MEMORY_MARK_MASK (0xFFFFFF00) typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている + KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている + KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ + KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ } KcMemoryMark; +/** + * ダンプサイズ + */ +#define KC_MEMORY_DUMP_SIZE (16) + /** * 指定されたメモリ管理用種別マークが正しいか判定します。 @@ -55,26 +60,28 @@ */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ - // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) + const char* file; /*!< メモリ確保ファイル名 */ + const char* func; /*!< メモリ確保関数名 */ + int line; /*!< メモリ確保行番号 */ + int size; /*!< 確保サイズ */ + int _mark; /*!< 確保メモリ状態 */ + struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ + struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ + void* data; /*!< データ */ + // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; // ハンドラ関数ポインタ typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); - // プロトタイプ宣言 void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -void kc_memory_dump(int mode); +bool kc_memory_entries(KcMemoryHandler handler); +bool kc_memory_freeif(KcMemoryHandler handler); +void kc_memory_dump(void); +// 以下は、通常直接使用しません。 void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); @@ -82,7 +89,7 @@ #ifdef __cplusplus -} // namespace kc -} // extern "C" +} // namespace kc +} // extern "C" #endif -#endif // KC_MEMORY_H +#endif // KC_MEMORY_H diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/modules/Makefile b/modules/Makefile index de1cfcf..7c1975e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = main +SUBDIRS = libkc main USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/modules/libkc/Makefile b/modules/libkc/Makefile index efd29fb..c51bae2 100644 --- a/modules/libkc/Makefile +++ b/modules/libkc/Makefile @@ -11,7 +11,7 @@ TOPDIR ?= ../.. RULEDIR ?= $(TOPDIR)/mk NAME = libkc -TARGET = $(NAME).so +TARGET = $(NAME).a SUBDIRS = USE_SO_VERSION = diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index 5f628ed..5a9aedc 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -5,6 +5,12 @@ #ifndef KC_H #define KC_H +// ============================================================================= +// 共通定義 +// ============================================================================= +#define UNUSED_VARIABLE(val) (void)(val) + + #if defined(__cplusplus) && (__cplusplus >= 201103L) // ============================================================================= // C++11 @@ -28,11 +34,6 @@ #error "suuports C11/C++1 or later" -// ============================================================================= -// 共通定義 -// ============================================================================= -#define UNUSED_VARIABLE(val) (void)(val) +#endif // C++11, C11, ERROR - -#endif // C11 #endif // KC_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index e54c14f..0408072 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -17,29 +17,34 @@ #ifdef KC_MEMORY_ENABLED // メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) +#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kc_memory_free (ptr) #else #include -#endif // !KC_MEMORY_ENABLED +#endif // !KC_MEMORY_ENABLED // 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) +#define KC_MEMORY_MARK_HEAD (0x55AA5A00) #define KC_MEMORY_MARK_MASK (0xFFFFFF00) typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている + KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている + KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ + KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ } KcMemoryMark; +/** + * ダンプサイズ + */ +#define KC_MEMORY_DUMP_SIZE (16) + /** * 指定されたメモリ管理用種別マークが正しいか判定します。 @@ -55,26 +60,28 @@ */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ - // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) + const char* file; /*!< メモリ確保ファイル名 */ + const char* func; /*!< メモリ確保関数名 */ + int line; /*!< メモリ確保行番号 */ + int size; /*!< 確保サイズ */ + int _mark; /*!< 確保メモリ状態 */ + struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ + struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ + void* data; /*!< データ */ + // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; // ハンドラ関数ポインタ typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); - // プロトタイプ宣言 void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -void kc_memory_dump(int mode); +bool kc_memory_entries(KcMemoryHandler handler); +bool kc_memory_freeif(KcMemoryHandler handler); +void kc_memory_dump(void); +// 以下は、通常直接使用しません。 void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); @@ -82,7 +89,7 @@ #ifdef __cplusplus -} // namespace kc -} // extern "C" +} // namespace kc +} // extern "C" #endif -#endif // KC_MEMORY_H +#endif // KC_MEMORY_H diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 351a4c1..35cf7cc 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -33,7 +33,6 @@ #define KC_MEMORY_PADDING (sizeof(void*) * 2) - //////////////////////////////////////////////////////////////////////////////// // // 内部変数 @@ -45,20 +44,70 @@ static KcMemoryEntry kc_memory_tail; //data = handler; + + bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); + return is_executed; +} + + +/** + * kc_memory_entries で使用されるハンドラ。 + * + * @param entry エントリ(使用しない) + * @param msg メッセージ(使用しない) + * @return true(固定) + */ +static +bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +{ + KcMemoryHandler handler = (KcMemoryHandler) entry->data; + KcMemoryEntry* next_entry = kc_memory_head._next; + while (next_entry != &kc_memory_tail) + { + handler(next_entry, msg); + next_entry = next_entry->_next; + } + return true; +} + + +// freeif +/** + * 指定された handler に現在管理しているメモリエントリが順次渡されます。 + * handler にて、true を返したメモリが解放されます。 + * + * @param handler ハンドラ + * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + */ +bool kc_memory_freeif(KcMemoryHandler handler) +{ + kc_memory_init_entry(); + + // KcMemoryEntry のデータに handler を設定 + size_t size = sizeof(KcMemoryHandler); + KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + entry->data = handler; + + bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); + return is_executed; +} + + +/** + * kc_memory_freeif で使用されるハンドラ。 + * + * @param entry エントリ(使用しない) + * @param msg メッセージ(使用しない) + * @return true(固定) + */ +static +bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) +{ + KcMemoryHandler handler = (KcMemoryHandler) entry->data; + KcMemoryEntry* next_entry = kc_memory_head._next; + while (next_entry != &kc_memory_tail) + { + bool exec_free = handler(next_entry, msg); + next_entry = next_entry->_next; + if (exec_free) + { // メモリ解放 + kc_memory_free(next_entry->_prev->data); + } + } + return true; +} + + +/** + * 現在管理しているメモリ情報をダンプします。 + */ +void kc_memory_dump(void) +{ + (void) kc_memory_entries(kc_memory_dump_entry); +} + + +/** + * 指定されたメモリエントリの情報をダンプ出力します。 + * + * @param entry 出力するエントリ + * @param msg メッセージ + * @return true(固定) + */ +static +bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) +{ + UNUSED_VARIABLE(msg); + + printf("%-15s:%05d:%-15s (%5d) %s ", + entry->file, + entry->line, + entry->func, + entry->size, + kc_memory_strmark(entry->_mark)); + + // dump + kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); + + // ascii + printf(" | "); + kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); + + printf("\n"); + + return true; +} + + + //////////////////////////////////////////////////////////////////////////////// // @@ -112,10 +335,41 @@ // +// ============================================================================= +// メモリ確保解放 +// ============================================================================= + /** * 指定されたサイズのメモリを確保します。 - * メモリの確保方法は、realloc(void* ptr, size_t size) と同様となります。 + * + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + if (entry == NULL) + { // メモリ確保失敗 + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); + return NULL; + } + + kc_memory_set_entry(entry, size, mark, file, func, line); + kc_memory_add_entry(entry); + return (entry->data); +} + + +/** + * 指定されたポインタがさすメモリサイズを変更します。 + * ポインタ ptr が NULL の場合、kc_memory_allocate を呼び出します。 * * @param ptr メモリサイズを変更するメモリへのポインタ * @param size 確保するメモリサイズ @@ -126,27 +380,197 @@ * @return 確保したメモリへのポインタ */ static -void* kc_memory_allocate(void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +void* kc_memory_reallocate(void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) { - if (size == 0) - { // サイズが 0 のため free と等価 - kc_memory_free(ptr); - return NULL; + if (ptr == NULL) + { + return kc_memory_allocate(size, mark, file, func, line); } - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - errno = ENOMEM; - kc_memory_ehandler(entry, ""); - - + KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; + old_entry--; + switch (old_entry->_mark) + { + case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc + return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); + case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) + return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); + case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) + return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); + case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする + return kc_memory_allocate(size, mark, file, func, line); + default: // 管理外メモリの realloc + return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); } } + +/** + * 管理されたメモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_reallocate_managed_ptr( + void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + UNUSED_VARIABLE(ptr); + + KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; + old_entry--; + + kc_memory_remove_entry(old_entry); + KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + if (entry != NULL) + { // メモリ確保成功 + // メモリ管理リストに加えてポインタを返す。 + kc_memory_set_entry(entry, size, mark, file, func, line); + kc_memory_add_entry(entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行し、NULL を返す。 + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + + // 古いメモリ領域は残っているため、管理対象に戻す。 + kc_memory_add_entry(old_entry); + return NULL; + } +} + + +/** + * 管理外メモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_reallocate_unmanaged_ptr( + void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + // |<-- 新たな領域 ---------------->| + // +------------+-------------------+ + // | 元々の領域 | 追加分 + 管理領域 | + // +------------+-------------------+ + // ↓ + // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー + // ↓ + // +----------+------------+--------+ + // | 管理領域 | 元々の領域 | 追加分 | + // +----------+------------+--------+ + KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + if (entry != NULL) + { // メモリ確保成功 + // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー + // メモリ管理リストに加えてポインタを返す。 + memmove((entry + 1), entry, size); + kc_memory_set_entry(entry, size, mark, file, func, line); + kc_memory_add_entry(entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行し、NULL を返す。 + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + return NULL; + } +} + + +/** + * 不正なメモリ領域に対する realloc のエラー処理を実施します。 + * + * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_reallocate_invalid_ptr( + void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + UNUSED_VARIABLE(ptr); + + errno = EINVAL; + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); + return NULL; +} + + +/** + * 指定されたポインタの指すメモリ領域を解放します。 + * NULL が指定された場合なにもしません。 + * 管理されたメモリの場合、管理領域を合わせて解放します。 + * 管理外メモリの場合、free を実行します。 + * + * @param ptr 解放するメモリへのポインタ + */ static void kc_memory_deallocate(void* ptr) { + if (ptr == NULL) + { // NULL ポインタに対してはなにもしない + return; + } + + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; + switch (entry->_mark) + { + case KC_MEMORY_ALLOCATED: // 管理メモリ + kc_memory_deallocate_entry(entry); + break; + case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ + kc_memory_deallocate_entry(entry); + perror("kc memory : warning : please use delete"); + kc_memory_execute_ehandler(entry, "warning : please use delete"); + break; + case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ + kc_memory_deallocate_entry(entry); + perror("kc memory : warning : please use delete[]"); + kc_memory_execute_ehandler(entry, "warning : please use delete[]"); + break; + case KC_MEMORY_DELETED: // 削除済みメモリ + // Nothing to do. + break; + default: + free(ptr); + break; + } +} + + +/** + * 指定されたメモリ管理およびデータ領域を解放します。 + * + * @param entry 解放するメモリ管理領域へのポインタ + */ +static +void kc_memory_deallocate_entry(KcMemoryEntry* entry) +{ + (void) kc_memory_remove_entry(entry); + entry->_mark = KC_MEMORY_DELETED; + entry->size = 0; + free(entry); } @@ -154,6 +578,7 @@ // メモリエントリ管理 // ============================================================================= + /** * メモリ管理エントリ全体を初期化します。 */ @@ -164,9 +589,9 @@ if (!kc_memory_entry_initialized) { // メモリ管理エントリ初期化 - kc_memory_init_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_init_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_init_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); + kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); + kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); + kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; @@ -212,6 +637,12 @@ bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); if (is_executed) { + kc_memory_execute_ahandler(entry, "allocate memory"); + } + else + { + perror("kc memory : can't add entry"); + kc_memory_execute_ehandler(entry, "can't add entry"); } } @@ -224,8 +655,10 @@ * @return true (固定) */ static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, msg) +bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) { + UNUSED_VARIABLE(msg); + // [tail] の一つ前に挿入する。 entry->_next = &kc_memory_tail; entry->_prev = kc_memory_tail._prev; @@ -235,75 +668,50 @@ } - -// ============================================================================ -// 同期化 -// ============================================================================= - -/** 同期化のための mutex */ -static mtx_t kc_memory_mutex; -static bool kc_memory_mutex_init(void); -static bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg); - /** - * 同期化実現のための mutex を初期化します。 + * 指定されたエントリをメモリ管理のリストから削除します。 + * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 * - * @return true/false (初期化成功/失敗) - */ + * @param entry 削除するエントリ + */ static -bool kc_memory_mutex_init(void) +void kc_memory_remove_entry(KcMemoryEntry* entry) { - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain); - if (result == thrd_success) - { - kc_memory_mutex_initialized = true; - } - else - { - perror("kc memory : can't init mutex"); - } - } - return kc_memory_mutex_initilized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) + kc_memory_init_entry(); + bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); + if (is_executed) { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; + kc_memory_execute_fhandler(entry, "free memory"); } - return false; + else + { + perror("kc memory : can't remove entry"); + kc_memory_execute_ehandler(entry, "can't remove entry"); + } } +/** + * 指定されたエントリをメモリ管理のリストより削除します。 + * + * @param entry 削除するエントリ + * @param msg メッセージ (使用されません) + * @return true (固定) + */ +static +bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) +{ + UNUSED_VARIABLE(msg); + + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + return true; +} + + + // ============================================================================= -// 登録ハンドラの実行 +// ハンドラ実行 // ============================================================================= @@ -358,193 +766,151 @@ } +// ============================================================================ +// 同期実行 // ============================================================================= -// 内部関数 プロトタイプ宣言 -// ============================================================================= -static void* kc_memory_allocate(void* ptr, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line); -static void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line); -static void kc_memory_do_init(void); -static void kc_memory_init(void); -static void kc_memory_add(KcMemoryEntry* entry); -static void kc_memory_remove(KcMemoryEntry* entry); -static void kc_memory_exec_handler(void (*handler)(const KcMemoryEntry* entry), KcMemoryEntry* entry); - -static bool kc_memory_mtx_lock(KcMemoryEntry* entry); -static bool kc_memory_mtx_unlock(KcMemoryEntry* entry); /** - * メモリ確保、解放、エラー発生時に呼び出されるハンドラを設定します。 + * 同期化実現のための mutex を初期化します。 * - * @param allocate_handler メモリ確保時に呼び出されるハンドラ - * @param free_handler メモリ解放時に呼び出されるハンドラ - * @param error_handler メモリ確保/解放エラー発生時に呼び出されるハンドラ + * @return true/false (初期化成功/失敗) */ -void kc_memory_set_memory_handler( void (*allocate_handler)(const KcMemoryEntry* entry), - void (*free_handler )(const KcMemoryEntry* entry), - void (*error_handler )(const KcMemoryEntry* entry)) +static +bool kc_memory_mutex_init(void) { - kc_memory_allocate_handler = allocate_handler; - kc_memory_free_handler = free_handler; - kc_memory_error_handler = error_handler; -} - - -void kc_memory_entries( void (*handler )(const KcMemoryEntry* entry)) -{ -} - -void* kc_memory_malloc(size_t size) -{ -} - -void* kc_memory_calloc(size_t nmemb, size_t size) -{ -} - -void* kc_memory_realloc(void* ptr, size_t size) -{ -} -void kc_memory_free(void* ptr) -{ -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理実装 -// - - -// ここでは、常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する -#ifdef KC_MEMORY_ENABLED -#undef KC_MEMORY_ENABLED -#endif - -#ifndef KC_MEMORY_DUMP_LEAK -#define KC_MEMORY_DUMP_LEAK (0) -#endif - - - -/** - * realloc(ptr, size) と同様の方法でメモリを確保します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file 呼び出し元ソースファイル - * @param func 呼び出し元関数 - * @param line 呼び出し元ソース行番号 - * @return 確保したメモリへのポインタ - */ -static void* kc_memory_allocate(void* ptr, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line) -{ - if (size == 0) - { // size == 0 の場合は、free と等価 - kc_memory_free(ptr); - return NULL; - } - - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - errno = ENOMEM; - kc_memory_set_entry(&kc_memory_error, mark, size, file, func, line); - kc_exec_handler(kc_memory_error_handler, kc_memory_error); - return NULL; - } - - if (ptr != NULL) - { - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - switch (old_entry->_mark) + static bool kc_memory_mutex_initialized = false; + if (!kc_memory_mutex_initialized) + { // 未初期化の場合のみ実施する。 + // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 + // mtx_recursive を使用し、同関数内での再帰呼び出し + // (handler内でのkc_memory_locked_execute)を許容する。 + int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); + if (result == thrd_success) { - case KC_MEMORY_DELETE: // 削除済みメモリ - case KC_MEMORY_ALLOCATE_NEW: // new で確保したメモリ - case KC_MEMORY_ALLOCATE_NEW_ARRAY: // new[] で確保したメモリ - // 管理メモリ(エラー) - errno = EINVAL; - kc_memory_exec_handler(kc_memory_error_handler, old_entry); - return NULL; - case KC_MEMORY_ALLOCATED: - default: - // ptr 領域のデータを移動し、解放する。 - memmove((entry + 1), ptr, size); - kc_memory_free(ptr); - break; + kc_memory_mutex_initialized = true; + } + else + { + perror("kc memory : can't init mutex"); + kc_memory_execute_ehandler(NULL, "can't init mutex"); } } - - // 基本設定 - kc_memory_set_entry(entry, mark, file, func, line); - (void) kc_memory_add(entry); - return (entry->data); + return kc_memory_mutex_initialized; } - - /** - * 指定された entry に各値を設定します。 + * 指定された handler の実行を同期化します。 + * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 * - * @param entry エントリ - * @param mark 種別マーク - * @param size メモリサイズ - * @param file 呼び出し元ソースファイル - * @param func 呼び出し元関数 - * @param line 呼び出し元関数 + * @param handler 同期化して実行する関数 + * @param entry handler へ渡される第一引数 (操作するメモリエントリ) + * @param msg handler へ渡される第二引数 + * @return true/false (handler を実行した/handler を実行できなかった) */ -static void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line) +static +bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) { - entry->size = size; - entry->file = file; - entry->func = func; - entry->line = line; - entry->_mark = mark; - entry->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; + (void) kc_memory_mutex_init(); + + int is_locked = mtx_lock(&kc_memory_mutex); + if (is_locked == thrd_success) + { + // ハンドラの戻り値は Don't Care + (void) handler(entry, msg); + + bool is_unlocked = mtx_unlock(&kc_memory_mutex); + if (is_unlocked != thrd_success) + { + perror("memory : can't unlock"); + kc_memory_execute_ehandler(NULL, "can't unlock"); + } + // アンロックに失敗しても handler 自体は実行しているため true を返す。 + return true; + } + return false; } -static void kc_memory_do_init(void); -static void kc_memory_init(void); -static void kc_memory_add(KcMemoryEntry* entry); -static void kc_memory_remove(KcMemoryEntry* entry); -static bool kc_memory_mtx_lock(KcMemoryEntry* entry); -static bool kc_memory_mtx_unlock(KcMemoryEntry* entry); +// ============================================================================ +// データダンプ +// ============================================================================= -static void (*kc_memory_allocate_handler)(const KcMemoryEntry* entry) = NULL; -static void (*kc_memory_free_handler) (const KcMemoryEntry* entry) = NULL; -static void (*kc_memory_error_handler) (const KcMemoryEntry* entry) = NULL; -static KcMemoryEntry kc_memory_head; -static KcMemoryEntry kc_memory_tail; -static KcMemoryEntry kc_memory_error; -static thread_local bool kc_memory_nolock = false; -static once_flag kc_memory_once_flag = ONCE_FLAG_INIT; -static mtx_t kc_memory_mutex; - +/** + * 指定されたバイトを ASCII 文字に変換します。 + * + * @param c バイト + */ +#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') /** - * メモリエントリ。 + * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 + * + * @param mark 確保メモリ状態 + * @return 確保メモリ状態に対応する文字列表現 */ -//typedef struct KcMemoryEntry_ -//{ -// const char* file; /*!< メモリ確保ファイル名 */ -// const char* func; /*!< メモリ確保関数名 */ -// int line; /*!< メモリ確保行番号 */ -// int size; /*!< 確保サイズ */ -// int status; /*!< 確保メモリ状態 */ -// struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ -// struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ -// uint8_t data[]; /*!< データ */ -//} KcMemoryEntry; +static +const char* kc_memory_strmark(int mark) +{ + switch (mark) + { + case KC_MEMORY_DELETED: return "del "; + case KC_MEMORY_ALLOCATED: return "alloc"; + case KC_MEMORY_ALLOCATED_NEW: return "new "; + case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; + default: return "other"; + } +} +/** + * 指定されたメモリエントリのデータをダンプします。 + * + * @param entry エントリ + * @param dump_size ダンプするサイズ(バイト数) + */ +static +void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) +{ + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; + int idx = 0; + + for (; idx < data_len; idx++) + { + printf("%02X ", data_ptr[idx]); + } + + for (; idx < dump_size; idx++) + { + printf("-- "); + } +} + + +/** + * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 + * + * @param entry エントリ + * @param dump_size ダンプするサイズ(バイト数) + */ +static +void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) +{ + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; + int idx = 0; + + for (; idx < data_len; idx++) + { + printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); + } + + for (; idx < dump_size; idx++) + { + printf(" "); + } +} diff --git a/config.mk b/config.mk index 33c1ebc..ceb6197 100644 --- a/config.mk +++ b/config.mk @@ -54,6 +54,6 @@ LIBS += -lpthread -lrt -CFLAGS += -DSC_MEMORY_MANAGE=1 -CFLAGS += -DSC_MEMORY_DUMP_LEAK=1 +CFLAGS += -DKC_MEMORY_ENABLED=1 +CFLAGS += -DKC_MEMORY_DUMP_LEAK=1 diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index b0c78de..4573bf1 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -31,8 +31,8 @@ TAR = tar TOUCH = touch -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ +CC = $(CROSS_COMPILE)clang +CXX = $(CROSS_COMPILE)clang++ RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/modules/Makefile b/modules/Makefile index de1cfcf..7c1975e 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = main +SUBDIRS = libkc main USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/modules/libkc/Makefile b/modules/libkc/Makefile index efd29fb..c51bae2 100644 --- a/modules/libkc/Makefile +++ b/modules/libkc/Makefile @@ -11,7 +11,7 @@ TOPDIR ?= ../.. RULEDIR ?= $(TOPDIR)/mk NAME = libkc -TARGET = $(NAME).so +TARGET = $(NAME).a SUBDIRS = USE_SO_VERSION = diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index 5f628ed..5a9aedc 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -5,6 +5,12 @@ #ifndef KC_H #define KC_H +// ============================================================================= +// 共通定義 +// ============================================================================= +#define UNUSED_VARIABLE(val) (void)(val) + + #if defined(__cplusplus) && (__cplusplus >= 201103L) // ============================================================================= // C++11 @@ -28,11 +34,6 @@ #error "suuports C11/C++1 or later" -// ============================================================================= -// 共通定義 -// ============================================================================= -#define UNUSED_VARIABLE(val) (void)(val) +#endif // C++11, C11, ERROR - -#endif // C11 #endif // KC_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index e54c14f..0408072 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -17,29 +17,34 @@ #ifdef KC_MEMORY_ENABLED // メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) +#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kc_memory_free (ptr) #else #include -#endif // !KC_MEMORY_ENABLED +#endif // !KC_MEMORY_ENABLED // 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) +#define KC_MEMORY_MARK_HEAD (0x55AA5A00) #define KC_MEMORY_MARK_MASK (0xFFFFFF00) typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている + KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている + KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ + KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ } KcMemoryMark; +/** + * ダンプサイズ + */ +#define KC_MEMORY_DUMP_SIZE (16) + /** * 指定されたメモリ管理用種別マークが正しいか判定します。 @@ -55,26 +60,28 @@ */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ - // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) + const char* file; /*!< メモリ確保ファイル名 */ + const char* func; /*!< メモリ確保関数名 */ + int line; /*!< メモリ確保行番号 */ + int size; /*!< 確保サイズ */ + int _mark; /*!< 確保メモリ状態 */ + struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ + struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ + void* data; /*!< データ */ + // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; // ハンドラ関数ポインタ typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); - // プロトタイプ宣言 void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -void kc_memory_dump(int mode); +bool kc_memory_entries(KcMemoryHandler handler); +bool kc_memory_freeif(KcMemoryHandler handler); +void kc_memory_dump(void); +// 以下は、通常直接使用しません。 void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); @@ -82,7 +89,7 @@ #ifdef __cplusplus -} // namespace kc -} // extern "C" +} // namespace kc +} // extern "C" #endif -#endif // KC_MEMORY_H +#endif // KC_MEMORY_H diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 351a4c1..35cf7cc 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -33,7 +33,6 @@ #define KC_MEMORY_PADDING (sizeof(void*) * 2) - //////////////////////////////////////////////////////////////////////////////// // // 内部変数 @@ -45,20 +44,70 @@ static KcMemoryEntry kc_memory_tail; //data = handler; + + bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); + return is_executed; +} + + +/** + * kc_memory_entries で使用されるハンドラ。 + * + * @param entry エントリ(使用しない) + * @param msg メッセージ(使用しない) + * @return true(固定) + */ +static +bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +{ + KcMemoryHandler handler = (KcMemoryHandler) entry->data; + KcMemoryEntry* next_entry = kc_memory_head._next; + while (next_entry != &kc_memory_tail) + { + handler(next_entry, msg); + next_entry = next_entry->_next; + } + return true; +} + + +// freeif +/** + * 指定された handler に現在管理しているメモリエントリが順次渡されます。 + * handler にて、true を返したメモリが解放されます。 + * + * @param handler ハンドラ + * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + */ +bool kc_memory_freeif(KcMemoryHandler handler) +{ + kc_memory_init_entry(); + + // KcMemoryEntry のデータに handler を設定 + size_t size = sizeof(KcMemoryHandler); + KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + entry->data = handler; + + bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); + return is_executed; +} + + +/** + * kc_memory_freeif で使用されるハンドラ。 + * + * @param entry エントリ(使用しない) + * @param msg メッセージ(使用しない) + * @return true(固定) + */ +static +bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) +{ + KcMemoryHandler handler = (KcMemoryHandler) entry->data; + KcMemoryEntry* next_entry = kc_memory_head._next; + while (next_entry != &kc_memory_tail) + { + bool exec_free = handler(next_entry, msg); + next_entry = next_entry->_next; + if (exec_free) + { // メモリ解放 + kc_memory_free(next_entry->_prev->data); + } + } + return true; +} + + +/** + * 現在管理しているメモリ情報をダンプします。 + */ +void kc_memory_dump(void) +{ + (void) kc_memory_entries(kc_memory_dump_entry); +} + + +/** + * 指定されたメモリエントリの情報をダンプ出力します。 + * + * @param entry 出力するエントリ + * @param msg メッセージ + * @return true(固定) + */ +static +bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) +{ + UNUSED_VARIABLE(msg); + + printf("%-15s:%05d:%-15s (%5d) %s ", + entry->file, + entry->line, + entry->func, + entry->size, + kc_memory_strmark(entry->_mark)); + + // dump + kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); + + // ascii + printf(" | "); + kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); + + printf("\n"); + + return true; +} + + + //////////////////////////////////////////////////////////////////////////////// // @@ -112,10 +335,41 @@ // +// ============================================================================= +// メモリ確保解放 +// ============================================================================= + /** * 指定されたサイズのメモリを確保します。 - * メモリの確保方法は、realloc(void* ptr, size_t size) と同様となります。 + * + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + if (entry == NULL) + { // メモリ確保失敗 + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); + return NULL; + } + + kc_memory_set_entry(entry, size, mark, file, func, line); + kc_memory_add_entry(entry); + return (entry->data); +} + + +/** + * 指定されたポインタがさすメモリサイズを変更します。 + * ポインタ ptr が NULL の場合、kc_memory_allocate を呼び出します。 * * @param ptr メモリサイズを変更するメモリへのポインタ * @param size 確保するメモリサイズ @@ -126,27 +380,197 @@ * @return 確保したメモリへのポインタ */ static -void* kc_memory_allocate(void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +void* kc_memory_reallocate(void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) { - if (size == 0) - { // サイズが 0 のため free と等価 - kc_memory_free(ptr); - return NULL; + if (ptr == NULL) + { + return kc_memory_allocate(size, mark, file, func, line); } - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - errno = ENOMEM; - kc_memory_ehandler(entry, ""); - - + KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; + old_entry--; + switch (old_entry->_mark) + { + case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc + return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); + case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) + return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); + case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) + return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); + case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする + return kc_memory_allocate(size, mark, file, func, line); + default: // 管理外メモリの realloc + return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); } } + +/** + * 管理されたメモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_reallocate_managed_ptr( + void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + UNUSED_VARIABLE(ptr); + + KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; + old_entry--; + + kc_memory_remove_entry(old_entry); + KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + if (entry != NULL) + { // メモリ確保成功 + // メモリ管理リストに加えてポインタを返す。 + kc_memory_set_entry(entry, size, mark, file, func, line); + kc_memory_add_entry(entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行し、NULL を返す。 + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + + // 古いメモリ領域は残っているため、管理対象に戻す。 + kc_memory_add_entry(old_entry); + return NULL; + } +} + + +/** + * 管理外メモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_reallocate_unmanaged_ptr( + void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + // |<-- 新たな領域 ---------------->| + // +------------+-------------------+ + // | 元々の領域 | 追加分 + 管理領域 | + // +------------+-------------------+ + // ↓ + // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー + // ↓ + // +----------+------------+--------+ + // | 管理領域 | 元々の領域 | 追加分 | + // +----------+------------+--------+ + KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); + if (entry != NULL) + { // メモリ確保成功 + // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー + // メモリ管理リストに加えてポインタを返す。 + memmove((entry + 1), entry, size); + kc_memory_set_entry(entry, size, mark, file, func, line); + kc_memory_add_entry(entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行し、NULL を返す。 + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + return NULL; + } +} + + +/** + * 不正なメモリ領域に対する realloc のエラー処理を実施します。 + * + * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報を示すマーク + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ +static +void* kc_memory_reallocate_invalid_ptr( + void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +{ + UNUSED_VARIABLE(ptr); + + errno = EINVAL; + kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); + kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); + return NULL; +} + + +/** + * 指定されたポインタの指すメモリ領域を解放します。 + * NULL が指定された場合なにもしません。 + * 管理されたメモリの場合、管理領域を合わせて解放します。 + * 管理外メモリの場合、free を実行します。 + * + * @param ptr 解放するメモリへのポインタ + */ static void kc_memory_deallocate(void* ptr) { + if (ptr == NULL) + { // NULL ポインタに対してはなにもしない + return; + } + + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; + switch (entry->_mark) + { + case KC_MEMORY_ALLOCATED: // 管理メモリ + kc_memory_deallocate_entry(entry); + break; + case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ + kc_memory_deallocate_entry(entry); + perror("kc memory : warning : please use delete"); + kc_memory_execute_ehandler(entry, "warning : please use delete"); + break; + case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ + kc_memory_deallocate_entry(entry); + perror("kc memory : warning : please use delete[]"); + kc_memory_execute_ehandler(entry, "warning : please use delete[]"); + break; + case KC_MEMORY_DELETED: // 削除済みメモリ + // Nothing to do. + break; + default: + free(ptr); + break; + } +} + + +/** + * 指定されたメモリ管理およびデータ領域を解放します。 + * + * @param entry 解放するメモリ管理領域へのポインタ + */ +static +void kc_memory_deallocate_entry(KcMemoryEntry* entry) +{ + (void) kc_memory_remove_entry(entry); + entry->_mark = KC_MEMORY_DELETED; + entry->size = 0; + free(entry); } @@ -154,6 +578,7 @@ // メモリエントリ管理 // ============================================================================= + /** * メモリ管理エントリ全体を初期化します。 */ @@ -164,9 +589,9 @@ if (!kc_memory_entry_initialized) { // メモリ管理エントリ初期化 - kc_memory_init_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_init_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_init_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); + kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); + kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); + kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; @@ -212,6 +637,12 @@ bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); if (is_executed) { + kc_memory_execute_ahandler(entry, "allocate memory"); + } + else + { + perror("kc memory : can't add entry"); + kc_memory_execute_ehandler(entry, "can't add entry"); } } @@ -224,8 +655,10 @@ * @return true (固定) */ static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, msg) +bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) { + UNUSED_VARIABLE(msg); + // [tail] の一つ前に挿入する。 entry->_next = &kc_memory_tail; entry->_prev = kc_memory_tail._prev; @@ -235,75 +668,50 @@ } - -// ============================================================================ -// 同期化 -// ============================================================================= - -/** 同期化のための mutex */ -static mtx_t kc_memory_mutex; -static bool kc_memory_mutex_init(void); -static bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg); - /** - * 同期化実現のための mutex を初期化します。 + * 指定されたエントリをメモリ管理のリストから削除します。 + * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 * - * @return true/false (初期化成功/失敗) - */ + * @param entry 削除するエントリ + */ static -bool kc_memory_mutex_init(void) +void kc_memory_remove_entry(KcMemoryEntry* entry) { - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain); - if (result == thrd_success) - { - kc_memory_mutex_initialized = true; - } - else - { - perror("kc memory : can't init mutex"); - } - } - return kc_memory_mutex_initilized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) + kc_memory_init_entry(); + bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); + if (is_executed) { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; + kc_memory_execute_fhandler(entry, "free memory"); } - return false; + else + { + perror("kc memory : can't remove entry"); + kc_memory_execute_ehandler(entry, "can't remove entry"); + } } +/** + * 指定されたエントリをメモリ管理のリストより削除します。 + * + * @param entry 削除するエントリ + * @param msg メッセージ (使用されません) + * @return true (固定) + */ +static +bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) +{ + UNUSED_VARIABLE(msg); + + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + return true; +} + + + // ============================================================================= -// 登録ハンドラの実行 +// ハンドラ実行 // ============================================================================= @@ -358,193 +766,151 @@ } +// ============================================================================ +// 同期実行 // ============================================================================= -// 内部関数 プロトタイプ宣言 -// ============================================================================= -static void* kc_memory_allocate(void* ptr, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line); -static void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line); -static void kc_memory_do_init(void); -static void kc_memory_init(void); -static void kc_memory_add(KcMemoryEntry* entry); -static void kc_memory_remove(KcMemoryEntry* entry); -static void kc_memory_exec_handler(void (*handler)(const KcMemoryEntry* entry), KcMemoryEntry* entry); - -static bool kc_memory_mtx_lock(KcMemoryEntry* entry); -static bool kc_memory_mtx_unlock(KcMemoryEntry* entry); /** - * メモリ確保、解放、エラー発生時に呼び出されるハンドラを設定します。 + * 同期化実現のための mutex を初期化します。 * - * @param allocate_handler メモリ確保時に呼び出されるハンドラ - * @param free_handler メモリ解放時に呼び出されるハンドラ - * @param error_handler メモリ確保/解放エラー発生時に呼び出されるハンドラ + * @return true/false (初期化成功/失敗) */ -void kc_memory_set_memory_handler( void (*allocate_handler)(const KcMemoryEntry* entry), - void (*free_handler )(const KcMemoryEntry* entry), - void (*error_handler )(const KcMemoryEntry* entry)) +static +bool kc_memory_mutex_init(void) { - kc_memory_allocate_handler = allocate_handler; - kc_memory_free_handler = free_handler; - kc_memory_error_handler = error_handler; -} - - -void kc_memory_entries( void (*handler )(const KcMemoryEntry* entry)) -{ -} - -void* kc_memory_malloc(size_t size) -{ -} - -void* kc_memory_calloc(size_t nmemb, size_t size) -{ -} - -void* kc_memory_realloc(void* ptr, size_t size) -{ -} -void kc_memory_free(void* ptr) -{ -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理実装 -// - - -// ここでは、常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する -#ifdef KC_MEMORY_ENABLED -#undef KC_MEMORY_ENABLED -#endif - -#ifndef KC_MEMORY_DUMP_LEAK -#define KC_MEMORY_DUMP_LEAK (0) -#endif - - - -/** - * realloc(ptr, size) と同様の方法でメモリを確保します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file 呼び出し元ソースファイル - * @param func 呼び出し元関数 - * @param line 呼び出し元ソース行番号 - * @return 確保したメモリへのポインタ - */ -static void* kc_memory_allocate(void* ptr, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line) -{ - if (size == 0) - { // size == 0 の場合は、free と等価 - kc_memory_free(ptr); - return NULL; - } - - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - errno = ENOMEM; - kc_memory_set_entry(&kc_memory_error, mark, size, file, func, line); - kc_exec_handler(kc_memory_error_handler, kc_memory_error); - return NULL; - } - - if (ptr != NULL) - { - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - switch (old_entry->_mark) + static bool kc_memory_mutex_initialized = false; + if (!kc_memory_mutex_initialized) + { // 未初期化の場合のみ実施する。 + // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 + // mtx_recursive を使用し、同関数内での再帰呼び出し + // (handler内でのkc_memory_locked_execute)を許容する。 + int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); + if (result == thrd_success) { - case KC_MEMORY_DELETE: // 削除済みメモリ - case KC_MEMORY_ALLOCATE_NEW: // new で確保したメモリ - case KC_MEMORY_ALLOCATE_NEW_ARRAY: // new[] で確保したメモリ - // 管理メモリ(エラー) - errno = EINVAL; - kc_memory_exec_handler(kc_memory_error_handler, old_entry); - return NULL; - case KC_MEMORY_ALLOCATED: - default: - // ptr 領域のデータを移動し、解放する。 - memmove((entry + 1), ptr, size); - kc_memory_free(ptr); - break; + kc_memory_mutex_initialized = true; + } + else + { + perror("kc memory : can't init mutex"); + kc_memory_execute_ehandler(NULL, "can't init mutex"); } } - - // 基本設定 - kc_memory_set_entry(entry, mark, file, func, line); - (void) kc_memory_add(entry); - return (entry->data); + return kc_memory_mutex_initialized; } - - /** - * 指定された entry に各値を設定します。 + * 指定された handler の実行を同期化します。 + * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 * - * @param entry エントリ - * @param mark 種別マーク - * @param size メモリサイズ - * @param file 呼び出し元ソースファイル - * @param func 呼び出し元関数 - * @param line 呼び出し元関数 + * @param handler 同期化して実行する関数 + * @param entry handler へ渡される第一引数 (操作するメモリエントリ) + * @param msg handler へ渡される第二引数 + * @return true/false (handler を実行した/handler を実行できなかった) */ -static void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, - KcMemoryMark mark, const char* file, const char* func, int line) +static +bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) { - entry->size = size; - entry->file = file; - entry->func = func; - entry->line = line; - entry->_mark = mark; - entry->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; + (void) kc_memory_mutex_init(); + + int is_locked = mtx_lock(&kc_memory_mutex); + if (is_locked == thrd_success) + { + // ハンドラの戻り値は Don't Care + (void) handler(entry, msg); + + bool is_unlocked = mtx_unlock(&kc_memory_mutex); + if (is_unlocked != thrd_success) + { + perror("memory : can't unlock"); + kc_memory_execute_ehandler(NULL, "can't unlock"); + } + // アンロックに失敗しても handler 自体は実行しているため true を返す。 + return true; + } + return false; } -static void kc_memory_do_init(void); -static void kc_memory_init(void); -static void kc_memory_add(KcMemoryEntry* entry); -static void kc_memory_remove(KcMemoryEntry* entry); -static bool kc_memory_mtx_lock(KcMemoryEntry* entry); -static bool kc_memory_mtx_unlock(KcMemoryEntry* entry); +// ============================================================================ +// データダンプ +// ============================================================================= -static void (*kc_memory_allocate_handler)(const KcMemoryEntry* entry) = NULL; -static void (*kc_memory_free_handler) (const KcMemoryEntry* entry) = NULL; -static void (*kc_memory_error_handler) (const KcMemoryEntry* entry) = NULL; -static KcMemoryEntry kc_memory_head; -static KcMemoryEntry kc_memory_tail; -static KcMemoryEntry kc_memory_error; -static thread_local bool kc_memory_nolock = false; -static once_flag kc_memory_once_flag = ONCE_FLAG_INIT; -static mtx_t kc_memory_mutex; - +/** + * 指定されたバイトを ASCII 文字に変換します。 + * + * @param c バイト + */ +#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') /** - * メモリエントリ。 + * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 + * + * @param mark 確保メモリ状態 + * @return 確保メモリ状態に対応する文字列表現 */ -//typedef struct KcMemoryEntry_ -//{ -// const char* file; /*!< メモリ確保ファイル名 */ -// const char* func; /*!< メモリ確保関数名 */ -// int line; /*!< メモリ確保行番号 */ -// int size; /*!< 確保サイズ */ -// int status; /*!< 確保メモリ状態 */ -// struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ -// struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ -// uint8_t data[]; /*!< データ */ -//} KcMemoryEntry; +static +const char* kc_memory_strmark(int mark) +{ + switch (mark) + { + case KC_MEMORY_DELETED: return "del "; + case KC_MEMORY_ALLOCATED: return "alloc"; + case KC_MEMORY_ALLOCATED_NEW: return "new "; + case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; + default: return "other"; + } +} +/** + * 指定されたメモリエントリのデータをダンプします。 + * + * @param entry エントリ + * @param dump_size ダンプするサイズ(バイト数) + */ +static +void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) +{ + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; + int idx = 0; + + for (; idx < data_len; idx++) + { + printf("%02X ", data_ptr[idx]); + } + + for (; idx < dump_size; idx++) + { + printf("-- "); + } +} + + +/** + * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 + * + * @param entry エントリ + * @param dump_size ダンプするサイズ(バイト数) + */ +static +void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) +{ + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; + int idx = 0; + + for (; idx < data_len; idx++) + { + printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); + } + + for (; idx < dump_size; idx++) + { + printf(" "); + } +} diff --git a/modules/main/Makefile b/modules/main/Makefile index cc87caf..bdd1023 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS +=