/src/tdengine/source/os/src/osMemory.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com> |
3 | | * |
4 | | * This program is free software: you can use, redistribute, and/or modify |
5 | | * it under the terms of the GNU Affero General Public License, version 3 |
6 | | * or later ("AGPL"), as published by the Free Software Foundation. |
7 | | * |
8 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
9 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | | * FITNESS FOR A PARTICULAR PURPOSE. |
11 | | * |
12 | | * You should have received a copy of the GNU Affero General Public License |
13 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
14 | | */ |
15 | | |
16 | | #define ALLOW_FORBID_FUNC |
17 | | #ifdef _TD_DARWIN_64 |
18 | | #include <malloc/malloc.h> |
19 | | #else |
20 | | #include <malloc.h> |
21 | | #endif |
22 | | #include "os.h" |
23 | | #include "tdef.h" |
24 | | |
25 | | int32_t tsRandErrChance = 1; |
26 | | int64_t tsRandErrDivisor = 10001; |
27 | | int64_t tsRandErrScope = (RAND_ERR_MEMORY | RAND_ERR_FILE | RAND_ERR_NETWORK); |
28 | | threadlocal bool tsEnableRandErr = 0; |
29 | | |
30 | | #if defined(USE_TD_MEMORY) || defined(USE_ADDR2LINE) |
31 | | |
32 | | #define TD_MEMORY_SYMBOL ('T' << 24 | 'A' << 16 | 'O' << 8 | 'S') |
33 | | |
34 | | #define TD_MEMORY_STACK_TRACE_DEPTH 10 |
35 | | |
36 | | typedef struct TdMemoryInfo *TdMemoryInfoPtr; |
37 | | |
38 | | typedef struct TdMemoryInfo { |
39 | | int32_t symbol; |
40 | | int32_t memorySize; |
41 | | void *stackTrace[TD_MEMORY_STACK_TRACE_DEPTH]; // gdb: disassemble /m 0xXXX |
42 | | // TdMemoryInfoPtr pNext; |
43 | | // TdMemoryInfoPtr pPrev; |
44 | | } TdMemoryInfo; |
45 | | |
46 | | // static TdMemoryInfoPtr GlobalMemoryPtr = NULL; |
47 | | |
48 | | #ifdef WINDOWS |
49 | | #define tstrdup(str) _strdup(str) |
50 | | |
51 | | int32_t taosBackTrace(void **buffer, int32_t size) { |
52 | | int32_t frame = 0; |
53 | | return frame; |
54 | | } |
55 | | |
56 | | #ifdef USE_ADDR2LINE |
57 | | #include <DbgHelp.h> |
58 | | #pragma comment(lib, "dbghelp.lib") |
59 | | |
60 | | void taosPrintBackTrace() { |
61 | | #define MAX_STACK_FRAMES 20 |
62 | | |
63 | | void *pStack[MAX_STACK_FRAMES]; |
64 | | |
65 | | HANDLE process = GetCurrentProcess(); |
66 | | SymInitialize(process, NULL, TRUE); |
67 | | WORD frames = CaptureStackBackTrace(1, MAX_STACK_FRAMES, pStack, NULL); |
68 | | |
69 | | char buf_tmp[1024]; |
70 | | for (WORD i = 0; i < frames; ++i) { |
71 | | DWORD64 address = (DWORD64)(pStack[i]); |
72 | | |
73 | | DWORD64 displacementSym = 0; |
74 | | char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; |
75 | | PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; |
76 | | pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
77 | | pSymbol->MaxNameLen = MAX_SYM_NAME; |
78 | | |
79 | | DWORD displacementLine = 0; |
80 | | IMAGEHLP_LINE64 line; |
81 | | // SymSetOptions(SYMOPT_LOAD_LINES); |
82 | | line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
83 | | |
84 | | if (SymFromAddr(process, address, &displacementSym, pSymbol) && |
85 | | SymGetLineFromAddr64(process, address, &displacementLine, &line)) { |
86 | | snprintf(buf_tmp, sizeof(buf_tmp), "BackTrace %08" PRId64 " %s:%d %s\n", taosGetSelfPthreadId(), line.FileName, |
87 | | line.LineNumber, pSymbol->Name); |
88 | | } else { |
89 | | snprintf(buf_tmp, sizeof(buf_tmp), "BackTrace error: %d\n", GetLastError()); |
90 | | } |
91 | | write(1, buf_tmp, strlen(buf_tmp)); |
92 | | } |
93 | | } |
94 | | #endif |
95 | | #else |
96 | | #define tstrdup(str) strdup(str) |
97 | | |
98 | | #include <execinfo.h> |
99 | | |
100 | | #define STACKCALL __attribute__((regparm(1), noinline)) |
101 | | void **STACKCALL taosGetEbp(void) { |
102 | | void **ebp = NULL; |
103 | | __asm__ __volatile__("mov %%rbp, %0;\n\t" |
104 | | : "=m"(ebp) /* output */ |
105 | | : /* input */ |
106 | | : "memory"); /* not affect register */ |
107 | | return (void **)(*ebp); |
108 | | } |
109 | | |
110 | | int32_t taosBackTrace(void **buffer, int32_t size) { |
111 | | int32_t frame = 0; |
112 | | void **ebp; |
113 | | void **ret = NULL; |
114 | | size_t func_frame_distance = 0; |
115 | | if (buffer != NULL && size > 0) { |
116 | | ebp = taosGetEbp(); |
117 | | func_frame_distance = (size_t)*ebp - (size_t)ebp; |
118 | | while (ebp && frame < size && (func_frame_distance < (1ULL << 24)) // assume function ebp more than 16M |
119 | | && (func_frame_distance > 0)) { |
120 | | ret = ebp + 1; |
121 | | buffer[frame++] = *ret; |
122 | | ebp = (void **)(*ebp); |
123 | | func_frame_distance = (size_t)*ebp - (size_t)ebp; |
124 | | } |
125 | | } |
126 | | return frame; |
127 | | } |
128 | | |
129 | | // char **taosBackTraceSymbols(int32_t *size) { |
130 | | // void *buffer[20] = {NULL}; |
131 | | // *size = taosBackTrace(buffer, 20); |
132 | | // return backtrace_symbols(buffer, *size); |
133 | | // } |
134 | | |
135 | | #ifdef USE_ADDR2LINE |
136 | | |
137 | | #include "dwarf.h" |
138 | | #include "libdwarf.h" |
139 | | #include "osThread.h" |
140 | | |
141 | | #define DW_PR_DUu "llu" |
142 | | |
143 | | typedef struct lookup_table { |
144 | | Dwarf_Line *table; |
145 | | Dwarf_Line_Context *ctxts; |
146 | | int cnt; |
147 | | Dwarf_Addr low; |
148 | | Dwarf_Addr high; |
149 | | } lookup_tableT; |
150 | | |
151 | | extern int create_lookup_table(Dwarf_Debug dbg, lookup_tableT *lookup_table); |
152 | | extern void delete_lookup_table(lookup_tableT *lookup_table); |
153 | | |
154 | | size_t addr = 0; |
155 | | lookup_tableT lookup_table; |
156 | | Dwarf_Debug tDbg; |
157 | | static TdThreadOnce traceThreadInit = PTHREAD_ONCE_INIT; |
158 | | |
159 | | void endTrace() { |
160 | | TdThreadOnce tmp = PTHREAD_ONCE_INIT; |
161 | | if (memcmp(&traceThreadInit, &tmp, sizeof(TdThreadOnce)) != 0) { |
162 | | delete_lookup_table(&lookup_table); |
163 | | dwarf_finish(tDbg); |
164 | | } |
165 | | } |
166 | | void startTrace() { |
167 | | int ret; |
168 | | Dwarf_Ptr errarg = 0; |
169 | | |
170 | | FILE *fp = fopen("/proc/self/maps", "r"); |
171 | | ret = fscanf(fp, "%lx-", &addr); |
172 | | fclose(fp); |
173 | | |
174 | | ret = dwarf_init_path("/proc/self/exe", NULL, 0, DW_GROUPNUMBER_ANY, NULL, errarg, &tDbg, NULL); |
175 | | if (ret == DW_DLV_NO_ENTRY) { |
176 | | printf("Unable to open file"); |
177 | | return; |
178 | | } |
179 | | |
180 | | ret = create_lookup_table(tDbg, &lookup_table); |
181 | | if (ret != DW_DLV_OK) { |
182 | | printf("Unable to create lookup table"); |
183 | | return; |
184 | | } |
185 | | atexit(endTrace); |
186 | | } |
187 | | static void print_line(Dwarf_Debug dbg, Dwarf_Line line, Dwarf_Addr pc) { |
188 | | char *linesrc = "??"; |
189 | | Dwarf_Unsigned lineno = 0; |
190 | | |
191 | | if (line) { |
192 | | dwarf_linesrc(line, &linesrc, NULL); |
193 | | dwarf_lineno(line, &lineno, NULL); |
194 | | } |
195 | | (void)printf("BackTrace %08" PRId64 " %s:%" DW_PR_DUu "\n", taosGetSelfPthreadId(), linesrc, lineno); |
196 | | if (line) dwarf_dealloc(dbg, linesrc, DW_DLA_STRING); |
197 | | } |
198 | | void taosPrintBackTrace() { |
199 | | int size = 20; |
200 | | void **buffer[20]; |
201 | | Dwarf_Addr pc; |
202 | | int32_t frame = 0; |
203 | | void **ebp; |
204 | | void **ret = NULL; |
205 | | size_t func_frame_distance = 0; |
206 | | |
207 | | taosThreadOnce(&traceThreadInit, startTrace); |
208 | | |
209 | | if (buffer != NULL && size > 0) { |
210 | | ebp = taosGetEbp(); |
211 | | func_frame_distance = (size_t)*ebp - (size_t)ebp; |
212 | | while (ebp && frame < size && (func_frame_distance < (1ULL << 24)) && (func_frame_distance > 0)) { |
213 | | ret = ebp + 1; |
214 | | buffer[frame++] = *ret; |
215 | | ebp = (void **)(*ebp); |
216 | | func_frame_distance = (size_t)*ebp - (size_t)ebp; |
217 | | } |
218 | | for (size_t i = 0; i < frame; i++) { |
219 | | pc = (size_t)buffer[i] - addr; |
220 | | if (pc > 0) { |
221 | | if (pc >= lookup_table.low && pc < lookup_table.high) { |
222 | | Dwarf_Line line = lookup_table.table[pc - lookup_table.low]; |
223 | | if (line) print_line(tDbg, line, pc); |
224 | | } |
225 | | } |
226 | | } |
227 | | } |
228 | | } |
229 | | #endif |
230 | | #endif |
231 | | #endif |
232 | | |
233 | | #ifndef USE_ADDR2LINE |
234 | 0 | void taosPrintBackTrace() { return; } |
235 | | #endif |
236 | | |
237 | 0 | int32_t taosMemoryDbgInit() { |
238 | 0 | #if defined(LINUX) && !defined(_ALPINE) && !defined(TD_ASTRA) |
239 | 0 | int ret = mallopt(M_MMAP_THRESHOLD, 0); |
240 | 0 | if (0 == ret) { |
241 | 0 | return TAOS_SYSTEM_ERROR(ERRNO); |
242 | 0 | } |
243 | | |
244 | 0 | return 0; |
245 | | #else |
246 | | return TSDB_CODE_FAILED; |
247 | | #endif |
248 | 0 | } |
249 | | |
250 | 0 | int32_t taosMemoryDbgInitRestore() { |
251 | 0 | #if defined(LINUX) && !defined(_ALPINE) && !defined(TD_ASTRA) |
252 | 0 | int ret = mallopt(M_MMAP_THRESHOLD, 128 * 1024); |
253 | 0 | if (0 == ret) { |
254 | 0 | return TAOS_SYSTEM_ERROR(ERRNO); |
255 | 0 | } |
256 | | |
257 | 0 | return 0; |
258 | | #else |
259 | | return TSDB_CODE_FAILED; |
260 | | #endif |
261 | 0 | } |
262 | | |
263 | 404k | void *taosMemMalloc(int64_t size) { |
264 | | #ifdef USE_TD_MEMORY |
265 | | void *tmp = malloc(size + sizeof(TdMemoryInfo)); |
266 | | if (tmp == NULL) return NULL; |
267 | | |
268 | | TdMemoryInfoPtr pTdMemoryInfo = (TdMemoryInfoPtr)tmp; |
269 | | pTdMemoryInfo->memorySize = size; |
270 | | pTdMemoryInfo->symbol = TD_MEMORY_SYMBOL; |
271 | | taosBackTrace(pTdMemoryInfo->stackTrace, TD_MEMORY_STACK_TRACE_DEPTH); |
272 | | |
273 | | return (char *)tmp + sizeof(TdMemoryInfo); |
274 | | #else |
275 | | |
276 | | #ifdef BUILD_WITH_RAND_ERR |
277 | | if (tsEnableRandErr && (tsRandErrScope & RAND_ERR_MEMORY)) { |
278 | | uint32_t r = taosRand() % tsRandErrDivisor; |
279 | | if ((r + 1) <= tsRandErrChance) { |
280 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
281 | | return NULL; |
282 | | } |
283 | | } |
284 | | #endif |
285 | | #ifdef TD_ASTRA |
286 | | if (size == 0) size = 1; |
287 | | #endif |
288 | 404k | void *p = malloc(size); |
289 | 404k | if (NULL == p) { |
290 | 0 | terrno = TSDB_CODE_OUT_OF_MEMORY; |
291 | 0 | } |
292 | 404k | return p; |
293 | 404k | #endif |
294 | 404k | } |
295 | | |
296 | 2.53M | void *taosMemCalloc(int64_t num, int64_t size) { |
297 | | #ifdef USE_TD_MEMORY |
298 | | int32_t memorySize = num * size; |
299 | | char *tmp = calloc(memorySize + sizeof(TdMemoryInfo), 1); |
300 | | if (tmp == NULL) { |
301 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
302 | | return NULL; |
303 | | } |
304 | | |
305 | | TdMemoryInfoPtr pTdMemoryInfo = (TdMemoryInfoPtr)tmp; |
306 | | pTdMemoryInfo->memorySize = memorySize; |
307 | | pTdMemoryInfo->symbol = TD_MEMORY_SYMBOL; |
308 | | taosBackTrace(pTdMemoryInfo->stackTrace, TD_MEMORY_STACK_TRACE_DEPTH); |
309 | | |
310 | | return (char *)tmp + sizeof(TdMemoryInfo); |
311 | | #else |
312 | | #ifdef BUILD_WITH_RAND_ERR |
313 | | if (tsEnableRandErr && (tsRandErrScope & RAND_ERR_MEMORY)) { |
314 | | uint32_t r = taosRand() % tsRandErrDivisor; |
315 | | if ((r + 1) <= tsRandErrChance) { |
316 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
317 | | uError("random memory error: %s, %s", tstrerror(terrno), __func__); |
318 | | return NULL; |
319 | | } |
320 | | } |
321 | | #endif |
322 | | #ifdef TD_ASTRA |
323 | | if (0 == num || 0 == size) num = size = 1; |
324 | | #endif |
325 | 2.53M | void *p = calloc(num, size); |
326 | 2.53M | if (NULL == p) { |
327 | 0 | terrno = TSDB_CODE_OUT_OF_MEMORY; |
328 | 0 | } |
329 | 2.53M | return p; |
330 | 2.53M | #endif |
331 | 2.53M | } |
332 | | |
333 | 1.99k | void *taosMemRealloc(void *ptr, int64_t size) { |
334 | | #ifdef USE_TD_MEMORY |
335 | | if (ptr == NULL) return taosMemoryMalloc(size); |
336 | | |
337 | | TdMemoryInfoPtr pTdMemoryInfo = (TdMemoryInfoPtr)((char *)ptr - sizeof(TdMemoryInfo)); |
338 | | if (tpTdMemoryInfo->symbol != TD_MEMORY_SYMBOL) { |
339 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
340 | | return NULL; |
341 | | } |
342 | | |
343 | | TdMemoryInfo tdMemoryInfo; |
344 | | memcpy(&tdMemoryInfo, pTdMemoryInfo, sizeof(TdMemoryInfo)); |
345 | | |
346 | | void *tmp = realloc(pTdMemoryInfo, size + sizeof(TdMemoryInfo)); |
347 | | if (tmp == NULL) { |
348 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
349 | | return NULL; |
350 | | } |
351 | | |
352 | | memcpy(tmp, &tdMemoryInfo, sizeof(TdMemoryInfo)); |
353 | | ((TdMemoryInfoPtr)tmp)->memorySize = size; |
354 | | |
355 | | return (char *)tmp + sizeof(TdMemoryInfo); |
356 | | #else |
357 | | #ifdef BUILD_WITH_RAND_ERR |
358 | | if (tsEnableRandErr && (tsRandErrScope & RAND_ERR_MEMORY)) { |
359 | | uint32_t r = taosRand() % tsRandErrDivisor; |
360 | | if ((r + 1) <= tsRandErrChance) { |
361 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
362 | | return NULL; |
363 | | } |
364 | | } |
365 | | #endif |
366 | | #ifdef TD_ASTRA |
367 | | if (size == 0) size = 1; |
368 | | #endif |
369 | 1.99k | void *p = realloc(ptr, size); |
370 | 1.99k | if (size > 0 && NULL == p) { |
371 | 0 | terrno = TSDB_CODE_OUT_OF_MEMORY; |
372 | 0 | } |
373 | 1.99k | return p; |
374 | 1.99k | #endif |
375 | 1.99k | } |
376 | | |
377 | 211k | char *taosStrdupi(const char *ptr) { |
378 | | #ifdef USE_TD_MEMORY |
379 | | if (ptr == NULL) return NULL; |
380 | | |
381 | | TdMemoryInfoPtr pTdMemoryInfo = (TdMemoryInfoPtr)((char *)ptr - sizeof(TdMemoryInfo)); |
382 | | if (pTdMemoryInfo->symbol != TD_MEMORY_SYMBOL) { |
383 | | return NULL; |
384 | | } |
385 | | void *tmp = tstrdup(pTdMemoryInfo); |
386 | | if (tmp == NULL) return NULL; |
387 | | |
388 | | memcpy(tmp, pTdMemoryInfo, sizeof(TdMemoryInfo)); |
389 | | taosBackTrace(((TdMemoryInfoPtr)tmp)->stackTrace, TD_MEMORY_STACK_TRACE_DEPTH); |
390 | | |
391 | | return (char *)tmp + sizeof(TdMemoryInfo); |
392 | | #else |
393 | | #ifdef BUILD_WITH_RAND_ERR |
394 | | if (tsEnableRandErr && (tsRandErrScope & RAND_ERR_MEMORY)) { |
395 | | uint32_t r = taosRand() % tsRandErrDivisor; |
396 | | if ((r + 1) <= tsRandErrChance) { |
397 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
398 | | return NULL; |
399 | | } |
400 | | } |
401 | | #endif |
402 | | |
403 | 211k | return tstrdup(ptr); |
404 | 211k | #endif |
405 | 211k | } |
406 | | |
407 | | |
408 | 3.12M | void taosMemFree(void *ptr) { |
409 | 3.12M | if (NULL == ptr) return; |
410 | | #ifdef USE_TD_MEMORY |
411 | | TdMemoryInfoPtr pTdMemoryInfo = (TdMemoryInfoPtr)((char *)ptr - sizeof(TdMemoryInfo)); |
412 | | if (pTdMemoryInfo->symbol == TD_MEMORY_SYMBOL) { |
413 | | pTdMemoryInfo->memorySize = 0; |
414 | | // memset(pTdMemoryInfo, 0, sizeof(TdMemoryInfo)); |
415 | | free(pTdMemoryInfo); |
416 | | } else { |
417 | | free(ptr); |
418 | | } |
419 | | #else |
420 | 3.12M | return free(ptr); |
421 | 3.12M | #endif |
422 | 3.12M | } |
423 | | |
424 | 0 | int64_t taosMemSize(void *ptr) { |
425 | 0 | if (ptr == NULL) return 0; |
426 | | |
427 | | #ifdef USE_TD_MEMORY |
428 | | TdMemoryInfoPtr pTdMemoryInfo = (TdMemoryInfoPtr)((char *)ptr - sizeof(TdMemoryInfo)); |
429 | | if (pTdMemoryInfo->symbol != TD_MEMORY_SYMBOL) { |
430 | | return NULL; |
431 | | } |
432 | | |
433 | | return pTdMemoryInfo->memorySize; |
434 | | #else |
435 | | #ifdef WINDOWS |
436 | | return _msize(ptr); |
437 | | #elif defined(_TD_DARWIN_64) |
438 | | return malloc_size(ptr); |
439 | | #elif defined(TD_ASTRA) |
440 | | return 0; // TD_ASTRA_TODO N/A |
441 | | #else |
442 | 0 | return malloc_usable_size(ptr); |
443 | 0 | #endif |
444 | 0 | #endif |
445 | 0 | } |
446 | | |
447 | 0 | int32_t taosMemTrim(int32_t size, bool* trimed) { |
448 | | #if defined(WINDOWS) || defined(DARWIN) || defined(_ALPINE) || defined(TD_ASTRA) |
449 | | // do nothing |
450 | | return TSDB_CODE_SUCCESS; |
451 | | #else |
452 | 0 | if (trimed) { |
453 | 0 | *trimed = malloc_trim(size); |
454 | 0 | } else { |
455 | 0 | TAOS_UNUSED(malloc_trim(size)); |
456 | 0 | } |
457 | | |
458 | 0 | return TSDB_CODE_SUCCESS; |
459 | 0 | #endif |
460 | 0 | } |
461 | | |
462 | 0 | void *taosMemMallocAlign(uint32_t alignment, int64_t size) { |
463 | | #ifdef USE_TD_MEMORY |
464 | | return NULL; |
465 | | #else |
466 | 0 | #if defined(LINUX) |
467 | | #ifdef BUILD_WITH_RAND_ERR |
468 | | if (tsEnableRandErr && (tsRandErrScope & RAND_ERR_MEMORY)) { |
469 | | uint32_t r = taosRand() % tsRandErrDivisor; |
470 | | if ((r + 1) <= tsRandErrChance) { |
471 | | terrno = TSDB_CODE_OUT_OF_MEMORY; |
472 | | return NULL; |
473 | | } |
474 | | } |
475 | | #endif |
476 | |
|
477 | 0 | void *p = memalign(alignment, size); |
478 | 0 | if (NULL == p) { |
479 | 0 | if (ENOMEM == ERRNO) { |
480 | 0 | terrno = TSDB_CODE_OUT_OF_MEMORY; |
481 | 0 | } else { |
482 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
483 | 0 | } |
484 | 0 | } |
485 | 0 | return p; |
486 | | #else |
487 | | return taosMemMalloc(size); |
488 | | #endif |
489 | 0 | #endif |
490 | 0 | } |