Programming - yanbin's Blog
valgrind 简单使用
valgrind 是一款 memorycheck 工具,用于 c/c++ 程序的内存检查/调试。
0. 为了使用 valgrind 编译程序时要使用 gcc 的 -g 编译选项,链接libc 的 debug 版本,
自动链接,但是要求有, 如果没有 valgrind 不能正常启动内存检查.
程序用到的其他 C 库也要有 debug 版本的。
也可以使用 gcc 的 -O0 编译选项。据说使用了更好。(大写 O, 数字 0)
自动链接,但是要求有, 如果没有 valgrind 不能正常启动内存检查.
程序用到的其他 C 库也要有 debug 版本的。
也可以使用 gcc 的 -O0 编译选项。据说使用了更好。(大写 O, 数字 0)
-g 是表示编译出的二进制代码中包含调试符号
-O0 是 gcc 的默认选项。可能一些低版本的 gcc 需要加上这个选项。
1. Memorycheck 是 valgrind 的默认工具,--leak-check 选项可以打开 valgrind 详细的 memory leak 探测工具。
$ gcc -O0 -g example.c -o example
$ valgrind --leack-check=yes ./example arg1 arg2
$ gcc -O0 -g example.c -o example
$ valgrind --leack-check=yes ./example arg1 arg2
2. valgrind 的输出。
下面是一个正常分配并正常释放内存的程序用 valgrind 调试的输出:
下面是一个正常分配并正常释放内存的程序用 valgrind 调试的输出:
# 733 是 process id, 无关紧要;
==733== Memcheck, a memory error detector
==733== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==733== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright
info
# 以下是重要信息;
==733== Command: ./mkdir_t 1/2/3/4
==733==
# 这里可能会有程序的 stdout/stderr 输出;
==733==
# 内存使用状况的汇总;
==733== HEAP SUMMARY:
==733== in use at exit: 0 bytes in 0 blocks
==733== total heap usage: 3 allocs, 3 frees, 18 bytes allocated
==733==
==733== All heap blocks were freed -- no leaks are possible
==733==
# 错误信息汇总;(这里没有错误)
==733== For counts of detected and suppressed errors, rerun with: -v
==733== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
下面是一个越界使用了动态分配的内存, 没有释放动态分配内存的程序的输出;
程序名字是 example, 程序文件名后面的数字说明了 memory allocated 之类的操作的
代码在文件中的位置, 或者说哪里有 memory allocated.
==733== Memcheck, a memory error detector
==733== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==733== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright
info
# 以下是重要信息;
==733== Command: ./mkdir_t 1/2/3/4
==733==
# 这里可能会有程序的 stdout/stderr 输出;
==733==
# 内存使用状况的汇总;
==733== HEAP SUMMARY:
==733== in use at exit: 0 bytes in 0 blocks
==733== total heap usage: 3 allocs, 3 frees, 18 bytes allocated
==733==
==733== All heap blocks were freed -- no leaks are possible
==733==
# 错误信息汇总;(这里没有错误)
==733== For counts of detected and suppressed errors, rerun with: -v
==733== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
下面是一个越界使用了动态分配的内存, 没有释放动态分配内存的程序的输出;
程序名字是 example, 程序文件名后面的数字说明了 memory allocated 之类的操作的
代码在文件中的位置, 或者说哪里有 memory allocated.
# Invalid write 指示了 memory overrun;
==1058== Invalid write of size 4
# 这里是堆栈跟踪(stack tracking), stack tracking 从下到上读比较容易;
==1058== at 0x40054A: func (example.c:15)
==1058== by 0x400524: main (example.c:8)
==1058== Address 0x51bc068 is 0 bytes after a block of size 40 alloc'd
==1058== Invalid write of size 4
# 这里是堆栈跟踪(stack tracking), stack tracking 从下到上读比较容易;
==1058== at 0x40054A: func (example.c:15)
==1058== by 0x400524: main (example.c:8)
==1058== Address 0x51bc068 is 0 bytes after a block of size 40 alloc'd
# 这里也是 stack tracking 信息,比上面的更深了一层,
# 已经到了 malloc 是内存分配的情况;
==1058== at 0x4C29554: malloc (vg_replace_malloc.c:298)
==1058== by 0x40053D: func (example.c:14) # 后面的数字指示了代码所在行
==1058== by 0x400524: main (example.c:8)
==1058==
==1058==
# 内存(HEAP),使用汇总;
==1058== HEAP SUMMARY:
==1058== in use at exit: 40 bytes in 1 blocks
# N allocs, N frees 才正常, 40 bytes allocated 指示分配了多少字节内存;
==1058== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==1058==
# definitely lost 指示了内存泄漏(memory leak);
==1058== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1058== at 0x4C29554: malloc (vg_replace_malloc.c:298)
==1058== by 0x40053D: func (example.c:14)
==1058== by 0x400524: main (example.c:8)
==1058==
# memory leak 汇总;
# definitely, indirectly, possibly 是三种 memory leadk 方式.
==1058== LEAK SUMMARY:
==1058== definitely lost: 40 bytes in 1 blocks # 明确地
==1058== indirectly lost: 0 bytes in 0 blocks # 间接地
==1058== possibly lost: 0 bytes in 0 blocks # 可能性很大地
==1058== still reachable: 0 bytes in 0 blocks
==1058== suppressed: 0 bytes in 0 blocks
==1058==
# 错误信息汇总
==1058== For counts of detected and suppressed errors, rerun with: -v
==1058== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
# 已经到了 malloc 是内存分配的情况;
==1058== at 0x4C29554: malloc (vg_replace_malloc.c:298)
==1058== by 0x40053D: func (example.c:14) # 后面的数字指示了代码所在行
==1058== by 0x400524: main (example.c:8)
==1058==
==1058==
# 内存(HEAP),使用汇总;
==1058== HEAP SUMMARY:
==1058== in use at exit: 40 bytes in 1 blocks
# N allocs, N frees 才正常, 40 bytes allocated 指示分配了多少字节内存;
==1058== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==1058==
# definitely lost 指示了内存泄漏(memory leak);
==1058== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1058== at 0x4C29554: malloc (vg_replace_malloc.c:298)
==1058== by 0x40053D: func (example.c:14)
==1058== by 0x400524: main (example.c:8)
==1058==
# memory leak 汇总;
# definitely, indirectly, possibly 是三种 memory leadk 方式.
==1058== LEAK SUMMARY:
==1058== definitely lost: 40 bytes in 1 blocks # 明确地
==1058== indirectly lost: 0 bytes in 0 blocks # 间接地
==1058== possibly lost: 0 bytes in 0 blocks # 可能性很大地
==1058== still reachable: 0 bytes in 0 blocks
==1058== suppressed: 0 bytes in 0 blocks
==1058==
# 错误信息汇总
==1058== For counts of detected and suppressed errors, rerun with: -v
==1058== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
3. If the stack trace is not big enough, use the --num-callers option to
make it bigger.
make it bigger.
4. valgrind 也报告"使用了未初始化变量错误(uninitialished value)"的情况。
使用 --track-origins=yes 可以看到有关于此的更多的扩展信息,
但是这样也会让程序变得更慢.
使用 --track-origins=yes 可以看到有关于此的更多的扩展信息,
但是这样也会让程序变得更慢.
5. Memcheck cannot detect every memory error your program has.
For example, it can't detect out-of-range reads or writes to arrays
that are allocated statically or on the stack.
But it should detect many errors that could crash your program
(eg. cause a segmentation fault).
For example, it can't detect out-of-range reads or writes to arrays
that are allocated statically or on the stack.
But it should detect many errors that could crash your program
(eg. cause a segmentation fault).
6. 使用 valgrind 调试使用了 GLib 的程序需要加一些 flag.
比如让 GLib 使用 std malloc.
使用 strtok() 函數要注意,這個函數會改變第一個參數的內容
char *strtok(char *str, const char *delim);
token = strtok(str, "\t "); # 算是安裝要操作的 string; str 指向這個 string;
while (token != NULL)
token = strtok(NULL, "\t "); # 每次操作都會改變第一此安裝的 string 的內容.
0. 第一次安裝後,delim 是可以改變的。
1. 如果在調用 strtok() 後還要正常使用 string 以及她的內容,應該在調用 strtok() 前,
調用 strdup() 複製一份 string 的內容;
用複製的 string 註冊 strtok();
char *dupstr = strdup(str);
token = strtok(dupstr, "\t ");
//...
free(dupstr); // str 指向的內容還在。
伪随机数函数 rand(); srand() 的简单实现
static unsigned long next = 1; static unsigned long myrand(void) { next = next * 1103515245 + 12345; return ((unsigned)(next/65536) % 32768); } static void mysrand(unsigned long speed) { next = speed; } /* example */ int main(int argc, char **argv) { int j, r, nloops; unsigned long speed; if (argc < 2) { fprintf(stderr, "usage: %s <speed> <nloops>\n", argv[0]); exit(EXIT_FAILURE); } speed = strtol(argv[1], NULL, 10); nloops = atoi(argv[2]); mysrand(speed); for (j = 0; j < nloops; ++j) { r = myrand(); fprintf(stderr, "%lu\n", r); } exit(EXIT_SUCCESS); }
bash 中的 -n 操作符判断某个变量为空时应该加上引号("")
bash 中的 -n 操作符判断某个变量为空时应该加上引号("")
不然一个为空的变量也会返回 TRUE.
e.g.
# [ -n $a ] 总是返回 TRUE
a=""
if [ -n $a ]; then
echo "hello" # 永远会执行这里
fi
$ hello
# [ -n "$a" ] 当 a 为 empty 时返回 FALSE
a=""
if [ -n "$a" ]; then
echo "hello" # 不会执行这里
fi
比 alarm() 分辨率更高的 timer: setitimer()
getitimer, setitimer - get or set value of an interval timer
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
seitimer() 设置三個 process 的内部定时器中的某一個, getitimer() 获得这三個定时器中某一個信息。
每一个 process 都有三个不同类型的定时器,which 的值对应:
ITIMER_REAL /* 运行在 real time, 定时器到期时提交 SIGALRM 信号 和 alarm()
* 提交给进程的是同一个信号。
*/
ITIMER_VIRTUAL // 运行在 process 运行时,定时器到期时提交 SIGVTALRM 信号
ITIMER_PROF // 运行在 process 运行或在后台运行时,定时器到期时提交 SIGPROF 信号
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
0. 設置 it_value.tv_sec > 0 || it_value.tv_usec > 0 timer 開始運行,
並且在固定頻率減少 it_value.tv_sec 和/或 it_value.tv_usec 的值。
可以說 it_value 是 timer 的剩餘值。
1. 當 it_value.tv_sec == 0 && it_value.tv_usec == 0 時, timer expire。
直接設置 it_value.tv_sec == 0 && it_value.tv_usec == 0 那麼 timer expire。
NOTE: 對 timer it_value 的檢查可能會在一定的時間之後才會到來,
所以不能假設清空了 it_value 就會 timer expire.
2. 一個 timer period 到期時,用 it_interval 的值初始化 it_value 開始下一個週期,
如果 it_interval.tv_sec > 0 || it_interval.tv_usec > 0 話。
那麼用 it_interval 的值初始化 it_value 開始下一個 timer period
3. 可以同時清空 it_interval 和 it_value 可以讓 timer 停止運行。