Category Archives: Solaris学习笔记

watch in DGB

有没有这样的经历,程序崩溃或者running abnormal, 经过一番调查,发现是某个变量被意外的改动了,但是到底是什么地方意外修改了我的数据呢?一般我们可以采用2分法逐步缩小怀疑对象的范围,可问题是很多“坏人”不是那么容易发现的。比如你看: class arrowpig{private:       char* m_pText;       std::list<char*> m_dataList;public:       ~arrowpig(){            for(std::list<char*>::iterator iter=m_dataList.begin();iter!=m_dataList.end();iter++){                   delete (*iter);            }        }}; #include <ThirdPartyLib.h>  //我们的程序用了第三方的库 arrowpig inst;……ThirdPartyLib::someClass 3rdInst(para1,para2,para3); 我发现只要一执行ThirdPartyLib::someClass 的构造函数,arrowpig实例inst的内容就被修改了,于是造成inst析构时crash! 这个时候比较适合在GDB中使用watch命令: gdb> watch inst.m_pText 这样,每当inst.m_pText中的内容被改动(读或者写)的时候,程序都会停下来,然后用where命令就能比较方便的定位罪魁祸首。但是注意一点,使用watch以后,程序会运行得非常的慢,所以实际操作的时候一定先要大致确认怀疑对象,在即将执行你怀疑的代码之前设置断点,然后使用watch,目的是减少受watch影响的范围。 上面这个问题最后发现是 由于我们include的第三方库的头文件版本和实际link的库文件版本不一致。头文件告诉我们ThirdPartyLib::someClass 占用100字节,但是库的实现文件认为是120个字节,于是在构造函数中修改了后面的20个字节,破坏了同样在栈中的属于arrowpig实例的内容。

发表在 Solaris学习笔记 | 发表评论

C++运行库版本问题

问题描述: 编译时没有问题,成功编译目标文件arrowpig 程序启动时,系统报错: ld.so.1: arrowpig: fatal: relocation error: file /view/jianxu1_arrowpig/vobs/Voyager/framework/arrowpig: symbol _ZNSs4_Rep26_M_set_length_and_sharableEj: referenced symbol not found Program terminated with signal SIGKILL, Killed. 问题解决步骤: -bash-3.00$ gc++filt _ZNSs4_Rep26_M_set_length_and_sharableEjstd::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep::_M_set_length_and_sharable(unsigned int) std::string, 我们基本确定这是C++运行库内的符号,在Solaris下,C++的运行库为libstdc++.so。检查一下我的应用程序在运行时所加载的C++运行库: -bash-3.00$ ldd arrowpig | grep libstdc++        libstdc++.so.6 =>        … 繼續閱讀

发表在 Solaris学习笔记 | 发表评论

Solaris下如何检测‘资源竞争’

