Valgrind简介:

Valgrind是动态分析工具的框架。有很多Valgrind工具可以自动的检测许多内存管理和多进程/线程的bugs,在细节上剖析你的程序。你也可以利用Valgrind框架来实现自己的工具。

Valgrind通常包括6个工具:一个内存错误侦测工具,两个线程错误侦测工具,cache和分支预测的分析工具,堆的分析工具。

Valgrind的使用与CPU OS以及编译器和C库都有关系。目前支持下面的平台:

– x86/Linux

– AMD64/Linux

– PPC32/Linux

– PPC64/Linux

– ARM/Linux

– x86/MacOSX

– AMD64/MacOSX

 

Valgrind是GNU v2下的开源软件,你可以从http://valgrind.org下载最新的源代码。

 

Valgrind的安装:

1.从http://valgrind.org下载最新的valgrind-3.7.0.tar.bz2d,用tar -xfvalgrind-3.7.0.tar.bz2解压安装包。

2.执行./configure,检查安装要求的配置。

3.执行make。

4.执行make install,最好是用root权限。

5.试着valgrind ls -l来检测是否正常工作。

 

 

Valgrind的概述:

Valgrind时建立动态分析工具的框架。它有一系列用于调试分析的工具。Valgrind的架构是组件化的,所以可以方便的添加新的工具而不影响当前的结构。

 

下面的工具是安装时的标准配置:

Memcheck:用于检测内存错误。它帮助c和c++的程序更正确。

Cachegrind:用于分析cache和分支预测。它帮助程序执行得更快。

Callgrind:用于函数调用的分析。

Helgrind:用于分析多线程。

DRD:也用于分析多线程。与Helgrind类似,但是用不同的分析技术,所以可以检测不同的问题。

Massif:用于分析堆。它帮助程序精简内存的使用。

SGcheck:检测栈和全局数组溢出的实验性工具,它和Memcheck互补使用。

 

 

Valgrind的使用:

1.准备好程序:

编译程序时用-g,这样编译后的文件包含调试信息,那Memcheck的分析信息中就包含正确的行号。最好使用-O0的优化等级,使用-O2及以上的优化等级使用时可能会有问题。

2.在Memcheck下运行程序:

如果你的程序执行如下:

myprog arg1 arg2

那么使用如下:

valgrind –leak-check=yes myprog arg1 arg2

Memcheck是默认的工具。–leak-check打开内存泄漏检测的细节。

在上面的命令中运行程序会使得程序运行很慢,而且占用大量的内存。Memcheck会显示内存错误和检测到的内存泄漏。

3.如何查看Memcheck的输出:

这里有一个实例c代码(a.c),有一个内存错误和一个内存泄漏。

#include <stdlib.h>

void f(void)

{

int*x = (int *)malloc(10 * sizeof(int));

x[10]= 0;

//problem 1: heap block overrun

}        //problem 2: memory leak — x not freed

 

int main(void)

{

f();

return0;

}

 

运行如下:

huerjia@huerjia:~/NFS/valg/test$ valgrind–leak-check=yes ./a

==24780== Memcheck, a memory error detector

==24780== Copyright (C) 2002-2011, and GNUGPL’d, by Julian Seward et al.

==24780== Using Valgrind-3.7.0 and LibVEX;rerun with -h for copyright info

==24780== Command: ./a

==24780==

==24780== Invalid write of size 4

==24780==   at 0x80484DF: f() (a.c:5)

==24780==   by 0x80484F1: main (a.c:11)

==24780== Address 0x42d3050 is 0 bytes after a block of size 40 alloc’d

==24780==   at 0x4026444: malloc (vg_replace_malloc.c:263)

==24780==   by 0x80484D5: f() (a.c:4)

==24780==   by 0x80484F1: main (a.c:11)

==24780==

==24780==

==24780== HEAP SUMMARY:

==24780==     in use at exit: 40 bytes in 1 blocks

==24780==  total heap usage: 1 allocs, 0 frees, 40 bytes allocated

==24780==

==24780== 40 bytes in 1 blocks aredefinitely lost in loss record 1 of 1

==24780==   at 0x4026444: malloc (vg_replace_malloc.c:263)

==24780==   by 0x80484D5: f() (a.c:4)

==24780==   by 0x80484F1: main (a.c:11)

==24780==

==24780== LEAK SUMMARY:

