log4cxx: ошибка сегментации в apr_pool_create_ex

Мне нужно использовать log4cxx для проекта C++. Однако я не понимаю основные настройки этой библиотеки. Вот моя минимальная попытка:

$ cat logger.cpp 
#include <log4cxx/logger.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/properties.h>

static log4cxx::LoggerPtr ptr;

int main()
{
  log4cxx::helpers::Properties prop;
  prop.setProperty("log4j.rootLogger","DEBUG, A1");
  prop.setProperty("log4j.appender.A1","org.apache.log4j.ConsoleAppender");
  prop.setProperty("log4j.appender.A1.layout","org.apache.log4j.PatternLayout");
  prop.setProperty("log4j.appender.A1.layout.ConversionPattern","%d{ABSOLUTE} %-5p [%c] %m%n");
  log4cxx::PropertyConfigurator::configure(prop);

  ptr = log4cxx::Logger::getLogger("API");
  LOG4CXX_INFO(ptr , "test_info");
  return 0;
}

Затем я скомпилирую его, используя:

$ g++ -g -o logger logger.cpp -llog4cxx

Это терпит неудачу с:

$ gdb ./logger
[...]
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff5f69dc9 in apr_pool_create_ex () from /lib64/libapr-1.so.0
Missing separate debuginfos, use: debuginfo-install apr-1.5.1-3.fc21.x86_64 apr-util-1.5.4-1.fc21.x86_64 cyrus-sasl-lib-2.1.26-19.fc21.x86_64 expat-2.1.0-10.fc21.x86_64 libdb-5.3.28-9.fc21.x86_64 libgcc-4.9.2-6.fc21.x86_64 libstdc++-4.9.2-6.fc21.x86_64 libuuid-2.25.2-3.fc21.x86_64 log4cxx-0.10.0-17.fc21.x86_64 nspr-4.10.8-1.fc21.x86_64 nss-3.19.1-1.0.fc21.x86_64 nss-softokn-freebl-3.19.1-1.0.fc21.x86_64 nss-util-3.19.1-1.0.fc21.x86_64 openldap-2.4.40-3.fc21.x86_64 zlib-1.2.8-7.fc21.x86_64
(gdb) bt
#0  0x00007ffff5f69dc9 in apr_pool_create_ex () from /lib64/libapr-1.so.0
#1  0x00007ffff7b26b58 in log4cxx::helpers::Pool::Pool() () from /lib64/liblog4cxx.so.10
#2  0x00007ffff7ae06ea in log4cxx::helpers::MutexException::formatMessage(int) () from /lib64/liblog4cxx.so.10
#3  0x00007ffff7ae0786 in log4cxx::helpers::MutexException::MutexException(int) () from /lib64/liblog4cxx.so.10
#4  0x00007ffff7b4a310 in log4cxx::helpers::synchronized::synchronized(log4cxx::helpers::Mutex const&) () from /lib64/liblog4cxx.so.10
#5  0x00007ffff7b5d9c8 in log4cxx::WriterAppender::close() () from /lib64/liblog4cxx.so.10
#6  0x00007ffff7ac979c in log4cxx::ConsoleAppender::~ConsoleAppender() () from /lib64/liblog4cxx.so.10
#7  0x00007ffff7ac98b9 in log4cxx::ConsoleAppender::~ConsoleAppender() () from /lib64/liblog4cxx.so.10
#8  0x00007ffff7aba247 in log4cxx::helpers::AppenderAttachableImpl::~AppenderAttachableImpl() () from /lib64/liblog4cxx.so.10
#9  0x00007ffff7b0494c in log4cxx::Logger::~Logger() () from /lib64/liblog4cxx.so.10
#10 0x00007ffff7b388b4 in log4cxx::spi::RootLogger::~RootLogger() () from /lib64/liblog4cxx.so.10
#11 0x00007ffff7b0429a in log4cxx::Logger::~Logger() () from /lib64/liblog4cxx.so.10
#12 0x00007ffff7b04429 in log4cxx::Logger::~Logger() () from /lib64/liblog4cxx.so.10
#13 0x0000000000401d6e in log4cxx::helpers::ObjectPtrT<log4cxx::Logger>::~ObjectPtrT (this=0x6031a0 <ptr>, __in_chrg=<optimized out>) at /usr/include/log4cxx/helpers/objectptr.h:100
#14 0x00007ffff6e38392 in __run_exit_handlers () from /lib64/libc.so.6
#15 0x00007ffff6e383e5 in exit () from /lib64/libc.so.6
#16 0x00007ffff6e1efe7 in __libc_start_main () from /lib64/libc.so.6
#17 0x0000000000401599 in _start ()

Что же в корне неверно в использовании глобальной переменной для логгера? Для меня имеет смысл иметь здесь шаблон синглтона.

Система: Fedora 21 с:

$ rpm -qv log4cxx
log4cxx-0.10.0-17.fc21.x86_64

а также:

$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.9.2/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.9.2 20150212 (Red Hat 4.9.2-6) (GCC) 

0 ответов

Даже более минимальная попытка приведет к сбою точно так же:

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>

static log4cxx::LoggerPtr ptr;

int main()
{
  log4cxx::BasicConfigurator::configure();

  ptr = log4cxx::Logger::getLogger("API");
  return 0;
}

Основные причины такого глупого исхода очень глубоки и связаны с фундаментальными недостатками дизайна в log4cxx (начиная с ошибочной попытки - и философии - заставить код C++ "выглядеть, работать и работать как Java").

Ближайшая причина в том, что деструктор Logger, удерживаемый глобальным LoggerPtr ptrвызывается слишком поздно. К тому времени библиотека APR (от которой зависит кодовая база log4cxx) будет закрыта, и, следовательно,synchronized::synchronizedвызов конструктора (№4 в трассировке стека) обязательно завершится неудачей, после чего неизбежен какой-то сбой. Почему код должен преодолевать такие препятствия, чтобы высвободить ресурсы - это сага сама по себе, и здесь не стоит вдаваться в подробности.

Проблема, как таковая, связана с порядком статической деинициализации: библиотека APR отклоняется "слишком рано", потому что на самом деле она инициализирована через статический синглтон слишком поздно (в примере кода, когда LoggerPtr фактически дан Регистратор для хранения).

Пример кода можно "исправить", добавив этот оператор перед выходом. main() (например, до return 0;):

ptr = 0;

Это приведет к вызову сложной последовательности высвобождения ресурсов, пока библиотека APR все еще "жива".

Более общее "решение" могло бы включать соответствующее управление временем жизни библиотеки APR. Кодовая база log4cxx имеет статические синглтоны Мейерса, разбросанные повсюду, в том числе один для переноса вызовов наapr_initialize() а также apr_terminate()для библиотеки APR, но хорошо известное " фиаско статического порядка инициализации" затрудняет упорядочение этого конкретного синглтона как "самого старого". (Его нужно будет вызывать из конструкторов каждого другого синглтона.) Практический "ответ", таким образом, состоит в том, чтобы сохранить библиотеку APR вечно, путем дополнительного вызоваapr_initialize() - говорят, в начале main()- не уравновешено с соответствиемapr_terminate(), и утечка этого конкретного ресурса при завершении программы.

Обратите внимание, что ошибка может быть вызвана другими способами. Фактически, эта ошибка стала препятствием для последующих выпусков, и, возможно, именно поэтому весь проект log4cxx практически умер десять лет назад.

Другие вопросы по тегам