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呼び出すらしいけど)。で、mallocgrep。そしたら一個だけ、コメントにヒット。そのコメントは、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のよう。じゃあ、現在のマシンの上でどの程度までエントリを大きくしても大丈夫かは逆算できるかなと。