==24780==   definitely lost: 40 bytes in 1 blocks

==24780==   indirectly lost: 0 bytes in 0 blocks

==24780==      possibly lost: 0 bytes in 0 blocks

==24780==   still reachable: 0 bytes in 0 blocks

==24780==         suppressed: 0 bytes in 0 blocks

==24780==

==24780== For counts of detected andsuppressed errors, rerun with: -v

==24780== ERROR SUMMARY: 2 errors from 2contexts (suppressed: 17 from 6)

 

如何来阅读这个输出结果:

==24780== Memcheck, a memory error detector

==24780== Copyright (C) 2002-2011, and GNUGPL’d, by Julian Seward et al.

==24780== Using Valgrind-3.7.0 and LibVEX;rerun with -h for copyright info

==24780== Command: ./a

这一部分是显示使用的工具以及版本信息。其中24780是Process ID。

 

==24780== Invalid write of size 4

==24780==   at 0x80484DF: f() (a.c:5)

==24780==   by 0x80484F1: main (a.c:11)

==24780== Address 0x42d3050 is 0 bytes after a block of size 40 alloc’d

==24780==   at 0x4026444: malloc (vg_replace_malloc.c:263)

==24780==   by 0x80484D5: f() (a.c:4)

==24780==   by 0x80484F1: main (a.c:11)

这部分指出了错误:Invalid write。后面的几行显示了函数堆栈。

 

==24780== HEAP SUMMARY:

==24780==     in use at exit: 40 bytes in 1 blocks

==24780==  total heap usage: 1 allocs, 0 frees, 40 bytes allocated

==24780==

==24780== 40 bytes in 1 blocks aredefinitely lost in loss record 1 of 1

==24780==   at 0x4026444: malloc (vg_replace_malloc.c:263)

==24780==   by 0x80484D5: f() (a.c:4)

==24780==   by 0x80484F1: main (a.c:11)

==24780==

==24780== LEAK SUMMARY:

==24780==   definitely lost: 40 bytes in 1 blocks

==24780==   indirectly lost: 0 bytes in 0 blocks

==24780==      possibly lost: 0 bytes in 0 blocks

==24780==   still reachable: 0 bytes in 0 blocks

==24780==         suppressed: 0 bytes in 0 blocks

这部分是对堆和泄漏的总结,可以看出内存泄漏的错误。

 

==24780== For counts of detected andsuppressed errors, rerun with: -v

==24780== ERROR SUMMARY: 2 errors from 2contexts (suppressed: 17 from 6)

这部分是堆所有检测到的错误的总结。代码中的两个错误都检测到了。

 

 

 

Helgrind:线程错误检测工具

若使用这个工具,在Valgrind的命令中添加–tool=helgrind。

Helgrind用于c,c++下使用POSIXpthreads的程序的线程同步错误。

Helgrind可以检测下面三类错误:

1.POSIX pthreads API的错误使用

2.由加锁和解锁顺序引起的潜在的死锁

3.数据竞态–在没有锁或者同步机制下访问内存

 

以数据竞态为例来说明Helgrind的用法:

在不使用合适的锁或者其他同步机制来保证单线程访问时,两个或者多个线程访问同一块内存就可能引发数据竞态。

一个简单的数据竞态的例子:

#include <pthread.h>

 

int var = 0;

 

void* child_fn ( void* arg ) {

var++;/* Unprotected relative to parent */ /* this is line 6 */

returnNULL;

}

 

int main ( void ) {

pthread_tchild;

pthread_create(&child,NULL, child_fn, NULL);

var++;/* Unprotected relative to child */ /* this is line 13 */

pthread_join(child,NULL);

return0;

}

 

运行如下:

huerjia@huerjia:~/NFS/valg/test$ valgrind–tool=helgrind ./b

==25449== Helgrind, a thread error detector

==25449== Copyright (C) 2007-2011, and GNUGPL’d, by OpenWorks LLP et al.

==25449== Using Valgrind-3.7.0 and LibVEX;rerun with -h for copyright info

==25449== Command: ./b

==25449==

==25449==—Thread-Announcement——————————————

==25449==

==25449== Thread #1 is the program’s rootthread

==25449==

==25449== —Thread-Announcement——————————————

==25449==

==25449== Thread #2 was created

==25449==   at 0x4123A38: clone (in /lib/tls/i686/cmov/libc-2.11.1.so)

