Home > Google OSS Archive

Google OSS Archive

gflags + glogでdeadlock? (2)

どうもgflagsからglogのMutexが呼ばれるのは何かおかしいと思っていたのだが、readelfで確認したところ、シンボルが競合しているようだ。

ソースコードからは競合しても問題無いようにコーディングしたいという意図が見えるが、glogからgflagsのMutexを使用しても問題が起こらないが、gflagsからglogのMutexを使用した場合に問題が起こるようだ。

id:nobu-q先生による解決策はこうですが、adhoc臭が漂いまくり(笑)


--- glog-0.2.1-orig/src/base/mutex.h    2009-01-22 07:17:01.000000000 +0900
+++ glog-0.2.1/src/base/mutex.h 2009-05-13 21:34:36.000000000 +0900
@@ -99,6 +99,8 @@
 # error Need to implement mutex.h for your architecture, or #define NO_THREADS
 #endif

+namespace hoge {
+
 class Mutex {
  public:
   // Create a Mutex that is not held by anybody.  This constructor is
@@ -246,9 +248,13 @@
   void operator=(const WriterMutexLock&);
 };

+};
+using namespace hoge;

でも本質的に解決するためには名前空間作るしか無いと思うのですがいかがでしょう? > shinhせんせい

googleのコードは必ず名前空間を作る雰囲気がしてたんですが、Mutexだけそうなってるのは歴史的事情かなんかなのかな。usingが禁止されてて面倒くさいのとstatic linkを使用しているので、よく使うものだけは例外的にglobalに有ったりするんだろうか。

gflags + glogでdeadlock?

gflagsとglogの組み合わせだと、初期化時にデッドロックするケースが有る模様 @ RHEL5。ソースコードは以下のような感じ。


#include <glog/logging.h>
#include <google/gflags.h>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
  google::InitGoogleLogging(argv[0]);
  google::InstallFailureSignalHandler();
  google::ParseCommandLineFlags(&argc, &argv, true);
  cout << "hogehoge" << endl;
}

リンクの順番によって、デッドロックしたりしなかったり。


$ g++ test.cpp -lgflags -lglog; ./a.out
hogehoge
$ g++ test.cpp -lglog -lgflags; ./a.out
^C # deadlock occurs

gdbするとこんな感じ。


(gdb) bt
#0  0x007c8402 in __kernel_vsyscall ()
#1  0x00787ceb in pthread_rwlock_wrlock () from /lib/libpthread.so.0
#2  0x00aaed87 in Mutex::Lock () from /opt/sedue/lib/libglog.so.0
#3  0x00aaedc3 in MutexLock::MutexLock () from /opt/sedue/lib/libglog.so.0
#4  0x00bb9034 in google::(anonymous namespace)::FlagRegistry::GlobalRegistry () from /opt/sedue/lib/libgflags.so.0
#5  0x00bbff49 in google::ParseCommandLineFlagsInternal () from /opt/sedue/lib/libgflags.so.0
#6  0x00bc0196 in google::ParseCommandLineFlags () from /opt/sedue/lib/libgflags.so.0
#7  0x080488a3 in main ()

-g付き。


(gdb) bt
#0  0x003fe402 in __kernel_vsyscall ()
#1  0x002d4ceb in pthread_rwlock_wrlock () from /lib/libpthread.so.0
#2  0x00ca0d87 in Mutex::Lock (this=0x44e904) at src/base/mutex.h:171
#3  0x00ca0dc3 in MutexLock (this=0xbfa390cc, mu=0x44e904) at src/base/mutex.h:217
#4  0x00435034 in GlobalRegistry () at src/gflags.cc:675
#5  0x0043bf49 in ParseCommandLineFlagsInternal (argc=0xbfa391d4, argv=0xbfa391d0, remove_flags=true, do_report=true) at src/gflags.cc:1825
#6  0x0043c196 in google::ParseCommandLineFlags (argc=0xbfa391d4, argv=0xbfa391d0, remove_flags=true) at src/gflags.cc:1855
#7  0x0804d9b3 in main (argc=1, argv=Cannot access memory at address 0x84
) at hoge.cpp:111

