valgrind高级主题
客户端请求机制
特殊宏定义
Valgrind特殊宏,依据相关功能分别定义于valgrind.h、memcheck.h、callgrind.h、helgrind.h头文件中。
注意:
-
VALGRIND_CLO_CHANGE宏提交于3a803036f719,此前版本的Valgrind不支持
valgrind –help-dyn-options: dynamically changeable options: -v –verbose -q –quiet -d –stats –vgdb=no –vgdb=yes –vgdb=full –vgdb-poll –vgdb-error –vgdb-stop-at –error-markers –show-error-list -s –show-below-main –time-stamp –trace-children –child-silent-after-fork –scheduling-quantum –trace-sched –trace-signals –trace-symtab –trace-cfi –debug-dump=syms –debug-dump=line –debug-dump=frames –trace-redir –trace-syscalls –sym-offsets –progress-interval –merge-recursive-frames –vex-iropt-verbosity –suppressions –trace-flags –trace-notbelow –trace-notabove –profile-flags –gen-suppressions=no –gen-suppressions=yes –gen-suppressions=all –errors-for-leak-kinds –show-leak-kinds –leak-check-heuristics –show-reachable –show-possibly-lost –freelist-vol –freelist-big-blocks –leak-check=no –leak-check=summary –leak-check=yes –leak-check=full –ignore-ranges –ignore-range-below-sp –show-mismatched-frees –show-realloc-size-zero
-
Valgrind错误分类
- Illegal read / Illegal write errors
- Use of uninitialised values
- Use of uninitialised or unaddressable values in system calls
- Illegal frees
- When a heap block is freed with an inappropriate deallocation function
- Overlapping source and destination blocks
- Fishy argument values
- Realloc size zero
- Memory leak detection(a leak is only counted as a true error if –leak-check=full is specified)
valgrind.h相关宏
- RUNNING_ON_VALGRIND: 判断环境是否运行于Valgrind模拟的CPU环境,返回Valgrind嵌套层数。
- VALGRIND_DISCARD_TRANSLATIONS:
- VALGRIND_COUNT_ERRORS: 返回Valgrind发现的错误数目。
- VALGRIND_MALLOCLIKE_BLOCK: 如果程序的内存管理非标准库函数malloc、new、new[], valgrind memcheck内存追踪将失效。使用该宏追踪非标准分配器分配的内存。
- VALGRIND_FREELIKE_BLOCK:
- VALGRIND_RESIZEINPLACE_BLOCK: 通知Valgrind工具分配的块的大小已被修改,但其地址没有变化。
- VALGRIND_CREATE_MEMPOOL, VALGRIND_DESTROY_MEMPOOL, VALGRIND_MEMPOOL_ALLOC, VALGRIND_MEMPOOL_FREE, VALGRIND_MOVE_MEMPOOL, VALGRIND_MEMPOOL_CHANGE, VALGRIND_MEMPOOL_EXISTS
- VALGRIND_NON_SIMD_CALL: 命令Valgrind在CPU上执行指定函数。函数支持0-3个参数。
- VALGRIND_PRINTF: 打印printf风格的消息至Valgrind日志文件。消息使用**pid**作为消息前缀。
- VALGRIND_PRINTF_BACKTRACE: VALGRIND_PRINTF类似,但在打印消息后立即打印堆栈回溯信息。
- VALGRIND_MONITOR_COMMAND: 执行Valgrind监控指令。指令有效返回0,否则返回-1。
- VALGRIND_CLO_CHANGE: 程序中动态改变Valgrind部分参数。(新版本支持)
- VALGRIND_STACK_DEREGISTER:
- VALGRIND_STACK_CHANGE: 通知Valgrind之前注册过的堆栈发生改变。
- VALGRIND_STACK_REGISTER: 通知Valgrind参数start和end之间是唯一的栈空间,返回的标识符作为其他VALGRIND_STACK_*函数的参数使用。Valgrind使用该信息来确定堆栈指针的更改是由于函数调用还是线换,如果堆栈指针的更改发生在注册的堆栈内,则认为是函数调用。如果堆栈指针的更改发生在未注册的堆栈内,则Valgrind将认为该更改是切换到新线程。Valgrind本身是基于操作系统的内存调试工具,可能对协程的堆栈存在一些困难或不完整。这个选项可以帮助Valgrind更准确地识别堆栈信息,从而改善Valgrind对于用户级线程包情境下的堆栈跟踪和错误报告的准确性。(Unfortunately, this client request is unreliable and best avoided)
memcheck.h相关宏
- VALGRIND_MAKE_MEM_NOACCESS:
- VALGRIND_MAKE_MEM_UNDEFINED:
- VALGRIND_MAKE_MEM_DEFINED:
- VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE:
- VALGRIND_CREATE_BLOCK:
- VALGRIND_DISCARD:
- VALGRIND_CHECK_MEM_IS_ADDRESSABLE:
- VALGRIND_CHECK_MEM_IS_DEFINED:
- VALGRIND_CHECK_VALUE_IS_DEFINED:
- VALGRIND_DO_LEAK_CHECK:
- VALGRIND_DO_ADDED_LEAK_CHECK:
- VALGRIND_DO_CHANGED_LEAK_CHECK:
- VALGRIND_DO_QUICK_LEAK_CHECK:
- VALGRIND_COUNT_LEAKS:
- VALGRIND_COUNT_LEAK_BLOCKS:
- VALGRIND_GET_VBITS:
- VALGRIND_SET_VBITS:
- VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE:
- VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE:
callgrind相关宏
- CALLGRIND_DUMP_STATS
- CALLGRIND_DUMP_STATS_AT
- CALLGRIND_ZERO_STATS
- CALLGRIND_TOGGLE_COLLECT
- CALLGRIND_START_INSTRUMENTATION
- CALLGRIND_STOP_INSTRUMENTATION
helgrind.h相关宏
- VALGRIND_HG_MUTEX_INIT_POST
- VALGRIND_HG_MUTEX_LOCK_PRE
- VALGRIND_HG_MUTEX_LOCK_POST
- VALGRIND_HG_MUTEX_UNLOCK_PRE
- VALGRIND_HG_MUTEX_UNLOCK_POST
- VALGRIND_HG_MUTEX_DESTROY_PRE
- VALGRIND_HG_SEM_INIT_POST
- VALGRIND_HG_SEM_WAIT_POST
- VALGRIND_HG_SEM_POST_PRE
- VALGRIND_HG_SEM_DESTROY_PRE
- VALGRIND_HG_BARRIER_INIT_PRE
- VALGRIND_HG_BARRIER_WAIT_PRE
- VALGRIND_HG_BARRIER_RESIZE_PRE
- VALGRIND_HG_BARRIER_DESTROY_PRE
- VALGRIND_HG_CLEAN_MEMORY
- VALGRIND_HG_CLEAN_MEMORY_HEAPBLOCK
- VALGRIND_HG_DISABLE_CHECKING
- VALGRIND_HG_ENABLE_CHECKING
- VALGRIND_HG_GET_ABITS
- VALGRIND_HG_GNAT_DEPENDENT_MASTER_JOIN
- ANNOTATE_CONDVAR_LOCK_WAIT
- ANNOTATE_CONDVAR_WAIT
- ANNOTATE_CONDVAR_SIGNAL
- ANNOTATE_CONDVAR_SIGNAL_ALL
- ANNOTATE_HAPPENS_BEFORE
- ANNOTATE_HAPPENS_AFTER
- ANNOTATE_HAPPENS_BEFORE_FORGET_ALL
- ANNOTATE_PUBLISH_MEMORY_RANGE
- ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX
- ANNOTATE_NEW_MEMORY
- ANNOTATE_PCQ_CREATE
- ANNOTATE_PCQ_DESTROY
- ANNOTATE_PCQ_PUT
- ANNOTATE_PCQ_GET
- ANNOTATE_BENIGN_RACE
- ANNOTATE_BENIGN_RACE_SIZED
- ANNOTATE_IGNORE_READS_BEGIN
- ANNOTATE_IGNORE_READS_END
- ANNOTATE_IGNORE_WRITES_BEGIN
- ANNOTATE_IGNORE_WRITES_END
- ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN
- ANNOTATE_IGNORE_READS_AND_WRITES_END
- ANNOTATE_TRACE_MEMORY
- ANNOTATE_THREAD_NAME
- ANNOTATE_RWLOCK_CREATE
- ANNOTATE_RWLOCK_DESTROY
- ANNOTATE_RWLOCK_ACQUIRED
- ANNOTATE_RWLOCK_RELEASED
- ANNOTATE_BARRIER_INIT
- ANNOTATE_BARRIER_WAIT_BEFORE
- ANNOTATE_BARRIER_WAIT_AFTER
- ANNOTATE_BARRIER_DESTROY
- ANNOTATE_EXPECT_RACE
- ANNOTATE_NO_OP
- ANNOTATE_FLUSH_STATE
用法举例
以下代码片段基本涵盖上述宏如何使用
valgrind.h宏举例
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "memp.h"
void definitely_loss() { int* p = malloc(sizeof(int)); }
int main() {
if (RUNNING_ON_VALGRIND) {
VALGRIND_PRINTF("valgrind nested: %d\r\n", RUNNING_ON_VALGRIND);
VALGRIND_CLO_CHANGE("--leak-check=full");
VALGRIND_CLO_CHANGE("--show-error-list=yes");
}
memp_t* memp = pool_create(1 << 20);
char* name = (char*)pool_alloc(memp, 64);
int* age = malloc(sizeof(int));
definitely_loss();
pool_free(memp, name);
strncpy(name, "peter", 64);
pool_destroy(memp);
if (RUNNING_ON_VALGRIND) {
VALGRIND_DO_ADDED_LEAK_CHECK;
if (VALGRIND_COUNT_ERRORS) VALGRIND_PRINTF_BACKTRACE("valgrind errors: %d\r\n", VALGRIND_COUNT_ERRORS);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <ucontext.h>
#include <valgrind/valgrind.h>
#define STACK_SIZE (8*1024)
int n_ucs = 1;
int max_switchs = 10;
int n_switchs = 0;
int tid = 0;
ucontext_t *ucs;
static ucontext_t engine_uc;
void func(int arg)
{
printf("I am thread %d\n", (int) arg);
while (n_switchs < max_switchs) {
int c_tid = tid;
int n_tid = (tid + 1) % n_ucs;
n_switchs++;
tid = n_tid;
printf("%d ---> %d\n", c_tid, n_tid);
swapcontext(&ucs[c_tid], &ucs[n_tid]);
}
}
int main(int argc, char **argv)
{
int i;
n_ucs = atoi(argv[1]);
max_switchs = atoi(argv[2]);
ucs = malloc(sizeof(ucontext_t) * n_ucs);
int* valgrind_ret = malloc(n_ucs*sizeof(int));
for (i = 0; i < n_ucs; i++) {
getcontext(&ucs[i]);
void* mystack = malloc(STACK_SIZE);
valgrind_ret[i] = VALGRIND_STACK_REGISTER(mystack, mystack + STACK_SIZE);
ucs[i].uc_stack.ss_sp = mystack;
ucs[i].uc_stack.ss_size = STACK_SIZE;
ucs[i].uc_stack.ss_flags = 0;
ucs[i].uc_link = &engine_uc;
makecontext(&ucs[i], (void (*)())func, 1, i);
}
swapcontext(&engine_uc, &ucs[tid]);
for (i = 0; i < n_ucs; i++) {
VALGRIND_STACK_DEREGISTER(valgrind_ret[i]);
free(ucs[i].uc_stack.ss_sp);
}
free(ucs);
free(valgrind_ret);
return 0;
}
memcheck.h宏举例
/* 摘自 Testing constant-timeness using Valgrind: case of the NSS library */
#include <valgrind/memcheck.h>
int compute(unsigned char secret[32]) {
if (secret[0] == 0) {
return 0;
} else {
return 1;
}
}
int main(void) {
unsigned char buf[32];
for (int i = 0; i < 32; ++i)
buf[i] = 0;
VALGRIND_MAKE_MEM_UNDEFINED(buf, 32);
compute(buf);
return 0;
}
callgrind.h宏举例
helgrind.h宏举例
使用Valgrind内置gdbserver
使用Valgrind诊断或测试的程序时,程序并非运行于真实cpu环境。由此当使用Valgrind工具时,并不能直接使用GDB调试你的程序。 本节介绍如何使用Valgrind内置的GDB环境,达到在Valgrind下程序调试目的。Valgrind的内置GDB除提供正常GDB相关命令外,提供Vagrind内存诊断、缓存分析等相关功能。
一般情况下,调试程序和GDB位于相同机器。GDB使用系统调用获取被调试程序的相关信息。当被调试程序于GDB不处于一台机器时,GDB定义"GDB remote debugging", 获取被调试程序的断点,寄存器,内存相关信息。GDB-SERVER是"GDB remote debugging"协议的实现,当远程调试时远程主机必须运行GDB-SERVER。 Valgrind通过参数vgdb开启内置GDB-SERVER。GDB于GDB-SERVER通信方式不限于TCP/IP,串口,管道。 下面例子中vgdb是Valgrind提供的,建立Valgrind与GDB的管道的Helper程序。
用法举例
# a.本地调试
valgrind --vgdb=yes --vgdb-stop-at=startup /usr/bin/sleep 3600
gdb /usr/bin/sleep -ex 'target remote | vgdb'
# b.远程调试
valgrind --vgdb=yes --vgdb-stop-at=startup /usr/bin/sleep 3600
vgdb --port=1024
gdb -ex 'target remote 127.0.0.1:1024'
特殊命令
函数包裹
Valgrind允许对某些指定函数的调用进行拦截,并重定向到用户提供的另一个函数。这个“包装函数”可以执行任意操作,通常包括:
- 检查函数参数
- 调用原始函数
- 检查原始函数的返回结果
函数包装可以对任意数量的函数进行包装,在对某个API进行监控或检测时非常有用。
int add(int x, int y){
return x + y ;
}
gcc -fPIC -shared -o libadd.so add.c
#include "add.h"
#include <stdio.h>
int main(){
printf("%d\r\n", add(1,1));
return 0;
}
gcc -g -O0 main.c -L. -ladd -Wl,-rpath=/tmp/test/
#include <stdio.h>
#include "valgrind/valgrind.h"
int I_WRAP_SONAME_FNNAME_ZU(NONE,add)(int x, int y)
{
int result;
OrigFn fn;
VALGRIND_GET_ORIG_FN(fn);
printf("foo's wrapper: args %d %d\n", x, y);
CALL_FN_W_WW(result, fn, x,y);
printf("foo's wrapper: result %d\n", result);
return result;
}
gcc -shared -fPIC -o add_wrapper.so add_wrapper.c
LD_PRELOAD=./add_wrapper.so valgrind ./a.out