==25449==   by 0x40430EA: pthread_create@@GLIBC_2.1 (in /lib/tls/i686/cmov/libpthread-2.11.1.so)

==25449==   by 0x402A9AD: pthread_create_WRK (hg_intercepts.c:255)

==25449==   by 0x402AA85: pthread_create@* (hg_intercepts.c:286)

==25449==   by 0x80484E1: main (b.c:11)

==25449==

==25449==—————————————————————-

==25449==

==25449== Possible data race during read ofsize 4 at 0x804A020 by thread #1

==25449== Locks held: none

==25449==   at 0x80484E2: main (b.c:12)

==25449==

==25449== This conflicts with a previouswrite of size 4 by thread #2

==25449== Locks held: none

==25449==   at 0x80484A7: child_fn (b.c:6)

==25449==   by 0x402AB04: mythread_wrapper (hg_intercepts.c:219)

==25449==   by 0x404296D: start_thread (in /lib/tls/i686/cmov/libpthread-2.11.1.so)

==25449==   by 0x4123A4D: clone (in /lib/tls/i686/cmov/libc-2.11.1.so)

==25449==

==25449==—————————————————————-

==25449==

==25449== Possible data race during writeof size 4 at 0x804A020 by thread #1

==25449== Locks held: none

==25449==   at 0x80484E2: main (b.c:12)

==25449==

==25449== This conflicts with a previouswrite of size 4 by thread #2

==25449== Locks held: none

==25449==   at 0x80484A7: child_fn (b.c:6)

==25449==   by 0x402AB04: mythread_wrapper (hg_intercepts.c:219)

==25449==   by 0x404296D: start_thread (in /lib/tls/i686/cmov/libpthread-2.11.1.so)

==25449==   by 0x4123A4D: clone (in /lib/tls/i686/cmov/libc-2.11.1.so)

==25449==

==25449==

==25449== For counts of detected andsuppressed errors, rerun with: -v

==25449== Use –history-level=approx or=none to gain increased speed, at

==25449== the cost of reduced accuracy ofconflicting-access information

==25449== ERROR SUMMARY: 2 errors from 2contexts (suppressed: 0 from 0)

 

错误信息从“Possible data race during write of size 4 at 0x804A020 by thread #1

”开始,这条信息你可以看到竞态访问的地址和大小,还有调用的堆栈信息。

第二条调用堆栈从“This conflicts with a previous write of size 4 by thread #2

”开始,这表明这里与第一个调用堆栈有竞态。

 

一旦你找到两个调用堆栈,如何找到竞态的根源:

首先通过每个调用堆栈检查代码,它们都会显示对同一个位置或者变量的访问。

现在考虑如何改正来使得多线程访问安全:

1.使用锁或者其他的同步机制,保证同一时间只有独立的访问。

2.使用条件变量等方法,确定多次访问的次序。

 

 

本文介绍了valgrind的体系结构,并重点介绍了其应用最广泛的工具:memcheck和helgrind。阐述了memcheck和helgrind的基本使用方法。在项目中尽早的发现内存问题和多进程同步问题,能够极大地提高开发效率,valgrind就是能够帮助你实现这一目标的出色工具。

 

link: http://blog.csdn.net/dndxhej/article/details/7855520

当连接数多时,经常出现大量FIN_WAIT1,可以修改 /etc/sysctl.conf
修改

net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0

然后:

/sbin/sysctl -p

使之生效
#######################################################################################
apache服务器的time_wait过多 fin_wait1过多等问题
1。time_wait状态过多。
    通常表现为apache服务器负载高,w命令显示load average可能上百,但是web服务基本没有问题。同时ssh能够登陆,但是反应非常迟钝。
原因:最可能的原因是httpd.conf里面keepalive没有开,导致每次请求都要建立新的tcp连接,请求完成以后关闭,增加了很多 time_wait的状态。另,keepalive可能会增加一部分内存的开销,但是问题不大。也有一些文章讨论到了sysctl里面一些参数的设置可以改善这个问题,但是这就舍本逐末了。
2。fin_wait1状态过多。fin_wait1状态是在server端主动要求关闭tcp连接,并且主动发送fin以后,等待client端回复ack时候的状态。fin_wait1的产生原因有很多,需要结合netstat的状态来分析。
netstat -nat|awk ‘{print awk $NF}’|sort|uniq -c|sort -n
上面的命令可以帮助分析哪种tcp状态数量异常
netstat -nat|grep “:80″|awk ‘{print $5}’ |awk -F: ‘{print $1}’ | sort| uniq -c|sort -n
则可以帮助你将请求80服务的client ip按照连接数排序。
回到fin_wait1这个话题,如果发现fin_wait1状态很多,并且client ip分布正常,那可能是有人用肉鸡进行ddos攻击、又或者最近的程序改动引起了问题。一般说来后者可能性更大,应该主动联系程序员解决。
但是如果有某个ip连接数非常多,就值得注意了,可以考虑用iptables直接封了他。