初期化順序に問題が有る気がするんだけど、ちょっとソース見た感じでは分からなかったので、バグ報告的エントリ。

Googleのロギングライブラリgoogle-glogを使ってみる

“google glog”で探せないgoogle-glogを軽く使ってみました。Googleからオープンソースで公開されたC++向けのロギングライブラリです。以下のエントリが参考になります。

google-gflags 1.0と、google-glog 0.1をダウンロードしてインストールします。今回はgoogle-gflagsを使用する場合についてのみ記述します。また、glogのマニュアルに一通り目を通してから読まれると良いかと思います。

まずはとにかく動かしてみます。


#include
int
main(int argc, char **argv)
{
    google::InitGoogleLogging(argv[0]);
    google::ParseCommandLineFlags(&amp;argc, &amp;argv, true);
    LOG(INFO) << "INFO";
    LOG(WARNING) << "WARNING";
    LOG(ERROR) << "ERROR";
    LOG(FATAL) << "FATAL";
}

これをコンパイル & 実行。

pficore% g++ glog.cpp -lglog -lpthread -lgflags; ./a.out
E1015 131626 glog.cpp:9] ERROR
F1015 131626 glog.cpp:10] FATAL
@ 0x2abe222d5ca7  google::LogMessage::SendToLog()
@ 0x2abe222d2017  google::LogMessage::Flush()
@ 0x2abe222d2216  google::LogMessageFatal::~LogMessageFatal()
@       0x400bb8  main
@ 0x2abe230d68e4  (unknown)
@       0x4009c9  (unknown)

デフォルトではERROR以上のレベルが標準出力に吐かれます。また、FATALを吐くと自動的にバックトレースを出力してくれます。

次はファイルに書き出してみます。これには-log_dirオプションを指定します(オプション一覧は./a.out –helpで見ることができます)。

pficore% ./a.out -log_dir=.
E1015 131953 glog.cpp:9] ERROR
F1015 131953 glog.cpp:10] FATAL
@ 0x2b685d158ca7  google::LogMessage::SendToLog()
@ 0x2b685d155017  google::LogMessage::Flush()
@ 0x2b685d155216  google::LogMessageFatal::~LogMessageFatal()
@       0x400bb8  main
@ 0x2b685df598e4  (unknown)
@       0x4009c9  (unknown)
pficore% ls
a.out        a.out.INFO                                                   a.out.pficore.dyndns.org.kzk.log.FATAL.20081015-131953.7491    glog.cpp
a.out.ERROR  a.out.WARNING                                                a.out.pficore.dyndns.org.kzk.log.INFO.20081015-131953.7491
a.out.FATAL  a.out.pficore.dyndns.org.kzk.log.ERROR.20081015-131953.7491  a.out.pficore.dyndns.org.kzk.log.WARNING.20081015-131953.7491

ログファイル名は、”program name”.”hostname”.”user name”.log.”severity level”.”date”.”time”.”pid”になります。また、-log_dirオプションを指定しない場合は実は/tmpに吐かれています。また、”program name”.”serverity level”という名前で最新の実行結果のログへのリンクが作成されます。

デバッグ用にstderrだけにログファイルを吐きたい場合は、-logtostderr=1というフラグをつけます。標準ではERRORレベル以上のものしか出力されないので、-stderrthreshold=0とするとINFOから出力されます (ログレベルはINFO, WARNING, ERROR, FATAL の順に 0, 1, 2, 3)。

pficore% ./a.out -logtostderr=1 -stderrthreshold=0
I1015 133604 glog.cpp:7] INFO
W1015 133604 glog.cpp:8] WARNING
E1015 133604 glog.cpp:9] ERROR
F1015 133604 glog.cpp:10] FATAL
@ 0x2ba7fa406ca7  google::LogMessage::SendToLog()
@ 0x2ba7fa403017  google::LogMessage::Flush()
@ 0x2ba7fa403216  google::LogMessageFatal::~LogMessageFatal()
@       0x400bb8  main
@ 0x2ba7fb2078e4  (unknown)
@       0x4009c9  (unknown)

