valgrindで、VG_N_SEGMENTが出たとき
valgrindで、VG_N_SEGMENT
発生した場合には、VG_N_SEGMENTSを大きくして、再ビルドという方針しかない模様。でも、実際あげるとなると数値を選ばないといけない、ので、valgrindのソースから意味合いをちょっと推測してみる。(このエントリに書いてある情報は、間違って解釈している可能性が考えられます。信頼しすぎないようにしてくださいね。)
まず、コミュニティには、http://old.nabble.com/VG_N_SEGMENTS-too-low,-valgrind-fails-td5092366.htmlという情報があるが、どの程度増やせばいいかについては、「動くまでVG_N_SEGMENTS」を増やしなさいというもので、うーん辛いなあ。という感じ。
cscopeで、VG_N_SEGMENTS をひっかけると以下が出てきた。
<global>[268] #define VG_N_SEGMENTS 160000000 <global>[300] static NSegment nsegments[VG_N_SEGMENTS]; split_nsegment_at[1410] if (nsegments_used >= VG_N_SEGMENTS)
意外に、nsegments_used だけと比較している。あと、NSegmentというのをつくるみたい。
split_nsegment_at()から、上の方にたどってみると。
add_segment()->split_nsegments_lo_and_hi()->split_nsegment_at()
ということで、segmentをテーブルに追加するときに呼び出されているようだ。add_segment()が呼び出されているのは、
*** aspacemgr-linux.c: read_maps_callback[1584] add_segment( &seg ); VG_[1698] add_segment(&seg); VG_[1702] add_segment(&seg); VG_[1711] add_segment(&seg); VG_[2052] add_segment( &seg ); VG_[2083] add_segment( &seg ); VG_[2183] add_segment( &seg ); VG_[2275] add_segment( &seg ); VG_[2333] add_segment( &seg ); VG_[2391] add_segment( &seg ); VG_[2489] add_segment( &seg ); VG_[2588] add_segment( &seg ); VG_[2814] add_segment( &seg ); VG_[2984] add_segment( &seg_copy ); VG_[3059] add_segment( &seg ); VG_[3074] add_segment( &seg );
と出てきて、なんだ?VG_ってと中身をちょっと見ると、
Bool VG_(am_notify_munmap)( Addr start, SizeT len )
なんて感じになっていて、コールバック関数化なんかだと思ってみる(確証はない)。そんなこんなで、とりあえず、mmap/munmapされたときに、add_segment() が呼び出されるようなので、malloc/mmapとかから帰ってきた、アドレス範囲をsegementと予想してみる。となると、mallocのコールバック関数的なものがちょっと気になる(malloc()は、条件によっては、内部でmmap呼び出すらしいけど)。で、mallocでgrep。そしたら一個だけ、コメントにヒット。そのコメントは、NSegmentの説明をするものだった。
The segment array and segment kinds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The central data structure is the segment array (segments[0 .. nsegments_used-1]). This covers the entire address space in order, giving account of every byte of it. Free spaces are represented explicitly as this makes many operations simpler. Mergeable adjacent segments are aggressively merged so as to create a "normalised" representation (preen_nsegments). There are 7 (mutually-exclusive) segment kinds, the meaning of which is important: SkFree: a free space, which may be allocated either to Valgrind (V) or the client (C). SkAnonC: an anonymous mapping belonging to C. For these, aspacem tracks a boolean indicating whether or not is is part of the client's heap area (can't remember why). SkFileC: a file mapping belonging to C. SkShmC: a shared memory segment belonging to C. SkAnonV: an anonymous mapping belonging to V. These cover all V's dynamic memory needs, including non-client malloc/free areas, shadow memory, and the translation cache. SkFileV: a file mapping belonging to V. As far as I know these are only created transiently for the purposes of reading debug info. SkResvn: a reservation segment. These are mostly straightforward. Reservation segments have some subtlety, however. A reservation segment is unmapped from the kernel's point of view, but is an area in which aspacem will not create anonymous maps (either Vs or Cs). The idea is that we will try to keep it clear when the choice to do so is ours. Reservation segments are 'invisible' from the client's point of view: it may choose to park a fixed mapping in the middle of one, and that's just tough -- we can't do anything about that. From the client's perspective reservations are semantically equivalent to (although distinguishable from, if it makes enquiries) free areas. Reservations are a primitive mechanism provided for whatever purposes the rest of the system wants. Currently they are used to reserve the expansion space into which a growdown stack is expanded, and into which the data segment is extended. Note, though, those uses are entirely external to this module, which only supplies the primitives. Reservations may be shrunk in order that an adjoining anonymous mapping may be extended. This makes dataseg/stack expansion work. A reservation may not be shrunk below one page.
ざっと読む限り、valgrind分もクライアントの文も含めて論理メモリ空間の中で割り当てが行われた場合に記憶しておくもののようだ。ちょっとわからないのは、srbkに対するフックはどうもあるみたいだけど、mallocのそれはよくわからない。まあ、いずれにせよ、基本的にはmmapやsrbk、多分mallocしたときにこのエントリは増えるだろう。大きなメモリをガッツりとれば、このエントリ自体は増えないと思うが、new/deleteを繰り返したりすると増えそう。
では、このエントリはどの程度のサイズなんだろうか?NSegment構造体は、pub_tool_aspacemgr.hにある。
typedef struct { SegKind kind; /* Extent (SkFree, SkAnon{C,V}, SkFile{C,V}, SkResvn) */ Addr start; // lowest address in range Addr end; // highest address in range /* Shrinkable? (SkResvn only) */ ShrinkMode smode; /* Associated file (SkFile{C,V} only) */ ULong dev; ULong ino; Off64T offset; UInt mode; Int fnIdx; // file name table index, if name is known /* Permissions (SkAnon{C,V}, SkFile{C,V} only) */ Bool hasR; Bool hasW; Bool hasX; Bool hasT; // True --> translations have (or MAY have) // been taken from this segment Bool isCH; // True --> is client heap (SkAnonC ONLY) /* Admin */ Bool mark; } NSegment;
これのsizeofをしてみると、typedefを適当に解決して、72byteとなったので、1エントリでとりあえず消費するのは、72byteのよう。じゃあ、現在のマシンの上でどの程度までエントリを大きくしても大丈夫かは逆算できるかなと。