TCP是一种面向连接的协议,连接的建立和断开需要通过收发相应的分节来实现。某些时候,由于网络的故障或是一方主机的突然崩溃而另一方无法检测到,以致始终保持着不存在的连接。下面介绍一种方法来检测这种异常断开的情况

1) 在TCP协议中提供了KEEPALIVE检测。该选项使能后,在一个TCP连接上,若指定的一段时间内没有数据交换,则自动发送分节等待对方确认。

     SO_KEEPALIVE : 该选项设置是否打开探测
TCP_KEEPIDLE : 开始发送探测分节前等待的空闲时间
TCP_KEEPINTVL: 两次发送探测分节的时间间隔
TCP_KEEPCNT: 判定断开前发送探测分节的次数

2) 设定探测相关选项值

     int keepalive = 1;             // 打开探测
int keepidle = 60;        // 开始探测前的空闲等待时间
int keepintvl = 10;        // 发送探测分节的时间间隔
int keepcnt = 3;        // 发送探测分节的次数

3) 设置套接字的属性

     if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof (keepalive) < 0)
{
perror(“fail to set SO_KEEPALIVE”);
exit(-1);
}
if (setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof (keepidle) < 0)
{
perror(“fail to set SO_KEEPIDLE”);
exit(-1);
}
if (setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof (keepintvl) < 0)
{
perror(“fail to set SO_KEEPINTVL”);
exit(-1);
}
if (setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof (keepcnt) < 0)
{
perror(“fail to set SO_KEEPALIVE”);
exit(-1);
}

一旦打开KEEPALIVE探测,当TCP连接异常断开后,对sockfd进行recv操作会返回-1,并且errno的值为ETIMEDOUT。

这样一来就可以很方便的在应用程序中检测TCP连接的情况,如果检测到异常断开最简单的处理就是关闭连接。

link: http://www.embedu.org/Column/Column318.htm

因为我们的svn服务器的版本太低(1.4.2版本),而我们需要用到1.6以上版本的目录合并功能,因此,需要对我们的svn服务器进行了升级。按照以前的安装方法进行了安装,发现出了不少问题。

原因是以前安装的时候操作系统是32位的系统,此次安装的为64位的系统。

再次记录上此次成功安装的步骤:

  1. 下载、编译和安装openssl [可选]

如果想让svn服务器支持https等加密的ssl链接,需要编译此模块

[flavorzhu@self ~]$ wget http://www.openssl.org/source/openssl-1.0.0d.tar.gz

[flavorzhu@self ~]$tar -zxvf openssl-1.0.0d.tar.gz

[flavorzhu@self ~]$cd openssl-1.0.0d

[flavorzhu@self ~]$./config –prefix=/usr/local/openssl

[flavorzhu@self ~]$sudo make  && make install

  1. 下载、编译和安装apache

[flavorzhu@self ~]$wget http://mirror.bjtu.edu.cn/apache/httpd/httpd-2.2.19.tar.gz

[flavorzhu@self ~]$tar  -zxvf  httpd-2.2.19.tar.gz

[flavorzhu@self ~]$cd httpd-2.2.19

[flavorzhu@self ~]$./configure –prefix=/usr/local/apache \

–enable-lib64 \

–enable-mods-shared=all

[flavorzhu@self ~]$ sudo make && make install

  1. 下载、编译和安装BerkeleyDB

http://www.oracle.com/technetwork/database/berkeleydb/downloads/index.html 下载Berkeley db,编译并安装

比如我下载的是4.8.24版本

[flavorzhu@self ~]$tar -zxvf db-4.8.24.tar.gz

[flavorzhu@self ~]$cd db-4.8.24/build_unix/

[flavorzhu@self ~]$../dist/configure –/usr/local/BerkeleyDB.4.8