我们有一个application运行时的速度离我们的预期有距离。在多线程的高并发应用程序中,我们可能怀疑是由于资源竞争导致过多的“等待”。 -bash-3.00$ prstat -mp 21746 PID     USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/NLWP21746 arrowpig     0.0 0.0   0.0 0.0  0.0  76  24   0.0  142  0   151   0   httpd/17 使用-m参数要求prstat显示进程的微状态。注意到LCK=76,表示在进程的运行过程中,有76%的时间用在了等待用户级锁上(The percentage of time the process … 繼續閱讀

发表在 Solaris学习笔记 | 发表评论

汇编级Call Stack的构成

为什么要学习这么底层的东西,因为能帮助我们分析core文件,从而定位production上的问题。往往我们production或者QA的机器上没有安装GDB,也没有源代码。学会一点基本的汇编知识配合mdb就能帮上大忙。 先看一个core stack: -bash-3.00$ g++ -o Test Test.cpp-bash-3.00$ ./TestSegmentation Fault (core dumped)-bash-3.00$ mdb coreLoading modules: [ libc.so.1 ld.so.1 ]> $C080476fc _Z9FunctionCPc+0×16(80509dc)08047718 _Z9FunctionBPc+0×14(80509dc)08047734 _Z9FunctionAPc+0×14(80509dc)0804776c main+0x2b(1, 8047790, 8047798)08047784 _start+0×80(1, 8047870, 0, 8047877, 80478b9, 804795c) 初看一下,_start->main->FunctionA->FunctionB->FunctionC。程序core在FunctionC中。一点都不错,不过而实际上mdb打印出来的call stack的意义是:_start+0×80是调用main的返回地址,main+0x2b是调用FunctionC的返回地址,_Z9FunctionAPc+0×14是调用FunctionB的返回地址,_Z9FunctionBPc+0×14是调用FunctionC的返回地址,那_Z9FunctionCPc+0×16呢?哈哈,_Z9FunctionCPc+0×16是当前指令地址,在这里表示导致程序core dump的指令地址。 结合反汇编和当前各寄存器的值,可以定位程序崩溃的直接原因: > _Z9FunctionCPc+0×16::dis_Z9FunctionCPc+0×16:         movl   $0×64,(%eax)..movl … 繼續閱讀

发表在 Solaris学习笔记 | 2条评论

Solaris Slab Allocator–Magazines[4]

Dynamic Magazine Resizing 现在来讨论一下,magazine中的rounds M是如何确定的。M越大,cache命中率越高,但是内存消耗越大。solaris使用的是自适应的方法: 给M设定一个比较小的初始值,depot layer中维护一个变量contention_rate表示cache miss的程度 每次试图访问depot的时候,注意depot是所有的CPU共享的,我们先试图加一个non-blocking trylock… 如果加锁失败,说明出现竞争,contention_rate++ 如果contention_rate超过某个阈值的时候,我们就增加magazine的M magazine的M是有上限的,以免失控! umem_depot_alloc{    umem_magazine_t *mp;    if(mutex_trylock(&cp->cache_depot_lock)!=0){ //如果non-block lock失败          (void)mutex_lock(&cp->cache_depot_lock);          cp->cache_depot_contention++;     }    …    (void)mutex_lock(&cp->cache_depot_lock);} 注意,这里并没有增加magazine的代码,solaris是专门有一个update线程来定时检查并完成magazine自适应的工作的。

发表在 Solaris学习笔记 | 发表评论

Solaris Slab Allocator–Magazines[3]

Object Construction 经典的slab allocator在slab创建的时候给slab中的每个object应用constructor。这样当从slab中申请object的时候,拿到的object都是初始化好的。但是我们看这样一个极端的情况:假设每个object是8个字节大小,如果一个slab含有一个8K的页面,那一个slab可以容纳1024个object,如果该object的构造函数中为每个object申请1K 内存,那当这个 slab创建的时候一共分配了1M的内存。但是如果这个slab中只有很少的一部分object被实际使用,那很多的内存就被浪费了! 当引入magazine之后,solaris的策略是: slab中维护raw buffer,将object construction操作从slab layer上升到magazine layer中进行。 在 buffer 进入到magazine layer的时候进行construction。 当object 从magazine 还回slab layer时进行destruction,从而变成raw buffer。 不能迷信别人说的话,我们来看看实际的代码:umem_cache_alloc     //情况4:从magazine layer分配full magazine失败, 我们试图slab layer取一块raw buffer        buf = umem_slab_alloc(cp, umflag); //注意,返回的buffer是没有应用过构造函数的        …      if (cp->cache_flags & UMF_BUFTAG) … 繼續閱讀

发表在 Solaris学习笔记 | 发表评论

Solaris Slab Allocator–Magazines[2]

Thrashing Wikipedia上的解释:Thrash(computer_science) 。我在这里就暂时叫做"抖动"吧。 首先从我的理解说一下为什么在CPU Layer和Slab Layer之间要有一个Depot Layer。从slab layer拿到的是slab, slab layer和cpu layer都没有把slab切分成magazine的逻辑。还有一点就是我们知道cpu layer中每个magazine中的slots越多(M越大),cache命中率就越高,代价是内存消耗越多。如何确定M的大小,也是放在depot layer来完成的。 现在的逻辑是这样的,depot layer中维护了一个full magazine list和一个empty magazine list。为什么depot不是只维护一个full_magazine_list呢?以cache_cpu[0]为例: 如果当前的magazine是empty的,现在需要分配一个新的object,cache_cpu[0] 就从full magazine list中拿一个full magazine下来作为新的当前magazine,把该full magazine的第一个object分配出去。然后把empty的magazine还给depot layer。 如果当前的magazine是full的,现在application free了一个object, 试想如果只有full_magazine_list的话,我们把新释放的object放在哪里呢?关键在于magazine其实是个逻辑容器,我们需要一个空的逻辑容器来容纳新释放的object。 但是,我们来看这样一种情况: 上图的情况就是thrashing。如果当前magazine为empty,需要alloc,于是造成一次cache miss,从depot load一个新的full magazine并还给depot一个empty magazine,然后接着free 两次,造成第二次miss cache,从depot load一个empty magazine并且还给depot一个full magazine。4次alloc/free,2次miss, … 繼續閱讀

发表在 Solaris学习笔记 | 1条评论

Solaris Slab Allocator –Magazines[1]

Overview 经典的slab allocator算法在多CPU系统中scalability不是很好,因为它的slab list是一个全局结构,分配和释放的时候需要一个global lock来保护list数据,这样等于线性化了所有分配操作。为了弥补这个不足,Solaris提出了per-CPU cache的策略。基本思想就是为每个CPU维护一个可以容纳M个Object的Cache, 学名叫作Magazine, Magazine除了杂志的意思外,还有一个意思是‘弹药库’。资源分配时先向这个Magazine Layer申请,如果Magazine Layer空了,Magazine Layer会自动"装填弹药reload"! 下面这张图展示了整个流程: 为了改善多CPU下的性能,我们在Slab Layer之上,添加了Magazine Layer, Magazine Layer有两层,CPU Layer和Depot Layer。注意到cache_cpu是每个CPU一个的,如果当前的cache_cpu能够满足资源分配需求,就不需要involve全局锁。 以cache_cpu[0]来说,其实它维护了两个Magazine:loaded和Previous,图中的每个magazine都可容纳8个大小相同的objects。这里的实现是每个 magazine都是一个拥有M个指针的array。其行为类似于stack: 分配: obj = magazine[--rounds];释放:   magazine[rounds++] = obj; 我这里给出magazine的定义:[感谢open solaris,使得源代码公开] typedef struct umem_magazine {     void    *mag_next;     void    *mag_round[1];        /* one … 繼續閱讀

发表在 Solaris学习笔记 | 发表评论

使用libumem定位memory leak和memory corruption(4)

Memory Corruption 今天介绍memory corruption的处理,往往在程序中会发生SEGMENMT FAULT, ARRAY BOUND WRITER,DOUBLE-FREE等等,处理以来很麻烦,这里先给一个教科书级别的例子,实际中的情况要复杂很多,特别是在多线程的时候更麻烦。 int *p = (int*)malloc(16); for(int i=0; i<=4;i++)     p[i]=0; > ::umem_verifyCache Name                      Addr         Cache Integrityumem_magazine_1              8087010     cleanumem_magazine_3              8087390     clean…umem_alloc_24                   808c710     1  corrupt buffer… umem_verify会检查所有在umem管辖之下的内存的完整性(integrity),我还不知道umem_verify的算法,希望有知道的人能够帮助我理解,因为我在实际使用中有发现memory已经corrupt了,但是verify还是报clean的。上面检查出来umem_alloc_24所管辖的内存中,有一个buffer是corrupted buffer,改cache的地址是0x808c710:> 808c710::umem_verifySummary for cache ‘umem_alloc_24′  buffer 809cfd0 (allocated) has … 繼續閱讀

发表在 Solaris学习笔记 | 发表评论

使用libumem定位memory leak和memory corruption(3)-续

我明白了,我明白了为什么在(3)中发现了两处内存泄漏!先回顾一下(3)中的第二处泄漏的call stack, 注意这个stack是经过C++ 的符号mangle的,可读性很差,我首先用gc++filt处理一下: > 080a7300$<bufctl_audit ! gc++filt            ADDR          BUFADDR        TIMESTAMP           THREAD                            CACHE          LASTLOG         CONTENTS         80a7300          80a4fc0    d073d324dabb8                1                          8090390          8075064                0                 libumem.so.1`umem_cache_alloc_debug+0x16c                 libumem.so.1`umem_cache_alloc+0xe1                 libumem.so.1`umem_alloc+0x3f                 libumem.so.1`malloc+0×23                 libstdc++.so.6`operator new(unsigned int)+0×25                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&)+0×43                 libstdc++.so.6`std::basic_string<char, std::char_traits<char>, … 繼續閱讀

发表在 Solaris学习笔记 | 4条评论