AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdio.h>4#include <stdlib.h>5#include <string.h>6#include <errno.h>7#include <time.h>8#ifdef _WIN329#include <io.h>10#define unlink _unlink11#else12#include <unistd.h>13#endif14 15/* Externs from mem2.c (public functions) */16extern void sqlite3MemSetDefault(void);17extern void sqlite3MemdebugSettitle(const char *zTitle);18extern void sqlite3MemdebugDump(const char *zFilename);19extern void sqlite3MemdebugBacktrace(int depth);20 21/* Test wrapper for static sqlite3MemFree provided in source */22extern void test_sqlite3MemFree(void *pPrior);23 24static int g_fileCounter = 0;25 26/* Ensure mem2 allocator is active and the state is clean for each test */27static void ensure_mem2_allocator(void){28 /* Reset SQLite so that sqlite3_config can take effect reliably */29 sqlite3_shutdown();30 /* Install mem2 allocator methods into global config */31 sqlite3MemSetDefault();32 /* Initialize mem2 allocator if an init function is provided */33 if( sqlite3GlobalConfig.m.xInit ){34 sqlite3GlobalConfig.m.xInit(0);35 }36 /* Keep backtrace off by default unless a test enables it */37 sqlite3MemdebugBacktrace(0);38 /* Reset title */39 sqlite3MemdebugSettitle("");40}41 42/* Create a unique temporary filename for dumps */43static void make_temp_filename(char *buf, size_t n, const char *tag){44 long t = (long)time(NULL);45 snprintf(buf, n, "test_mem2_%s_%ld_%d.log", tag ? tag : "dump", t, g_fileCounter++);46}47 48/* Read entire file contents into a malloc'd buffer. Return 0 on success. */49static int read_file_all(const char *zFilename, char **pzOut, size_t *pnOut){50 FILE *f = fopen(zFilename, "rb");51 if( !f ){52 return errno ? errno : -1;53 }54 if( fseek(f, 0, SEEK_END)!=0 ){55 fclose(f);56 return -1;57 }58 long sz = ftell(f);59 if( sz < 0 ){60 fclose(f);61 return -1;62 }63 rewind(f);64 char *buf = (char*)malloc((size_t)sz + 1);65 if( !buf ){66 fclose(f);67 return ENOMEM;68 }69 size_t nRead = fread(buf, 1, (size_t)sz, f);70 fclose(f);71 buf[nRead] = '\0';72 if( pzOut ) *pzOut = buf; else free(buf);73 if( pnOut ) *pnOut = nRead;74 return 0;75}76 77/* Check if the dump file contains a particular substring */78static int file_contains(const char *zFilename, const char *needle){79 char *content = NULL;80 size_t n = 0;81 int rc = read_file_all(zFilename, &content, &n);82 if( rc!=0 ){83 return 0;84 }85 int found = (content && needle && strstr(content, needle)!=NULL);86 free(content);87 return found;88}89 90void setUp(void) {91 ensure_mem2_allocator();92}93void tearDown(void) {94 /* Nothing to cleanup */95}96 97#ifdef SQLITE_MEMDEBUG98 99/* Helper to allocate with a given title and size */100static void* titled_alloc(const char *title, int nByte){101 sqlite3MemdebugSettitle(title ? title : "");102 void *p = NULL;103 if( sqlite3GlobalConfig.m.xMalloc ){104 p = sqlite3GlobalConfig.m.xMalloc(nByte);105 }106 return p;107}108 109void test_sqlite3MemFree_removes_head_allocation(void){110 void *pA = titled_alloc("ALLOC_A_HEAD", 40);111 void *pB = titled_alloc("ALLOC_B_HEAD", 32);112 TEST_ASSERT_NOT_NULL(pA);113 TEST_ASSERT_NOT_NULL(pB);114 115 char fname1[256], fname2[256];116 make_temp_filename(fname1, sizeof(fname1), "head_pre");117 make_temp_filename(fname2, sizeof(fname2), "head_post");118 119 sqlite3MemdebugDump(fname1);120 TEST_ASSERT_TRUE_MESSAGE(file_contains(fname1, "ALLOC_A_HEAD"), "Pre-dump missing ALLOC_A_HEAD");121 TEST_ASSERT_TRUE_MESSAGE(file_contains(fname1, "ALLOC_B_HEAD"), "Pre-dump missing ALLOC_B_HEAD");122 123 /* Free the head allocation */124 test_sqlite3MemFree(pA);125 126 sqlite3MemdebugDump(fname2);127 TEST_ASSERT_FALSE_MESSAGE(file_contains(fname2, "ALLOC_A_HEAD"), "Freed head allocation still present in dump");128 TEST_ASSERT_TRUE_MESSAGE(file_contains(fname2, "ALLOC_B_HEAD"), "Unfreed tail allocation missing from dump");129 130 /* cleanup */131 test_sqlite3MemFree(pB);132 unlink(fname1);133 unlink(fname2);134}135 136void test_sqlite3MemFree_removes_tail_allocation(void){137 void *pA = titled_alloc("ALLOC_A_TAIL", 24);138 void *pB = titled_alloc("ALLOC_B_TAIL", 88);139 TEST_ASSERT_NOT_NULL(pA);140 TEST_ASSERT_NOT_NULL(pB);141 142 char fname1[256], fname2[256];143 make_temp_filename(fname1, sizeof(fname1), "tail_pre");144 make_temp_filename(fname2, sizeof(fname2), "tail_post");145 146 sqlite3MemdebugDump(fname1);147 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_A_TAIL"));148 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_B_TAIL"));149 150 /* Free the tail allocation */151 test_sqlite3MemFree(pB);152 153 sqlite3MemdebugDump(fname2);154 TEST_ASSERT_TRUE_MESSAGE(file_contains(fname2, "ALLOC_A_TAIL"), "Unfreed head allocation missing from dump");155 TEST_ASSERT_FALSE_MESSAGE(file_contains(fname2, "ALLOC_B_TAIL"), "Freed tail allocation still present in dump");156 157 /* cleanup */158 test_sqlite3MemFree(pA);159 unlink(fname1);160 unlink(fname2);161}162 163void test_sqlite3MemFree_removes_middle_allocation(void){164 void *pA = titled_alloc("ALLOC_A_MID", 16);165 void *pB = titled_alloc("ALLOC_B_MID", 48);166 void *pC = titled_alloc("ALLOC_C_MID", 72);167 TEST_ASSERT_NOT_NULL(pA);168 TEST_ASSERT_NOT_NULL(pB);169 TEST_ASSERT_NOT_NULL(pC);170 171 char fname1[256], fname2[256];172 make_temp_filename(fname1, sizeof(fname1), "mid_pre");173 make_temp_filename(fname2, sizeof(fname2), "mid_post");174 175 sqlite3MemdebugDump(fname1);176 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_A_MID"));177 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_B_MID"));178 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_C_MID"));179 180 /* Free the middle allocation */181 test_sqlite3MemFree(pB);182 183 sqlite3MemdebugDump(fname2);184 TEST_ASSERT_TRUE(file_contains(fname2, "ALLOC_A_MID"));185 TEST_ASSERT_FALSE_MESSAGE(file_contains(fname2, "ALLOC_B_MID"), "Freed middle allocation still present in dump");186 TEST_ASSERT_TRUE(file_contains(fname2, "ALLOC_C_MID"));187 188 /* cleanup */189 test_sqlite3MemFree(pA);190 test_sqlite3MemFree(pC);191 unlink(fname1);192 unlink(fname2);193}194 195void test_sqlite3MemFree_with_backtrace_enabled(void){196 /* Enable backtrace and make sure free still removes the allocation */197 sqlite3MemdebugBacktrace(6);198 199 void *p = titled_alloc("ALLOC_BT", 64);200 TEST_ASSERT_NOT_NULL(p);201 202 char fname1[256], fname2[256];203 make_temp_filename(fname1, sizeof(fname1), "bt_pre");204 make_temp_filename(fname2, sizeof(fname2), "bt_post");205 206 sqlite3MemdebugDump(fname1);207 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_BT"));208 209 test_sqlite3MemFree(p);210 211 sqlite3MemdebugDump(fname2);212 TEST_ASSERT_FALSE_MESSAGE(file_contains(fname2, "ALLOC_BT"), "Freed allocation with backtrace still present");213 214 unlink(fname1);215 unlink(fname2);216 217 /* Reset backtrace depth */218 sqlite3MemdebugBacktrace(0);219}220 221void test_sqlite3MemFree_various_sizes_alignment(void){222 /* Allocate several sizes to exercise roundup and REARGUARD placement paths */223 void *p1 = titled_alloc("ALLOC_S1", 1);224 void *p7 = titled_alloc("ALLOC_S7", 7);225 void *p8 = titled_alloc("ALLOC_S8", 8);226 void *p9 = titled_alloc("ALLOC_S9", 9);227 TEST_ASSERT_NOT_NULL(p1);228 TEST_ASSERT_NOT_NULL(p7);229 TEST_ASSERT_NOT_NULL(p8);230 TEST_ASSERT_NOT_NULL(p9);231 232 char fname1[256], fname2[256];233 make_temp_filename(fname1, sizeof(fname1), "sizes_pre");234 make_temp_filename(fname2, sizeof(fname2), "sizes_post");235 236 sqlite3MemdebugDump(fname1);237 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_S1"));238 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_S7"));239 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_S8"));240 TEST_ASSERT_TRUE(file_contains(fname1, "ALLOC_S9"));241 242 /* Free a subset and verify remaining */243 test_sqlite3MemFree(p1);244 test_sqlite3MemFree(p8);245 246 sqlite3MemdebugDump(fname2);247 TEST_ASSERT_FALSE(file_contains(fname2, "ALLOC_S1"));248 TEST_ASSERT_TRUE(file_contains(fname2, "ALLOC_S7"));249 TEST_ASSERT_FALSE(file_contains(fname2, "ALLOC_S8"));250 TEST_ASSERT_TRUE(file_contains(fname2, "ALLOC_S9"));251 252 /* cleanup rest */253 test_sqlite3MemFree(p7);254 test_sqlite3MemFree(p9);255 unlink(fname1);256 unlink(fname2);257}258 259#else /* !SQLITE_MEMDEBUG */260 261void test_sqlite3MemFree_noop_because_feature_disabled(void){262 /* When SQLITE_MEMDEBUG is not enabled, this module is not active. */263 TEST_IGNORE_MESSAGE("SQLITE_MEMDEBUG not defined; mem2 tests skipped.");264}265 266#endif /* SQLITE_MEMDEBUG */267 268int main(void) {269 UNITY_BEGIN();270 271#ifdef SQLITE_MEMDEBUG272 RUN_TEST(test_sqlite3MemFree_removes_head_allocation);273 RUN_TEST(test_sqlite3MemFree_removes_tail_allocation);274 RUN_TEST(test_sqlite3MemFree_removes_middle_allocation);275 RUN_TEST(test_sqlite3MemFree_with_backtrace_enabled);276 RUN_TEST(test_sqlite3MemFree_various_sizes_alignment);277#else278 RUN_TEST(test_sqlite3MemFree_noop_because_feature_disabled);279#endif280 281 return UNITY_END();282}