Created
March 7, 2026 07:12
-
-
Save jacky860226/2cb0e1fdbc5b74cd93f32d138e1145fe to your computer and use it in GitHub Desktop.
Memory Hooking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef _GNU_SOURCE | |
| #define _GNU_SOURCE | |
| #endif | |
| #include <dlfcn.h> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <unistd.h> | |
| #include <new> | |
| #include <cstdarg> | |
| #include <mutex> | |
| #include <sys/mman.h> | |
| #include <cerrno> | |
| #include <sys/syscall.h> | |
| #include <chrono> | |
| #include <atomic> | |
| #include <malloc.h> | |
| #include <unordered_set> | |
| #include <algorithm> | |
| #include <vector> | |
| #include <ctime> | |
| // 1. 定義函式指標型別 | |
| // classic functions | |
| using CallocFunc = decltype(calloc); | |
| using FreeFunc = decltype(free); | |
| using MallocFunc = decltype(malloc); | |
| using ReallocFunc = decltype(realloc); | |
| // pagesize functions | |
| using VallocFunc = decltype(valloc); | |
| using MemalignFunc = decltype(memalign); | |
| using Posix_memalignFunc = decltype(posix_memalign); | |
| using Aligned_allocFunc = decltype(aligned_alloc); | |
| // mmap/munmap | |
| using MmapFunc = decltype(mmap); | |
| using Mmap64Func = decltype(mmap64); | |
| using MunmapFunc = decltype(munmap); | |
| using MallocUsableSizeFunc = decltype(malloc_usable_size); | |
| // 2. 全域變數 | |
| static CallocFunc *callocFunc = nullptr; | |
| static FreeFunc *freeFunc = nullptr; | |
| static MallocFunc *mallocFunc = nullptr; | |
| static ReallocFunc *reallocFunc = nullptr; | |
| static VallocFunc *vallocFunc = nullptr; | |
| static MemalignFunc *memalignFunc = nullptr; | |
| static Posix_memalignFunc *posix_memalignFunc = nullptr; | |
| static Aligned_allocFunc* aligned_allocFunc = nullptr; | |
| static MmapFunc *mmapFunc = nullptr; | |
| static Mmap64Func *mmap64Func = nullptr; | |
| static MunmapFunc *munmapFunc = nullptr; | |
| static MallocUsableSizeFunc *mallocUsableSizeFunc = nullptr; | |
| static std::atomic<bool> initialized{false}; | |
| static std::atomic<bool> initializing{false}; | |
| // Static Buffer 設定 (128MB) | |
| static constexpr auto KB = 1024UL; | |
| static constexpr auto MB = 1024UL * 1024UL; | |
| static constexpr size_t StaticMemorySize = 128 * MB; | |
| static char staticMemory[StaticMemorySize]; | |
| static char *staticPtr = staticMemory; | |
| static size_t totalStaticSize = 0; | |
| static size_t lastStaticAlloc = 0; | |
| static std::recursive_mutex mtStaticMem; | |
| static std::timed_mutex mtLog; | |
| // 3. 輔助函式 | |
| /** | |
| * @brief RAII 輔助類別,用於在解構時恢復變數的原始值。 | |
| * | |
| * 這在防止遞迴呼叫或暫時修改全域狀態時非常有用。 | |
| */ | |
| template <typename T> | |
| class Keep { | |
| T &ref_; | |
| T oldvalue_; | |
| public: | |
| explicit Keep (T &var) : ref_(var), oldvalue_(var) { } | |
| Keep (T &var, T value) : ref_(var), oldvalue_(var) { | |
| var = value; | |
| } | |
| ~Keep () { | |
| ref_ = oldvalue_; | |
| } | |
| Keep(const Keep&) = delete; | |
| Keep& operator=(const Keep&) = delete; | |
| }; | |
| /** | |
| * @brief 記憶體配置紀錄結構。 | |
| * | |
| * 儲存單次記憶體配置的指標位址、大小以及配置時間。 | |
| */ | |
| struct MemoryRec { | |
| void *ptr_; ///< 配置的記憶體起始位址 | |
| size_t size_; ///< 配置的大小 (bytes) | |
| std::chrono::time_point<std::chrono::system_clock> time_; ///< 配置發生的時間點 | |
| MemoryRec(void *ptr, size_t size) : ptr_(ptr), size_(size), time_(std::chrono::system_clock::now()) {} | |
| MemoryRec(void *ptr) : ptr_(ptr), size_(0) {} | |
| }; | |
| struct MemoryRecOp { | |
| size_t operator()(const MemoryRec* rec) const { | |
| return std::hash<void*>()(rec->ptr_); | |
| } | |
| bool operator()(const MemoryRec* a, const MemoryRec* b) const { | |
| return a->ptr_ == b->ptr_; | |
| } | |
| }; | |
| /** | |
| * @brief 管理所有記憶體配置紀錄的類別。 | |
| * | |
| * 使用雜湊集合 (unordered_set) 儲存紀錄,並提供執行緒安全的操作介面。 | |
| */ | |
| class MemoryRecords { | |
| std::unordered_set<MemoryRec*, MemoryRecOp, MemoryRecOp> records_; | |
| // 使用 std::mutex 即可。 | |
| // 雖然 malloc/free 可能在多執行緒環境下頻繁呼叫,但我們在 Dump 時採用 Snapshot 策略, | |
| // 複製完資料即釋放鎖,不會在持有鎖的期間呼叫可能導致遞迴鎖定的外部函式。 | |
| std::mutex lock_; | |
| public: | |
| /** | |
| * @brief 新增一筆記憶體配置紀錄。 | |
| * | |
| * @param ptr 配置的記憶體指標。 | |
| * @param size 配置的大小。 | |
| */ | |
| void Add(void* ptr, size_t size) { | |
| std::lock_guard<std::mutex> guard(lock_); | |
| records_.insert(new MemoryRec(ptr, size)); | |
| } | |
| /** | |
| * @brief 移除一筆記憶體配置紀錄。 | |
| * | |
| * 當記憶體被釋放 (free) 時呼叫此函式。 | |
| * | |
| * @param ptr 要釋放的記憶體指標。 | |
| */ | |
| void Remove(void* ptr) { | |
| std::lock_guard<std::mutex> guard(lock_); | |
| MemoryRec searchKey(ptr); | |
| auto it = records_.find(&searchKey); | |
| if (it != records_.end()) { | |
| delete *it; | |
| records_.erase(it); | |
| } | |
| } | |
| /** | |
| * @brief 查詢指定記憶體位址的配置紀錄。 | |
| * | |
| * @param ptr 記憶體指標。 | |
| * @return const MemoryRec* 記憶體紀錄指標 (若找不到則回傳 nullptr)。 | |
| */ | |
| const MemoryRec* Find(void* ptr) { | |
| std::lock_guard<std::mutex> guard(lock_); | |
| MemoryRec searchKey(ptr); | |
| auto it = records_.find(&searchKey); | |
| if (it != records_.end()) { | |
| return *it; | |
| } | |
| return nullptr; | |
| } | |
| /** | |
| * @brief 將目前的記憶體紀錄輸出到指定的檔案串流。 | |
| * | |
| * 實作細節與設計考量: | |
| * 1. **Snapshot (快照) 策略**: | |
| * 先在 Critical Section (持有 lock_ 期間) 將所有紀錄複製到 local vector。 | |
| * 這讓我們能盡快釋放鎖,避免在進行緩慢的 I/O 操作 (fprintf) 時阻塞其他執行緒的 malloc/free。 | |
| * 若在持有鎖的情況下做 I/O,會嚴重影響程式效能,甚至增加 Deadlock 風險。 | |
| * | |
| * 2. **時間排序**: | |
| * 依據 time_ 欄位對快照進行排序,還原記憶體配置的發生順序,方便閱讀。 | |
| * | |
| * 3. **執行緒安全的時間格式化**: | |
| * 使用 ctime_r 取代 ctime。ctime 使用內部的 Static Buffer,在多執行緒下不安全 (Race Condition)。 | |
| * | |
| * @param stream 輸出的目標檔案串流 (例如 stdout 或 stderr)。 | |
| */ | |
| void Dump(FILE* stream) { | |
| std::vector<MemoryRec> tempRecords; | |
| { | |
| std::lock_guard<std::mutex> guard(lock_); | |
| tempRecords.reserve(records_.size()); | |
| for (auto* rec : records_) { | |
| tempRecords.push_back(*rec); | |
| } | |
| } | |
| std::sort(tempRecords.begin(), tempRecords.end(), [](const MemoryRec& a, const MemoryRec& b) { | |
| return a.time_ < b.time_; | |
| }); | |
| fprintf(stream, "=== Memory Records ===\n"); | |
| size_t total = 0; | |
| for (const auto& rec : tempRecords) { | |
| auto timet = std::chrono::system_clock::to_time_t(rec.time_); | |
| char timeStr[26]; | |
| ctime_r(&timet, timeStr); | |
| timeStr[24] = '\0'; // Remove newline | |
| fprintf(stream, "[%s] Ptr: %p, Size: %zu\n", timeStr, rec.ptr_, rec.size_); | |
| total += rec.size_; | |
| } | |
| fprintf(stream, "Total allocated: %zu bytes in %zu blocks\n", total, tempRecords.size()); | |
| fprintf(stream, "======================\n"); | |
| } | |
| }; | |
| /** | |
| * @brief 全域記憶體紀錄物件指標。 | |
| * | |
| * 使用指標而非靜態物件實體,是為了手動控制其生命週期 (Life-cycle)。 | |
| * 避免在程式結束 (exit) 階段,靜態物件已被解構後,仍有其他解構子呼叫 free() 導致 Crash。 | |
| */ | |
| static MemoryRecords* g_records = nullptr; | |
| /** | |
| * @brief 執行緒區域 (Thread-local) 的遞迴防止旗標。 | |
| * | |
| * 這是 Malloc Hook 最核心的保護機制。 | |
| * 當我們在 malloc/free Hook 內部執行邏輯 (如 g_records->Add) 時, | |
| * 這些邏輯本身 (例如 STL 容器操作) 也會呼叫 malloc/free。 | |
| * 若沒有此旗標,會導致無限遞迴 (Infinite Recursion) 直到 Stack Overflow。 | |
| * | |
| * 流程: | |
| * 1. 進入 malloc Hook | |
| * 2. 檢查 insideBase,若為 true 則直接呼叫原始 malloc (不紀錄) | |
| * 3. 設定 insideBase = true | |
| * 4. 執行紀錄邏輯 (可能觸發 malloc -> 步驟 1 -> 步驟 2 -> 返回) | |
| * 5. 恢復 insideBase = false | |
| */ | |
| static thread_local bool insideBase = false; | |
| // Round up to alignment | |
| template <typename T> | |
| inline T RoundUp(T n, T roundTo) { | |
| return roundTo ? ((n + roundTo - 1) / roundTo) * roundTo : 0; | |
| } | |
| inline void *AlignPointer(void *ptr, size_t align) { | |
| return (void *) (((size_t) ptr + align - 1) / align * align); | |
| } | |
| bool IsStaticMemory(void *ptr) { | |
| return ptr >= staticMemory && ptr < staticMemory + StaticMemorySize; | |
| } | |
| void *StaticAlloc(size_t size, size_t alignment) { | |
| std::lock_guard<std::recursive_mutex> lock(mtStaticMem); | |
| totalStaticSize += size; | |
| staticPtr = (char *) AlignPointer(staticPtr, alignment); | |
| if (staticPtr + size > staticMemory + StaticMemorySize) { | |
| const char* msg = "*** no more static memory ***\n"; | |
| write(STDERR_FILENO, msg, strlen(msg)); | |
| abort(); | |
| } | |
| void *ptr = staticPtr; | |
| staticPtr += size; | |
| lastStaticAlloc = size; | |
| return ptr; | |
| } | |
| void StaticFree(void *ptr) { | |
| std::lock_guard<std::recursive_mutex> lock(mtStaticMem); | |
| if (staticPtr - lastStaticAlloc == ptr) { | |
| staticPtr = (char *) ptr; | |
| } | |
| } | |
| void *StaticRealloc(void *ptr, size_t size) { | |
| std::lock_guard<std::recursive_mutex> lock(mtStaticMem); | |
| if (ptr == staticPtr - lastStaticAlloc) { | |
| if ((char*)ptr + size > staticMemory + StaticMemorySize) { | |
| const char* msg = "*** no more static memory (realloc) ***\n"; | |
| write(STDERR_FILENO, msg, strlen(msg)); | |
| abort(); | |
| } | |
| long delta = long(size - lastStaticAlloc); | |
| totalStaticSize += delta; | |
| staticPtr += delta; | |
| lastStaticAlloc = size; | |
| return ptr; | |
| } | |
| void *newPtr = StaticAlloc(size, 8); | |
| size_t offset = (char*)ptr - staticMemory; | |
| size_t oldSize = (size < offset) ? size : offset; | |
| memmove(newPtr, ptr, oldSize); | |
| return newPtr; | |
| } | |
| void InitializePointers() { | |
| if (initialized) return; | |
| initializing = true; | |
| // 使用 RTLD_NEXT 載入 libc 符號 | |
| callocFunc = (CallocFunc *)dlsym(RTLD_NEXT, "calloc"); | |
| freeFunc = (FreeFunc *)dlsym(RTLD_NEXT, "free"); | |
| mallocFunc = (MallocFunc *)dlsym(RTLD_NEXT, "malloc"); | |
| reallocFunc = (ReallocFunc *)dlsym(RTLD_NEXT, "realloc"); | |
| vallocFunc = (VallocFunc *)dlsym(RTLD_NEXT, "valloc"); | |
| memalignFunc = (MemalignFunc *)dlsym(RTLD_NEXT, "memalign"); | |
| posix_memalignFunc = (Posix_memalignFunc *)dlsym(RTLD_NEXT, "posix_memalign"); | |
| aligned_allocFunc = (Aligned_allocFunc *)dlsym(RTLD_NEXT, "aligned_alloc"); | |
| mmapFunc = (MmapFunc *)dlsym(RTLD_NEXT, "mmap"); | |
| mmap64Func = (Mmap64Func *)dlsym(RTLD_NEXT, "mmap64"); | |
| munmapFunc = (MunmapFunc *)dlsym(RTLD_NEXT, "munmap"); | |
| mallocUsableSizeFunc = (MallocUsableSizeFunc *)dlsym(RTLD_NEXT, "malloc_usable_size"); | |
| if (!g_records) { | |
| bool old = insideBase; | |
| insideBase = true; | |
| g_records = new MemoryRecords(); | |
| insideBase = old; | |
| } | |
| initialized = true; | |
| initializing = false; | |
| } | |
| void Initialize() { | |
| if (initializing) return; | |
| if (!initialized) InitializePointers(); | |
| } | |
| /** | |
| * @brief 執行緒安全的日誌輸出函式。 | |
| * | |
| * 關鍵實作細節: | |
| * 1. **防止無限遞迴 (Infinite Recursion)**: | |
| * printf/snprintf 內部實作可能會呼叫 malloc。 | |
| * 若不檢查 insideLog 旗標,流程會變成:Log -> snprintf -> malloc -> Log -> snprintf ... 導致 Stack Overflow。 | |
| * 這是撰寫 Malloc Hook 時最容易踩到的坑。 | |
| * | |
| * 2. **保存 errno**: | |
| * 使用 Keep<int> 保存並恢復 errno,避免 Log 內部的系統呼叫改變了 errno, | |
| * 導致原本程式邏輯判斷錯誤 (例如原本 malloc 失敗設了 ENOMEM,卻被 Log 蓋掉)。 | |
| * | |
| * @param fmt 格式化字串。 | |
| * @param ... 可變參數。 | |
| */ | |
| void Log(const char* fmt, ...) { | |
| // 防止 Log 內部 (如 vsnprintf) 再次呼叫 malloc 導致無限遞迴 | |
| static thread_local bool insideLog = false; | |
| if (insideLog) return; | |
| Keep<bool> k(insideLog, true); | |
| Keep<int> kErrno(errno); // 保存 errno,避免 Log 影響程式邏輯 | |
| char buf[1024]; | |
| // 加上 Thread ID 方便除錯 | |
| int len = snprintf(buf, sizeof(buf), "[TID:%ld] ", syscall(SYS_gettid)); | |
| va_list args; | |
| va_start(args, fmt); | |
| len += vsnprintf(buf + len, sizeof(buf) - len, fmt, args); | |
| va_end(args); | |
| if (len >= (int)sizeof(buf)) { | |
| len = sizeof(buf); | |
| buf[sizeof(buf) - 1] = '\0'; | |
| } | |
| if (!mtLog.try_lock_for(std::chrono::milliseconds(100))) { | |
| const char* msg = "Cannot lock mtLog\n"; | |
| write(STDERR_FILENO, msg, strlen(msg)); | |
| return; | |
| } | |
| std::unique_lock<std::timed_mutex> lock(mtLog, std::adopt_lock); | |
| write(STDERR_FILENO, buf, len); | |
| } | |
| extern "C" { | |
| /** | |
| * @brief C 語言介面:輸出記憶體紀錄。 | |
| * | |
| * 使用 extern "C" 宣告以關閉 C++ Name Mangling (名稱修飾)。 | |
| * 這有兩個重要目的: | |
| * 1. **跨語言相容**:讓純 C 語言撰寫的程式也能連結並呼叫此函式。 | |
| * 2. **動態載入支援**:若使用 dlsym (Dynamic Loading) 取得函式位址, | |
| * 必須使用未修飾的名稱 "dump_memory_records" 才能找到符號。 | |
| * | |
| * @param stream 輸出的目標檔案串流。 | |
| */ | |
| void dump_memory_records(FILE* stream) { | |
| if (g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Dump(stream); | |
| } | |
| } | |
| void *calloc(size_t nmemb, size_t size) { | |
| if (initializing) { | |
| if (callocFunc == nullptr) { | |
| auto ptr = StaticAlloc(nmemb * size, 8); | |
| if (ptr) memset(ptr, 0, nmemb * size); | |
| return ptr; | |
| } | |
| return (*callocFunc)(nmemb, size); | |
| } | |
| Initialize(); | |
| if (insideBase) return (*callocFunc)(nmemb, size); | |
| void* ptr = (*callocFunc)(nmemb, size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, nmemb * size); | |
| } | |
| Log("[calloc] nmemb: %zu, size: %zu\n", nmemb, size); | |
| return ptr; | |
| } | |
| void free(void *ptr) { | |
| if (ptr == nullptr) return; | |
| if (IsStaticMemory(ptr)) { | |
| StaticFree(ptr); | |
| return; | |
| } | |
| if (initializing && freeFunc == nullptr) return; | |
| Initialize(); | |
| if (insideBase) { | |
| (*freeFunc)(ptr); | |
| return; | |
| } | |
| if (g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Remove(ptr); | |
| } | |
| Log("[free] ptr: %p\n", ptr); | |
| (*freeFunc)(ptr); | |
| } | |
| void cfree(void *ptr) { | |
| free(ptr); | |
| } | |
| void *malloc(size_t size) { | |
| if (initializing) return StaticAlloc(size, 8); | |
| Initialize(); | |
| if (insideBase) return (*mallocFunc)(size); | |
| void* ptr = (*mallocFunc)(size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[malloc] size: %zu, ptr: %p\n", size, ptr); | |
| return ptr; | |
| } | |
| void *realloc(void *ptr, size_t size) { | |
| if (IsStaticMemory(ptr)) return StaticRealloc(ptr, size); | |
| if (initializing && reallocFunc == nullptr) return StaticAlloc(size, 8); | |
| Initialize(); | |
| if (insideBase) return (*reallocFunc)(ptr, size); | |
| void* new_ptr = (*reallocFunc)(ptr, size); | |
| if (new_ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| if (ptr) g_records->Remove(ptr); | |
| g_records->Add(new_ptr, size); | |
| } | |
| Log("[realloc] old: %p, size: %zu, new: %p\n", ptr, size, new_ptr); | |
| return new_ptr; | |
| } | |
| void *valloc(size_t size) { | |
| static auto pagesize = (size_t)sysconf(_SC_PAGESIZE); | |
| if (initializing) return StaticAlloc(size, pagesize); | |
| Initialize(); | |
| if (insideBase) return (*vallocFunc)(size); | |
| void* ptr = (*vallocFunc)(size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[valloc] size: %zu\n", size); | |
| return ptr; | |
| } | |
| void *pvalloc(size_t size) { | |
| static auto pagesize = (size_t)sysconf(_SC_PAGESIZE); | |
| Log("[pvalloc] size: %zu\n", size); | |
| return memalign(pagesize, RoundUp(size, pagesize)); | |
| } | |
| void *memalign(size_t alignment, size_t size) { | |
| if (initializing) return StaticAlloc(size, alignment); | |
| Initialize(); | |
| if (insideBase) return (*memalignFunc)(alignment, size); | |
| void* ptr = (*memalignFunc)(alignment, size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[memalign] align: %zu, size: %zu\n", alignment, size); | |
| return ptr; | |
| } | |
| int posix_memalign(void **memptr, size_t alignment, size_t size) { | |
| if (initializing) { | |
| *memptr = StaticAlloc(size, alignment); | |
| return 0; | |
| } | |
| Initialize(); | |
| if (insideBase) return (*posix_memalignFunc)(memptr, alignment, size); | |
| int ret = (*posix_memalignFunc)(memptr, alignment, size); | |
| if (ret == 0 && *memptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(*memptr, size); | |
| } | |
| Log("[posix_memalign] align: %zu, size: %zu\n", alignment, size); | |
| return ret; | |
| } | |
| void *aligned_alloc(size_t alignment, size_t size) { | |
| if (initializing) return StaticAlloc(size, alignment); | |
| Initialize(); | |
| if (insideBase) return (*aligned_allocFunc)(alignment, size); | |
| void* ptr = (*aligned_allocFunc)(alignment, size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[aligned_alloc] align: %zu, size: %zu\n", alignment, size); | |
| return ptr; | |
| } | |
| void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { | |
| if (initializing) { | |
| if (mmapFunc == nullptr) return StaticAlloc(length, 8); | |
| return (*mmapFunc)(addr, length, prot, flags, fd, offset); | |
| } | |
| Initialize(); | |
| if (insideBase) return (*mmapFunc)(addr, length, prot, flags, fd, offset); | |
| void* ptr = (*mmapFunc)(addr, length, prot, flags, fd, offset); | |
| Log("[mmap] len: %zu, ptr: %p\n", length, ptr); | |
| return ptr; | |
| } | |
| void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset) { | |
| if (initializing) { | |
| if (mmap64Func == nullptr) return StaticAlloc(length, 8); | |
| return (*mmap64Func)(addr, length, prot, flags, fd, offset); | |
| } | |
| Initialize(); | |
| if (insideBase) return (*mmap64Func)(addr, length, prot, flags, fd, offset); | |
| void* ptr = (*mmap64Func)(addr, length, prot, flags, fd, offset); | |
| Log("[mmap64] len: %zu, ptr: %p\n", length, ptr); | |
| return ptr; | |
| } | |
| int munmap(void *addr, size_t length) { | |
| if (initializing) return 0; | |
| Initialize(); | |
| if (insideBase) return (*munmapFunc)(addr, length); | |
| Log("[munmap] addr: %p, len: %zu\n", addr, length); | |
| return (*munmapFunc)(addr, length); | |
| } | |
| size_t malloc_usable_size(void *ptr) { | |
| // 絕對不能將 Static Memory 的指標傳給 glibc 的 malloc_usable_size,否則會 Crash | |
| if (IsStaticMemory(ptr)) return 0; | |
| if (initializing) return 0; | |
| Initialize(); | |
| if (insideBase) return (*mallocUsableSizeFunc)(ptr); | |
| if (g_records) { | |
| Keep<bool> k(insideBase, true); | |
| const MemoryRec* rec = g_records->Find(ptr); | |
| if (rec) return rec->size_; | |
| } | |
| return 0; | |
| } | |
| } // extern "C" | |
| // C++ Operators | |
| void *operator new(size_t size) { | |
| if (initializing) return StaticAlloc(size, 8); | |
| Initialize(); | |
| if (insideBase) { | |
| void* ptr = (*mallocFunc)(size); | |
| if (!ptr) throw std::bad_alloc(); | |
| return ptr; | |
| } | |
| void* ptr = (*mallocFunc)(size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[new] size: %zu, ptr: %p\n", size, ptr); | |
| if (!ptr) throw std::bad_alloc(); | |
| return ptr; | |
| } | |
| void *operator new[](size_t size) { | |
| if (initializing) return StaticAlloc(size, 8); | |
| Initialize(); | |
| if (insideBase) { | |
| void* ptr = (*mallocFunc)(size); | |
| if (!ptr) throw std::bad_alloc(); | |
| return ptr; | |
| } | |
| void* ptr = (*mallocFunc)(size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[new[]] size: %zu, ptr: %p\n", size, ptr); | |
| if (!ptr) throw std::bad_alloc(); | |
| return ptr; | |
| } | |
| void operator delete(void *ptr) noexcept { | |
| if (ptr == nullptr) return; | |
| if (IsStaticMemory(ptr)) { StaticFree(ptr); return; } | |
| if (initializing) return; | |
| Initialize(); | |
| if (insideBase) { | |
| (*freeFunc)(ptr); | |
| return; | |
| } | |
| if (g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Remove(ptr); | |
| } | |
| Log("[delete] ptr: %p\n", ptr); | |
| (*freeFunc)(ptr); | |
| } | |
| void operator delete[](void *ptr) noexcept { | |
| if (ptr == nullptr) return; | |
| if (IsStaticMemory(ptr)) { StaticFree(ptr); return; } | |
| if (initializing) return; | |
| Initialize(); | |
| if (insideBase) { | |
| (*freeFunc)(ptr); | |
| return; | |
| } | |
| if (g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Remove(ptr); | |
| } | |
| Log("[delete[]] ptr: %p\n", ptr); | |
| (*freeFunc)(ptr); | |
| } | |
| // C++98 Nothrow New/Delete | |
| void *operator new(size_t size, const std::nothrow_t &) noexcept { | |
| if (initializing) return StaticAlloc(size, 8); | |
| Initialize(); | |
| if (insideBase) return (*mallocFunc)(size); | |
| void* ptr = (*mallocFunc)(size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[new nothrow] size: %zu, ptr: %p\n", size, ptr); | |
| return ptr; | |
| } | |
| void *operator new[](size_t size, const std::nothrow_t &) noexcept { | |
| if (initializing) return StaticAlloc(size, 8); | |
| Initialize(); | |
| if (insideBase) return (*mallocFunc)(size); | |
| void* ptr = (*mallocFunc)(size); | |
| if (ptr && g_records) { | |
| Keep<bool> k(insideBase, true); | |
| g_records->Add(ptr, size); | |
| } | |
| Log("[new[] nothrow] size: %zu, ptr: %p\n", size, ptr); | |
| return ptr; | |
| } | |
| void operator delete(void *ptr, const std::nothrow_t &) noexcept { | |
| free(ptr); | |
| } | |
| void operator delete[](void *ptr, const std::nothrow_t &) noexcept { | |
| free(ptr); | |
| } | |
| // C++17 Aligned New/Delete | |
| #if __cplusplus >= 201703L | |
| void *operator new(size_t size, std::align_val_t al) { | |
| return memalign(size_t(al), size); | |
| } | |
| void *operator new[](size_t size, std::align_val_t al) { | |
| return memalign(size_t(al), size); | |
| } | |
| void *operator new(size_t size, std::align_val_t al, const std::nothrow_t &) noexcept { | |
| return memalign(size_t(al), size); | |
| } | |
| void *operator new[](size_t size, std::align_val_t al, const std::nothrow_t &) noexcept { | |
| return memalign(size_t(al), size); | |
| } | |
| void operator delete(void *ptr, std::align_val_t) noexcept { | |
| free(ptr); | |
| } | |
| void operator delete[](void *ptr, std::align_val_t) noexcept { | |
| free(ptr); | |
| } | |
| #endif | |
| #define TEST | |
| #ifdef TEST | |
| #include <dirent.h> | |
| #include <fcntl.h> | |
| #pragma GCC diagnostic ignored "-Wstringop-overflow" | |
| #pragma GCC diagnostic ignored "-Wmismatched-new-delete" | |
| extern "C" void dump_memory_records(FILE* stream); | |
| int main () | |
| { | |
| // Disable buffering for stdout to avoid the 4KB buffer allocation by the first printf | |
| setvbuf(stdout, NULL, _IONBF, 0); | |
| char *pc1 = new char; | |
| printf("malloc_usable_size(pc1): %zu\n", malloc_usable_size(pc1)); | |
| //char *pc2 = new char; | |
| char *pca1 = new char [11]; | |
| printf("malloc_usable_size(pca1): %zu\n", malloc_usable_size(pca1)); | |
| char *pca2 = new char [12]; | |
| printf("malloc_usable_size(pca2): %zu\n", malloc_usable_size(pca2)); | |
| double *pd = new double; | |
| printf("malloc_usable_size(pd): %zu\n", malloc_usable_size(pd)); | |
| double *pda = new double[2]; | |
| printf("malloc_usable_size(pda): %zu\n", malloc_usable_size(pda)); | |
| memset (pca1, 1, 12); | |
| delete pc1; | |
| //delete [] pc1; | |
| delete [] pca1; | |
| delete pca2; | |
| delete pd; | |
| delete [] pda; | |
| char *p = 0; | |
| char s[101] = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; | |
| size_t sz = 0; | |
| for (int i = 0 ; i < 200; i += 33) { | |
| sz += 101; | |
| p = (char*)realloc(p, sz); | |
| printf("malloc_usable_size(p): %zu\n", malloc_usable_size(p)); | |
| strcpy(p+sz-101, s); | |
| } | |
| DIR *d = opendir("."); | |
| closedir(d); | |
| int f = open(".", O_RDONLY); | |
| d = fdopendir(f); | |
| (void)f; | |
| dump_memory_records(stdout); | |
| exit(0); | |
| } | |
| #endif // TEST |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment