diff --git a/CMakeLists.txt b/CMakeLists.txt
index c89195a89a12792e43f7048159577caf84dea107..a1b2c16f4637f6500eab1a255fd45f1364483377 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -125,7 +125,9 @@ IF (NOT DEFINED TD_CLUSTER)
# debug flag
#
# ADD_DEFINITIONS(-D_CHECK_HEADER_FILE_)
- # ADD_DEFINITIONS(-D_TAOS_MEM_TEST_)
+ IF (${MEM_CHECK} MATCHES "true")
+ ADD_DEFINITIONS(-DTAOS_MEM_CHECK)
+ ENDIF ()
IF (TD_CLUSTER)
ADD_DEFINITIONS(-DCLUSTER)
diff --git a/src/client/jni/com_taosdata_jdbc_TSDBJNIConnector.h b/src/client/jni/com_taosdata_jdbc_TSDBJNIConnector.h
index 9f9632cadc3b0f8abb80d43f0eed87981f0db7c0..958252b4deca7708c99e6b762613813c2f9d330b 100644
--- a/src/client/jni/com_taosdata_jdbc_TSDBJNIConnector.h
+++ b/src/client/jni/com_taosdata_jdbc_TSDBJNIConnector.h
@@ -9,6 +9,22 @@ extern "C" {
#endif
#undef com_taosdata_jdbc_TSDBJNIConnector_INVALID_CONNECTION_POINTER_VALUE
#define com_taosdata_jdbc_TSDBJNIConnector_INVALID_CONNECTION_POINTER_VALUE 0LL
+/*
+ * Class: com_taosdata_jdbc_TSDBJNIConnector
+ * Method:
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_setAllocModeImp
+ (JNIEnv *, jclass, jint, jstring, jboolean);
+
+/*
+ * Class: com_taosdata_jdbc_TSDBJNIConnector
+ * Method:
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT void JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_dumpMemoryLeakImp
+ (JNIEnv *, jclass);
+
/*
* Class: com_taosdata_jdbc_TSDBJNIConnector
* Method: initImp
diff --git a/src/client/src/TSDBJNIConnector.c b/src/client/src/TSDBJNIConnector.c
index d958679544093b54e4c5090557376ac8f96abc43..71f983dadbe243e78330ea7f8d361bb5712f449e 100644
--- a/src/client/src/TSDBJNIConnector.c
+++ b/src/client/src/TSDBJNIConnector.c
@@ -111,6 +111,20 @@ void jniGetGlobalMethod(JNIEnv *env) {
jniTrace("native method register finished");
}
+JNIEXPORT void JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_setAllocModeImp(JNIEnv *env, jobject jobj, jint jMode, jstring jPath, jboolean jAutoDump) {
+ if (jPath != NULL) {
+ const char *path = (*env)->GetStringUTFChars(env, jPath, NULL);
+ taosSetAllocMode(jMode, path, !!jAutoDump);
+ (*env)->ReleaseStringUTFChars(env, jPath, path);
+ } else {
+ taosSetAllocMode(jMode, NULL, !!jAutoDump);
+ }
+}
+
+JNIEXPORT void JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_dumpMemoryLeakImp(JNIEnv *env, jobject jobj) {
+ taosDumpMemoryLeak();
+}
+
JNIEXPORT void JNICALL Java_com_taosdata_jdbc_TSDBJNIConnector_initImp(JNIEnv *env, jobject jobj, jstring jconfigDir) {
if (jconfigDir != NULL) {
const char *confDir = (*env)->GetStringUTFChars(env, jconfigDir, NULL);
diff --git a/src/connector/go/src/taosSql/utils.go b/src/connector/go/src/taosSql/utils.go
index a5a90059b50f57fab7b2f4e8749947a0af840265..a104322fcc9012ea0370bdbea9b89e6674daabf3 100755
--- a/src/connector/go/src/taosSql/utils.go
+++ b/src/connector/go/src/taosSql/utils.go
@@ -12,15 +12,25 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-
package taosSql
+/*
+#cgo CFLAGS : -I/usr/include
+#include
+#cgo LDFLAGS: -L/usr/lib -ltaos
+void taosSetAllocMode(int mode, const char* path, _Bool autoDump);
+void taosDumpMemoryLeak();
+*/
+import "C"
+
+
import (
"database/sql/driver"
"errors"
"fmt"
"sync/atomic"
"time"
+ "unsafe"
)
// Returns the bool value of the input.
@@ -398,3 +408,15 @@ func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
}
+/******************************************************************************
+* Utils for C memory issues debugging *
+******************************************************************************/
+func SetAllocMode(mode int32, path string) {
+ cpath := C.CString(path)
+ defer C.free(unsafe.Pointer(cpath))
+ C.taosSetAllocMode(C.int(mode), cpath, false)
+}
+
+func DumpMemoryLeak() {
+ C.taosDumpMemoryLeak()
+}
diff --git a/src/inc/tutil.h b/src/inc/tutil.h
index 7725bd2d1b337ad6cd8c908a616a464448c28e66..683927c816d5284ac5b30af851e4e1cde28b5f25 100644
--- a/src/inc/tutil.h
+++ b/src/inc/tutil.h
@@ -187,18 +187,35 @@ static FORCE_INLINE void taosEncryptPass(uint8_t *inBuf, unsigned int inLen, cha
char *taosIpStr(uint32_t ipInt);
-#ifdef _TAOS_MEM_TEST_
-// Use during test to simulate the success and failure scenarios of memory allocation
-extern void* taos_malloc(unsigned int size, char* _func);
-extern void* taos_calloc(unsigned int num, unsigned int size, char* _func);
-extern void* taos_realloc(void* ptr, unsigned int size, char* _func);
-extern void taos_free(void* ptr);
-#define malloc(size) taos_malloc(size, __FUNCTION__)
-#define calloc(num, size) taos_calloc(num, size, __FUNCTION__)
-#define realloc(ptr, size) taos_realloc(ptr, size, __FUNCTION__)
-#define free(ptr) taos_free(ptr)
-#endif
-
+#define TAOS_ALLOC_MODE_DEFAULT 0
+#define TAOS_ALLOC_MODE_RANDOM_FAIL 1
+#define TAOS_ALLOC_MODE_DETECT_LEAK 2
+void taosSetAllocMode(int mode, const char* path, bool autoDump);
+void taosDumpMemoryLeak();
+
+#ifdef TAOS_MEM_CHECK
+
+void * taos_malloc(size_t size, const char *file, uint32_t line);
+void * taos_calloc(size_t num, size_t size, const char *file, uint32_t line);
+void * taos_realloc(void *ptr, size_t size, const char *file, uint32_t line);
+void taos_free(void *ptr, const char *file, uint32_t line);
+char * taos_strdup(const char *str, const char *file, uint32_t line);
+char * taos_strndup(const char *str, size_t size, const char *file, uint32_t line);
+ssize_t taos_getline(char **lineptr, size_t *n, FILE *stream, const char *file, uint32_t line);
+
+#ifndef TAOS_MEM_CHECK_IMPL
+
+#define malloc(size) taos_malloc(size, __FILE__, __LINE__)
+#define calloc(num, size) taos_calloc(num, size, __FILE__, __LINE__)
+#define realloc(ptr, size) taos_realloc(ptr, size, __FILE__, __LINE__)
+#define free(ptr) taos_free(ptr, __FILE__, __LINE__)
+#define strdup(str) taos_strdup(str, __FILE__, __LINE__)
+#define strndup(str, size) taos_strndup(str, size, __FILE__, __LINE__)
+#define getline(lineptr, n, stream) taos_getline(lineptr, n, stream, __FILE__, __LINE__)
+
+#endif // TAOS_MEM_CHECK_IMPL
+
+#endif // TAOS_MEM_CHECK
#ifdef __cplusplus
}
diff --git a/src/os/linux/src/tsystem.c b/src/os/linux/src/tsystem.c
index 70b970e2a81b6f81209e3f9352a5621e008e14d0..03192bb9cc49bd8ba613662d27ed4d38364dd7ac 100644
--- a/src/os/linux/src/tsystem.c
+++ b/src/os/linux/src/tsystem.c
@@ -81,6 +81,7 @@ bool taosGetProcMemory(float *memoryUsedMB) {
char * line = NULL;
while (!feof(fp)) {
tfree(line);
+ len = 0;
getline(&line, &len, fp);
if (line == NULL) {
break;
@@ -137,7 +138,7 @@ bool taosGetProcCpuInfo(ProcCpuInfo *cpuInfo) {
return false;
}
- size_t len;
+ size_t len = 0;
char * line = NULL;
getline(&line, &len, fp);
if (line == NULL) {
@@ -409,6 +410,7 @@ bool taosGetCardInfo(int64_t *bytes) {
while (!feof(fp)) {
tfree(line);
+ len = 0;
getline(&line, &len, fp);
if (line == NULL) {
break;
@@ -480,6 +482,7 @@ bool taosReadProcIO(int64_t *readbyte, int64_t *writebyte) {
while (!feof(fp)) {
tfree(line);
+ len = 0;
getline(&line, &len, fp);
if (line == NULL) {
break;
diff --git a/src/system/detail/src/dnodeService.c b/src/system/detail/src/dnodeService.c
index a14ec1fda6aee8d3e7942b5890d1301fb11c66df..9764afc5934c84d5672e37312bf1598803158ee1 100644
--- a/src/system/detail/src/dnodeService.c
+++ b/src/system/detail/src/dnodeService.c
@@ -61,6 +61,20 @@ int main(int argc, char *argv[]) {
return 0;
} else if (strcmp(argv[i], "-k") == 0) {
dnodeParseParameterK();
+#ifdef TAOS_MEM_CHECK
+ } else if (strcmp(argv[i], "--alloc-random-fail") == 0) {
+ if ((i < argc - 1) && (argv[i+1][0] != '-')) {
+ taosSetAllocMode(TAOS_ALLOC_MODE_RANDOM_FAIL, argv[++i], true);
+ } else {
+ taosSetAllocMode(TAOS_ALLOC_MODE_RANDOM_FAIL, NULL, true);
+ }
+ } else if (strcmp(argv[i], "--detect-mem-leak") == 0) {
+ if ((i < argc - 1) && (argv[i+1][0] != '-')) {
+ taosSetAllocMode(TAOS_ALLOC_MODE_DETECT_LEAK, argv[++i], true);
+ } else {
+ taosSetAllocMode(TAOS_ALLOC_MODE_DETECT_LEAK, NULL, true);
+ }
+#endif
}
}
diff --git a/src/util/src/tmem.c b/src/util/src/tmem.c
index 462da884b9da8aaaac82d61c8332a2cb795f1486..2625e4e5e6a645c4de2bd97534324b037acb4aaa 100644
--- a/src/util/src/tmem.c
+++ b/src/util/src/tmem.c
@@ -16,63 +16,462 @@
#include "os.h"
#include "tlog.h"
+#define TAOS_MEM_CHECK_IMPL
+#include "tutil.h"
+
+
+#ifdef TAOS_MEM_CHECK
+
+static int allocMode = TAOS_ALLOC_MODE_DEFAULT;
+static FILE* fpAllocLog = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// memory allocator which fails randomly
+
extern int32_t taosGetTimestampSec();
-static int32_t startTime = 0;
-static int64_t m_curLimit = 100*1024;
+static int32_t startTime = INT32_MAX;;
-bool isMallocMem(unsigned int size, char* _func) {
- if (0 == startTime) {
- startTime = taosGetTimestampSec();
- return true;
- } else {
- int32_t currentTime = taosGetTimestampSec();
- if (currentTime - startTime < 10) return true;
+static bool random_alloc_fail(size_t size, const char* file, uint32_t line) {
+ if (taosGetTimestampSec() < startTime) {
+ return false;
}
- if (size > m_curLimit) {
- if (3 == rand() % 20) {
- pTrace("====no alloc mem in func: %s, size:%d", _func, size);
- return false;
- }
+ if (size < 100 * (size_t)1024) {
+ return false;
+ }
+
+ if (rand() % 20 != 0) {
+ return false;
+ }
+
+ if (fpAllocLog != NULL) {
+ fprintf(fpAllocLog, "%s:%d: memory allocation of %zu bytes will fail.\n", file, line, size);
}
return true;
}
-void* taos_malloc(unsigned int size, char* _func) {
+static void* malloc_random(size_t size, const char* file, uint32_t line) {
+ return random_alloc_fail(size, file, line) ? NULL : malloc(size);
+}
+
+static void* calloc_random(size_t num, size_t size, const char* file, uint32_t line) {
+ return random_alloc_fail(num * size, file, line) ? NULL : calloc(num, size);
+}
+
+static void* realloc_random(void* ptr, size_t size, const char* file, uint32_t line) {
+ return random_alloc_fail(size, file, line) ? NULL : realloc(ptr, size);
+}
+
+static char* strdup_random(const char* str, const char* file, uint32_t line) {
+ size_t len = strlen(str);
+ return random_alloc_fail(len + 1, file, line) ? NULL : strdup(str);
+}
+
+static char* strndup_random(const char* str, size_t size, const char* file, uint32_t line) {
+ size_t len = strlen(str);
+ if (len > size) {
+ len = size;
+ }
+ return random_alloc_fail(len + 1, file, line) ? NULL : strndup(str, len);
+}
+
+static ssize_t getline_random(char **lineptr, size_t *n, FILE *stream, const char* file, uint32_t line) {
+ return random_alloc_fail(*n, file, line) ? -1 : getline(lineptr, n, stream);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// memory allocator with leak detection
+
+#define MEMBLK_MAGIC 0x55AA
+
+typedef struct SMemBlock {
+ const char* file;
+ uint16_t line;
+ uint16_t magic;
+ uint32_t size;
+ struct SMemBlock* prev;
+ struct SMemBlock* next;
+ // TODO: need pading in 32bit platform
+ char data[0];
+} SMemBlock;
+
+static SMemBlock *blocks = NULL;
+static uintptr_t lock = 0;
+
+static void add_mem_block(SMemBlock* blk) {
+ blk->prev = NULL;
+ while (atomic_val_compare_exchange_ptr(&lock, 0, 1) != 0);
+ blk->next = blocks;
+ if (blocks != NULL) {
+ blocks->prev = blk;
+ }
+ blocks = blk;
+ atomic_store_ptr(&lock, 0);
+}
+
+static void remove_mem_block(SMemBlock* blk) {
+ while (atomic_val_compare_exchange_ptr(&lock, 0, 1) != 0);
+
+ if (blocks == blk) {
+ blocks = blk->next;
+ }
+ if (blk->prev != NULL) {
+ blk->prev->next = blk->next;
+ }
+ if (blk->next != NULL) {
+ blk->next->prev = blk->prev;
+ }
+
+ atomic_store_ptr(&lock, 0);
+
+ blk->prev = NULL;
+ blk->next = NULL;
+}
+
+static void free_detect_leak(void* ptr, const char* file, uint32_t line) {
+ if (ptr == NULL) {
+ return;
+ }
+
+ SMemBlock* blk = (SMemBlock*)(((char*)ptr) - sizeof(SMemBlock));
+ if (blk->magic != MEMBLK_MAGIC) {
+ if (fpAllocLog != NULL) {
+ fprintf(fpAllocLog, "%s:%d: memory is allocated by default allocator.\n", file, line);
+ }
+ free(ptr);
+ return;
+ }
+
+ remove_mem_block(blk);
+ free(blk);
+}
- if (false == isMallocMem(size, _func)) {
+static void* malloc_detect_leak(size_t size, const char* file, uint32_t line) {
+ if (size == 0) {
return NULL;
}
-
- void *p = NULL;
- p = malloc(size);
+
+ SMemBlock *blk = (SMemBlock*)malloc(size + sizeof(SMemBlock));
+ if (blk == NULL) {
+ return NULL;
+ }
+
+ if (line > UINT16_MAX && fpAllocLog != NULL) {
+ fprintf(fpAllocLog, "%s:%d: line number too large.\n", file, line);
+ }
+
+ if (size > UINT32_MAX && fpAllocLog != NULL) {
+ fprintf(fpAllocLog, "%s:%d: size too large: %zu.\n", file, line, size);
+ }
+
+ blk->file = file;
+ blk->line = (uint16_t)line;
+ blk->magic = MEMBLK_MAGIC;
+ blk->size = size;
+ add_mem_block(blk);
+
+ return blk->data;
+}
+
+static void* calloc_detect_leak(size_t num, size_t size, const char* file, uint32_t line) {
+ size *= num;
+ void* p = malloc_detect_leak(size, file, line);
+ if (p != NULL) {
+ memset(p, 0, size);
+ }
return p;
}
-void* taos_calloc(unsigned int num, unsigned int size, char* _func) {
-
- if (false == isMallocMem(size, _func)) {
+static void* realloc_detect_leak(void* ptr, size_t size, const char* file, uint32_t line) {
+ if (size == 0) {
+ free_detect_leak(ptr, file, line);
return NULL;
}
-
- void *p = NULL;
- p = calloc(num, size);
+
+ if (ptr == NULL) {
+ return malloc_detect_leak(size, file, line);
+ }
+
+ SMemBlock* blk = ((char*)ptr) - sizeof(SMemBlock);
+ if (blk->magic != MEMBLK_MAGIC) {
+ if (fpAllocLog != NULL) {
+ fprintf(fpAllocLog, "%s:%d: memory is allocated by default allocator.\n", file, line);
+ }
+ return realloc(ptr, size);
+ }
+
+ remove_mem_block(blk);
+
+ void* p = realloc(blk, size + sizeof(SMemBlock));
+ if (p == NULL) {
+ add_mem_block(blk);
+ return NULL;
+ }
+
+ if (size > UINT32_MAX && fpAllocLog != NULL) {
+ fprintf(fpAllocLog, "%s:%d: size too large: %zu.\n", file, line, size);
+ }
+
+ blk = (SMemBlock*)p;
+ blk->size = size;
+ add_mem_block(blk);
+ return blk->data;
+}
+
+static char* strdup_detect_leak(const char* str, const char* file, uint32_t line) {
+ size_t len = strlen(str);
+ char *p = malloc_detect_leak(len + 1, file, line);
+ if (p != NULL) {
+ memcpy(p, str, len);
+ p[len] = 0;
+ }
return p;
}
-void* taos_realloc(void* ptr, unsigned int size, char* _func) {
-
- if (false == isMallocMem(size, _func)) {
- return NULL;
+static char* strndup_detect_leak(const char* str, size_t size, const char* file, uint32_t line) {
+ size_t len = strlen(str);
+ if (len > size) {
+ len = size;
+ }
+ char *p = malloc_detect_leak(len + 1, file, line);
+ if (p != NULL) {
+ memcpy(p, str, len);
+ p[len] = 0;
}
-
- void *p = NULL;
- p = realloc(ptr, size);
return p;
}
-void taos_free(void* ptr) {
- free(ptr);
+static ssize_t getline_detect_leak(char **lineptr, size_t *n, FILE *stream, const char* file, uint32_t line) {
+ char* buf = NULL;
+ size_t bufSize = 0;
+ ssize_t size = getline(&buf, &bufSize, stream);
+ if (size != -1) {
+ if (*n < size + 1) {
+ void* p = realloc_detect_leak(*lineptr, size + 1, file, line);
+ if (p == NULL) {
+ free(buf);
+ return -1;
+ }
+ *lineptr = (char*)p;
+ *n = size + 1;
+ }
+ memcpy(*lineptr, buf, size + 1);
+ }
+
+ free(buf);
+ return size;
+}
+
+static void dump_memory_leak() {
+ const char* hex = "0123456789ABCDEF";
+ const char* fmt = ":%d: addr=%p, size=%d, content(first 16 bytes)=";
+ size_t numOfBlk = 0, totalSize = 0;
+
+ if (fpAllocLog == NULL) {
+ return;
+ }
+
+ fputs("memory blocks allocated but not freed before exit:\n", fpAllocLog);
+
+ while (atomic_val_compare_exchange_ptr(&lock, 0, 1) != 0);
+
+ for (SMemBlock* blk = blocks; blk != NULL; blk = blk->next) {
+ ++numOfBlk;
+ totalSize += blk->size;
+
+ fputs(blk->file, fpAllocLog);
+ fprintf(fpAllocLog, fmt, blk->line, blk->data, blk->size);
+
+ char sep = '\'';
+ size_t size = blk->size > 16 ? 16 : blk->size;
+ for (size_t i = 0; i < size; ++i) {
+ uint8_t c = (uint8_t)(blk->data[i]);
+ fputc(sep, fpAllocLog);
+ sep = ' ';
+ fputc(hex[c >> 4], fpAllocLog);
+ fputc(hex[c & 0x0f], fpAllocLog);
+ }
+
+ fputs("'\n", fpAllocLog);
+ }
+
+ atomic_store_ptr(&lock, 0);
+
+ fprintf(fpAllocLog, "\nnumber of blocks: %zu, total bytes: %zu\n", numOfBlk, totalSize);
+ fflush(fpAllocLog);
+}
+
+static void dump_memory_leak_on_sig(int sig) {
+ fprintf(fpAllocLog, "signal %d received.\n", sig);
+
+ // restore default signal handler
+ struct sigaction act = {0};
+ act.sa_handler = SIG_DFL;
+ sigaction(sig, &act, NULL);
+
+ dump_memory_leak();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// interface functions
+
+void* taos_malloc(size_t size, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return malloc(size);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return malloc_random(size, file, line);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return malloc_detect_leak(size, file, line);
+ }
+ return malloc(size);
+}
+
+void* taos_calloc(size_t num, size_t size, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return calloc(num, size);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return calloc_random(num, size, file, line);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return calloc_detect_leak(num, size, file, line);
+ }
+ return calloc(num, size);
+}
+
+void* taos_realloc(void* ptr, size_t size, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return realloc(ptr, size);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return realloc_random(ptr, size, file, line);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return realloc_detect_leak(ptr, size, file, line);
+ }
+ return realloc(ptr, size);
+}
+
+void taos_free(void* ptr, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return free(ptr);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return free(ptr);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return free_detect_leak(ptr, file, line);
+ }
+ return free(ptr);
+}
+
+char* taos_strdup(const char* str, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return strdup(str);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return strdup_random(str, file, line);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return strdup_detect_leak(str, file, line);
+ }
+ return strdup(str);
+}
+
+char* taos_strndup(const char* str, size_t size, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return strndup(str, size);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return strndup_random(str, size, file, line);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return strndup_detect_leak(str, size, file, line);
+ }
+ return strndup(str, size);
+}
+
+ssize_t taos_getline(char **lineptr, size_t *n, FILE *stream, const char* file, uint32_t line) {
+ switch (allocMode) {
+ case TAOS_ALLOC_MODE_DEFAULT:
+ return getline(lineptr, n, stream);
+
+ case TAOS_ALLOC_MODE_RANDOM_FAIL:
+ return getline_random(lineptr, n, stream, file, line);
+
+ case TAOS_ALLOC_MODE_DETECT_LEAK:
+ return getline_detect_leak(lineptr, n, stream, file, line);
+ }
+ return getline(lineptr, n, stream);
+}
+
+static void close_alloc_log() {
+ if (fpAllocLog != NULL) {
+ if (fpAllocLog != stdout) {
+ fclose(fpAllocLog);
+ }
+ fpAllocLog = NULL;
+ }
+}
+
+void taosSetAllocMode(int mode, const char* path, bool autoDump) {
+ assert(mode >= TAOS_ALLOC_MODE_DEFAULT);
+ assert(mode <= TAOS_ALLOC_MODE_DETECT_LEAK);
+
+ if (fpAllocLog != NULL || allocMode != TAOS_ALLOC_MODE_DEFAULT) {
+ printf("memory allocation mode can only be set once.\n");
+ return;
+ }
+
+ if (path == NULL || path[0] == 0) {
+ fpAllocLog = stdout;
+ } else if ((fpAllocLog = fopen(path, "w")) != NULL) {
+ atexit(close_alloc_log);
+ } else {
+ printf("failed to open memory allocation log file '%s', errno=%d\n", path, errno);
+ return;
+ }
+
+ allocMode = mode;
+
+ if (mode == TAOS_ALLOC_MODE_RANDOM_FAIL) {
+ startTime = taosGetTimestampSec() + 10;
+ return;
+ }
+
+ if (autoDump && mode == TAOS_ALLOC_MODE_DETECT_LEAK) {
+ atexit(dump_memory_leak);
+
+ struct sigaction act = {0};
+ act.sa_handler = dump_memory_leak_on_sig;
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ }
+}
+
+void taosDumpMemoryLeak() {
+ dump_memory_leak();
+ close_alloc_log();
+}
+
+#else // 'TAOS_MEM_CHECK' not defined
+
+void taosSetAllocMode(int mode, const char* path, bool autoDump) {
+ // do nothing
+}
+
+void taosDumpMemoryLeak() {
+ // do nothing
}
+#endif // TAOS_MEM_CHECK