LoginSignup
2
2

More than 5 years have passed since last update.

geoip-c(0.9.1)のGeoIP::Organization#look_upにはメモリリークするバグがある

Last updated at Posted at 2014-11-01

現象

ISP別のアクセス数を調べるために、td-agentにGeoIP Organizationを組み込んで当該IPアドレスのISPをログに落とすようにしたところ、td-agentプロセスがメモリリークする現象に遭遇した。
GeoIP Cityのみを使用していたときは問題なかった。

メモリリークを確認するために、ローカル環境でGeoIP::Organization#look_upを100万回ループしたときのメモリ使用量を10万回毎に見てみた。

check_memoryleak.rb
require 'geoip'

db = GeoIP::Organization.new('GeoIPOrg.dat')
for i in 1..1000000 do
  db.look_up('8.8.8.8')
  puts `ps -o rss= -p #{Process.pid}`.to_i if i % 100000 == 0
end

結果

% ruby check_memoryleak.rb
44868
48228
49576
51300
53196
56084
57004
58308
59872
61080

回数が増えるにつれてメモリ使用量が増え続けている、つまりメモリリークしていることが分かる。

原因

ソースを読んでみると、geoip-cからGeoIP_name_by_addr()というGeoIPライブラリの関数を呼び出す箇所がある。
この関数はIPアドレスからISPもしくは組織名の文字列を返すもので、この文字列のメモリ領域はライブラリ内部でmallocして確保している。
このため、コール元(geoip-c側)でこの領域を解放する必要があるが、それが漏れているためメモリリークしていた。

対処法

Pull Requestを送って修正してもらったので、そのうちgeoip-c 0.9.2が出ると思うが、手っ取り早く対処するには以下のように修正すればよい。

1) geoip-cのソースがあるディレクトリに移動する

% cd `find ~/.rbenv -name 'geoip-c-0.9.1' -type d`
% cd ext/geoip

2) free()を追加

@@ -126,6 +126,7 @@
   VALUE result = rb_hash_new();
   if(value) {
     rb_hash_sset(result, key, encode_to_utf8_and_return_rb_str(value));
+    free(value);
     return result;
   } else {
     return Qnil;

3) リコンパイル

% make clean
% make install

ここで、再度GeoIP::Organization#look_upを100万回ループしたときのメモリ使用量経過を見てみる。

% ruby check_memoryleak.rb
42140
43076
43076
44836
45708
45708
45720
45868
45868
45868

45868バイト以降は増えていないのでメモリリークが解消されたことが確認できた。

以上

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2