ファイルにもstderrにも吐かせたい場合は、-alsologtostderr=1というフラグを利用します。

デバッグ時にはVLOG関数が役立ちます。


#include
int
main(int argc, char **argv)
{
    google::InitGoogleLogging(argv[0]);
    google::ParseCommandLineFlags(&amp;amp;amp;amp;amp;amp;amp;amp;argc, &amp;amp;amp;amp;amp;amp;amp;amp;argv, true);
    VLOG(1) << "VLOG 1";
    VLOG(2) << "VLOG 2";
}

コンパイル & 実行します。

pficore% g++ glog.cpp -lglog -lpthread -lgflags;
pficore% ./a.out -v=1 -logtostderr=1 -stderrthreshold=0
I1015 134214 glog.cpp:7] VLOG 1
pficore% ./a.out -v=2 -logtostderr=1 -stderrthreshold=0
I1015 134218 glog.cpp:7] VLOG 1
I1015 134218 glog.cpp:8] VLOG 2

VLOGのレベルはINFOと同じなので、-stderrthreshold=0を指定しています。また、-v=Nと指定すると、VLOG(X) (ただし X <= N) のメッセージが表示されます。

後、まだ動かし方が分かってないのが、moduleです。ファイル名ベースでモジュールが作成されるようなのですが、以下のようにしても何も出ないので、もう少し調べる必要が有りそうです (suffixは.ccに変更)。凄く初歩的なミスをしている気がしてならない。


#include
int
main(int argc, char **argv)
{
    google::InitGoogleLogging(argv[0]);
    google::ParseCommandLineFlags(&amp;amp;amp;amp;amp;amp;amp;amp;argc, &amp;amp;amp;amp;amp;amp;amp;amp;argv, true);
    VLOG(1) << "VLOG 1";
    VLOG(2) << "VLOG 2";
}
pficore% g++ glog.cc -lglog -lpthread -lgflags
pficore% ./a.out --vmodule=glog=2 -logtostderr=1 -stderrthreshold=0

一定レベル以上のメッセージをメールで送信する機能もあるので、これも試してみたいと思っています。ログローテート周りも調べないとな。ざっとそんな感じです。

ちょうどid:tkngさんと、ロギングライブラリ欲しいよねーと話していたところにこれがリリースされたので、正直凄くありがたいです。欲しい機能は大体あるし。

google++ shinh++ !!!

P.S

このコメントへーって思った。

// fwrite() doesn't return an error when the disk is full, for
// messages that are less than 4096 bytes. When the disk is full,
// it returns the message length for messages that are less than
// 4096 bytes. fwrite() returns 4096 for message lengths that are
// greater than 4096, thereby indicating an error.

まあfwrite()の返り値は write(2)で泣きを見た ISer なら誰でもチェックすると思われるが (including me)、disk full時の細かい挙動までは知らなかった。

「プロトコルバッファー」がオープンソース化

Googleで使用されているRPC/シリアライズフレームワーク「ProtocolBuffer」がオープンソース化されたらしい via @ohkuraさん

おーおーお、GoogleTestといい何かオープンソース化ラッシュですね。FacebookのThriftと比較してC++, Java, Pythonしかバインディングが無いので、PHPとかPerlとか使ってる場合はまだ移行できなさそう。

この2つは週末に時間とってじっくり調べてみようー。

追記:
RPCの部分は無くて、シリアライズのところしかなかった・・・。

追記:
RPC有るっぽ

今更ながらyupo先生もgoogle perf toolsについて書かれているのを発見。はてブ万歳。LDR見落としたっぽいな・・・。

Home > Google OSS Archive

お薦め本
広告
Archives
Categories

Return to page top