MemTracer¶
MemTracer 是一个动态链接库,被用于监控 MiniOB 内存使用;用于在内存受限的条件下,运行和调试 MiniOB。MemTracer 通过 hook 内存分配释放函数,记录 MiniOB 进程中的内存分配情况。
原理介绍¶
MemTracer 对内存分配释放函数进行了覆盖(override),以达到对内存动态分配释放(如malloc/free
, new/delete
)的监控。除此之外,MemTracer 还会将 MiniOB 进程中代码段等内存占用统计在内。
通过在 LD_PRELOAD
环境变量中指定 MemTracer 动态库来覆盖 glibc 中的符号,可以实现可插拔方式监控 MiniOB 进程的内存占用。
MemTracer 支持设置最大内存限额,当 MiniOB 进程申请超过内存限额的内存时,MemTracer 会调用 exit(-1)
使 MiniOB 进程退出。
使用介绍¶
编译¶
可通过指定 WITH_MEMTRACER
控制 MemTracer 的编译(默认编译),MemTracer 动态库默认输出在${CMAKE_BINARY_DIR}/lib
目录下。
下述示例将关闭MemTracer 的编译。
sudo bash build.sh init
bash build.sh release -DWITH_MEMTRACER=OFF
运行¶
通过指定 LD_PRELOAD
环境变量, 将 MemTracer 动态库加载到 MiniOB 进程中。如:
LD_PRELOAD=./lib/libmemtracer.so ./bin/observer
通过指定MT_PRINT_INTERVAL_MS
环境变量,设置内存使用的打印间隔时间,单位为毫秒(ms),默认为 5000 ms(5s)。通过指定MT_MEMORY_LIMIT
环境变量,设置内存使用的上限,单位为字节,当超过该值,MiniOB 进程会立即退出。下述示例表明设置内存使用情况的打印间隔为 1000 ms(1s),内存使用上限为1000 字节。
MT_PRINT_INTERVAL_MS=1000 MT_MEMORY_LIMIT=1000 LD_PRELOAD=./lib/libmemtracer.so ./bin/observer
使用场景示例¶
- 通过指定 MiniOB 进程的最大内存限额,可以模拟在内存受限的情况下运行、调试 MiniOB。当超出最大内存限额后,MiniOB 进程会自动退出。
指定最大内存限额:
MT_MEMORY_LIMIT=100000000 LD_PRELOAD=./lib/libmemtracer.so ./bin/observer
当由于申请内存超过限额退出,则在退出时会打印相关日志:
[MEMTRACER] alloc memory:24, allocated_memory: 31653580, memory_limit: 31653600, Memory limit exceeded!
- 通过链接 MemTracer,可以在 MiniOB 进程的任意位置获取当前内存使用情况。
步骤1: 链接 libmemtracer.so 到 MiniOB 进程中。
步骤2: 在需要获取内存使用情况的位置,调用 memtracer/mt_info.h
头文件中的 memtracer::allocated_memory()
函数,获取当前内存使用情况。
示例代码如下:
diff --git a/src/observer/CMakeLists.txt b/src/observer/CMakeLists.txt
index c62ac3c..d105fbf 100644
--- a/src/observer/CMakeLists.txt
+++ b/src/observer/CMakeLists.txt
@@ -20,7 +20,7 @@ FIND_PACKAGE(Libevent CONFIG REQUIRED)
# JsonCpp cannot work correctly with FIND_PACKAGE
-SET(LIBRARIES common pthread dl libevent::core libevent::pthreads libjsoncpp.a)
+SET(LIBRARIES common pthread dl libevent::core libevent::pthreads libjsoncpp.a memtracer)
# 指定目标文件位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
diff --git a/src/observer/sql/parser/parse.cpp b/src/observer/sql/parser/parse.cpp
index def0ed1..4f22799 100644
--- a/src/observer/sql/parser/parse.cpp
+++ b/src/observer/sql/parser/parse.cpp
@@ -15,6 +15,7 @@ See the Mulan PSL v2 for more details. */
#include "sql/parser/parse.h"
#include "common/log/log.h"
#include "sql/expr/expression.h"
+#include "memtracer/mt_info.h"
RC parse(char *st, ParsedSqlNode *sqln);
@@ -41,6 +42,7 @@ int sql_parse(const char *st, ParsedSqlResult *sql_result);
RC parse(const char *st, ParsedSqlResult *sql_result)
{
+ LOG_ERROR("parse sql `%s`, allocated: %lu\n", st, memtracer::allocated_memory());
sql_parse(st, sql_result);
return RC::SUCCESS;
}
注意¶
- MemTracer 会记录
mmap
映射的整个虚拟内存占用, 因此不建议使用mmap
管理内存。 - 不允许使用绕过常规内存分配(
malloc
/free
,new
/delete
)的方式申请并使用内存。如使用brk/sbrk/syscall
等。 - MemTracer 不支持与 sanitizers (ASAN等)一起使用。
- MemTracer 除统计动态内存的申请释放,也会将进程的代码段等内存占用统计在内。
- 引入 MemTracer 后,会对内存申请释放函数的性能产生一定影响,在 Github Action 中测试结果如下:
// MemTracer 开启
-----------------------------------------------------------------------------------
Benchmark Time CPU Iterations UserCounters...
-----------------------------------------------------------------------------------
BM_MallocFree/8 42.1 ns 42.1 ns 17090212 bytes_per_second=181.357M/s
BM_MallocFree/64 39.9 ns 39.9 ns 16763826 bytes_per_second=1.49326G/s
BM_MallocFree/512 40.1 ns 40.1 ns 17553391 bytes_per_second=11.8925G/s
BM_MallocFree/1024 39.9 ns 39.9 ns 17548820 bytes_per_second=23.9052G/s
BM_MallocFree/1048576 53.4 ns 53.4 ns 13150036 bytes_per_second=17.8628T/s
BM_MallocFree/8388608 53.3 ns 53.3 ns 13155704 bytes_per_second=143.106T/s
BM_MallocFree/1073741824 24877 ns 24750 ns 28814 bytes_per_second=39.4565T/s
BM_NewDelete/8 40.6 ns 40.6 ns 17270523 bytes_per_second=187.866M/s
BM_NewDelete/64 40.5 ns 40.5 ns 17279131 bytes_per_second=1.47047G/s
BM_NewDelete/512 40.5 ns 40.5 ns 17276789 bytes_per_second=11.7686G/s
BM_NewDelete/1024 40.5 ns 40.5 ns 17279484 bytes_per_second=23.5455G/s
BM_NewDelete/1048576 53.8 ns 53.8 ns 13003320 bytes_per_second=17.7138T/s
BM_NewDelete/8388608 54.0 ns 54.0 ns 13011425 bytes_per_second=141.183T/s
BM_NewDelete/1073741824 24844 ns 24611 ns 29271 bytes_per_second=39.6796T/s
// MemTracer 关闭
-----------------------------------------------------------------------------------
Benchmark Time CPU Iterations UserCounters...
-----------------------------------------------------------------------------------
BM_MallocFree/8 10.1 ns 10.1 ns 70194674 bytes_per_second=753.492M/s
BM_MallocFree/64 10.8 ns 10.8 ns 64456192 bytes_per_second=5.50213G/s
BM_MallocFree/512 10.9 ns 10.9 ns 64672143 bytes_per_second=43.8294G/s
BM_MallocFree/1024 10.8 ns 10.8 ns 64616114 bytes_per_second=88.0599G/s
BM_MallocFree/1048576 23.5 ns 23.5 ns 29758318 bytes_per_second=40.5388T/s
BM_MallocFree/8388608 23.5 ns 23.5 ns 29743010 bytes_per_second=324.33T/s
BM_MallocFree/1073741824 20574 ns 20436 ns 34738 bytes_per_second=47.7856T/s
BM_NewDelete/8 13.4 ns 13.4 ns 50775388 bytes_per_second=570.14M/s
BM_NewDelete/64 14.2 ns 14.2 ns 49231328 bytes_per_second=4.18804G/s
BM_NewDelete/512 14.3 ns 14.3 ns 49198936 bytes_per_second=33.4308G/s
BM_NewDelete/1024 14.3 ns 14.3 ns 49215085 bytes_per_second=66.7825G/s
BM_NewDelete/1048576 26.9 ns 26.9 ns 25987611 bytes_per_second=35.4036T/s
BM_NewDelete/8388608 26.9 ns 26.9 ns 25997689 bytes_per_second=283.11T/s
BM_NewDelete/1073741824 20694 ns 20648 ns 34975 bytes_per_second=47.2954T/s