[flavorzhu@self ~]$sudo make && make install

将Berkeley DB的.so加入到ld.so.conf中

[flavorzhu@self ~]$sudo  echo /usr/local/BerkeleyDB.4.8 >> /etc/ld.so.conf

[flavorzhu@self ~]$sudo /sbin/ldconfig

svn的存储有Berkeley DB(2001年开始支持) 和 FSFS(2004年开始支持)系统两种存储,默认使用的是FSFS模式的(如果没有安装Berkeley DB)。

详情见:http://www.zhuyanbin.com/?p=74

  1. 下载、编译和安装subversion依赖包

[flavorzhu@self ~]$wget  http://subversion.tigris.org/downloads/subversion-deps-1.6.16.tar.gz

[flavorzhu@self ~]$tar zxvf subversion-deps-1.6.16.tar.gz

[flavorzhu@self ~]$cd subversion-1.6.16

4.1 安装apr

注:Apache Portable Runtime

[flavorzhu@self ~]$cd apr

[flavorzhu@self ~]$./configure –prefix=/usr/local/apache LDFLAGS=”-L/lib6″

[flavorzhu@self ~]$sudo make && make install

4.2 编译和安装apr-util

[flavorzhu@self ~]$cd apr-util

[flavorzhu@self ~]$./configure –prefix=/usr/local/apache –with-apr=/usr/local/apache LDFLAGS=”-L/lib64″ –with-berkeley-db=/usr/local/BerkeleyDB.4.8

[flavorzhu@self ~]$sudo make && make install

4.3 编译和安装neon

注:neon是使得svn客户端支持http和https协议的关键模块

[flavorzhu@self ~]$cd neon/

[flavorzhu@self ~]$./configure –prefix=/usr/local/neon –enable-shared –enable-ssl –with-ssl=openssl LDFLAGS=”-L/lib64″

[flavorzhu@self ~]$sudo make && make install

4.4 编译和安装serf

[flavorzhu@self ~]$cd serf/

[flavorzhu@self ~]$./configure –prefix=/usr/local/serf –with-apr=/usr/local/apache –with-apr-util=/usr/local/apache –with-openssl=/usr/local/openssl LDFLAGS=”-L/lib64″

[flavorzhu@self ~]$sudo make && make install

4.5 编译和安装zlib

[flavorzhu@self ~]$cd zlib/

[flavorzhu@self ~]$./configure –prefix=/usr/local/zlib

[flavorzhu@self ~]$sudo make && make install

4.6 下载、编译和安装subversion

[flavorzhu@self ~]$wget  http://subversion.tigris.org/downloads/subversion-1.6.16.tar.gz

[flavorzhu@self ~]$tar zxvf subversion-1.6.16.tar.gz

[flavorzhu@self ~]$ cd subversion-1.6.16

将subversion依赖包中的sqlite-amalgamation拷贝到subversion-1.6.16里

[flavorzhu@self ~]$./autogen.sh

[flavorzhu@self ~]$./configure  –with-ssl –with-zlib=/usr/local/zlib –with-apr=/usr/local/apache –with-apr-util=/usr/local/apache –with-serf=/usr/local/serf –with-neon=/usr/local/neon LDFLAGS=”-L/lib64″

[flavorzhu@self ~]$sudo make && make install

注:在make之前检查一下,subversion里的Makefile的BerkeleyDB的lib包使用引用正确的版本,具体搜索一下, 比如我的是“-ldb-4.8”

配置apache

修改httpd.conf,增加如下代码

LoadModule dav_module modules/mod_dav.so

LoadModule dav_svn_module     modules/mod_dav_svn.so

LoadModule authz_svn_module   modules/mod_authz_svn.so

<Location /svn>

DAV svn

SVNParentPath /absolute/path/to/repository

AuthzSVNAccessFile /my/svn/user/authz.conf

AuthType Basic

AuthName “Subversion”

AuthUserFile  /my/svn/user/passwd/file

Require valid-user

</Location>

 

  1. subversion仓库的升级

5.1 方法一 upgrade方式

svnadmin upgrade /path/to/repository

5.2 方法二 dump/load方式

svnadmin dump your-repository > repository.dump

svnadmin create your-repository

svnadmin load your-repository < repository.dump

经过测试,使用dump/load方式比较靠谱,不管你使用那种方式升级你的仓库,操作之前备份你的仓库都是不可省略的步骤。