/
githubmirror
/
incubator-mxnet
Обзор
Документация
Войти
/
githubmirror
/
incubator-mxnet
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/initialize.cc
406 строк
14 KB
Joe Evans
Avoid modifying loaded library map while iterating in lib_close() (#20941)
08 мар 2022, 20:46
Не верифицирован
08 мар 2022, 20:46
13b8690
Код
Авторство
О чём код?
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /*! * \file initialize.cc * \brief initialize mxnet library */ #include "initialize.h" #include <algorithm> #include <csignal> #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) #include <windows.h> /*! * \brief Retrieve the system error message for the last-error code * \param err string that gets the error message */ void win_err(char** err) { uint32_t dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<char*>(err), 0, nullptr); } #else #include <cxxabi.h> #include <dlfcn.h> #if MXNET_USE_SIGNAL_HANDLER && DMLC_LOG_STACK_TRACE #include <execinfo.h> #endif #include <cerrno> #endif #include <dmlc/logging.h> #include <mxnet/c_api.h> #include <mxnet/engine.h> #include "./engine/openmp.h" #include "./operator/custom/custom-inl.h" #if MXNET_USE_OPENCV #include <opencv2/opencv.hpp> #endif // MXNET_USE_OPENCV #include "common/utils.h" #include "engine/openmp.h" #if defined(MKL_USE_SINGLE_DYNAMIC_LIBRARY) #include <mkl.h> #endif namespace mxnet { // pthread_atfork handlers, delegated to LibraryInitializer members. void pthread_atfork_prepare() { LibraryInitializer* library_initializer = LibraryInitializer::Get(); library_initializer->atfork_prepare(); } void pthread_atfork_parent() { LibraryInitializer* library_initializer = LibraryInitializer::Get(); library_initializer->atfork_parent(); } void pthread_atfork_child() { LibraryInitializer* library_initializer = LibraryInitializer::Get(); library_initializer->atfork_child(); } // LibraryInitializer member functions LibraryInitializer::LibraryInitializer() : original_pid_(common::current_process_id()), mp_worker_nthreads_(dmlc::GetEnv("MXNET_MP_WORKER_NTHREADS", 1)), cpu_worker_nthreads_(dmlc::GetEnv("MXNET_CPU_WORKER_NTHREADS", 1)), mp_cv_num_threads_(dmlc::GetEnv("MXNET_MP_OPENCV_NUM_THREADS", 0)) { dmlc::InitLogging("mxnet"); init_mkl_dynamic_library(); engine::OpenMP::Get(); // force OpenMP initialization install_pthread_atfork_handlers(); } LibraryInitializer::~LibraryInitializer() = default; bool LibraryInitializer::lib_is_loaded(const std::string& path) const { return loaded_libs_.count(path) > 0; } /*! * \brief Loads the dynamic shared library file * \param path library file location * \return handle a pointer for the loaded library, throws dmlc::error if library can't be loaded */ void* LibraryInitializer::lib_load(const char* path) { void* handle = nullptr; // check if library was already loaded if (!lib_is_loaded(path)) { // if not, load it #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) handle = LoadLibrary(path); if (!handle) { char* err_msg = nullptr; win_err(&err_msg); LOG(FATAL) << "Error loading library: '" << path << "'\n" << err_msg; LocalFree(err_msg); return nullptr; } #else /* library loading flags: * RTLD_LAZY - Perform lazy binding. Only resolve symbols as the code that * references them is executed. * RTLD_LOCAL - Symbols defined in this library are not made available to * resolve references in subsequently loaded libraries. */ handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL); if (!handle) { LOG(FATAL) << "Error loading library: '" << path << "'\n" << dlerror(); return nullptr; } #endif // _WIN32 or _WIN64 or __WINDOWS__ // then store the pointer to the library loaded_libs_[path] = handle; } else { handle = loaded_libs_.at(path); } return handle; } /*! * \brief Closes the loaded dynamic shared library file * \param handle library file handle */ void LibraryInitializer::lib_close(void* handle, const std::string& libpath) { #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) FreeLibrary((HMODULE)handle); #else if (dlclose(handle)) { LOG(WARNING) << "LibraryInitializer::lib_close: couldn't close library at address: " << handle << " loaded from: '" << libpath << "': " << dlerror(); } #endif // _WIN32 or _WIN64 or __WINDOWS__ } /*! * \brief Obtains address of given function in the loaded library * \param handle pointer for the loaded library * \param func function pointer that gets output address * \param name function name to be fetched */ void LibraryInitializer::get_sym(void* handle, void** func, const char* name) { #if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) *func = GetProcAddress((HMODULE)handle, name); if (!(*func)) { char* err_msg = nullptr; win_err(&err_msg); LOG(FATAL) << "Error getting function '" << name << "' from library\n" << err_msg; LocalFree(err_msg); } #else *func = dlsym(handle, name); if (!(*func)) { LOG(FATAL) << "Error getting function '" << name << "' from library\n" << dlerror(); } #endif // _WIN32 or _WIN64 or __WINDOWS__ } bool LibraryInitializer::was_forked() const { return common::current_process_id() != original_pid_; } void LibraryInitializer::atfork_prepare() { using op::custom::CustomOperator; CustomOperator::Get()->Stop(); Engine::Get()->Stop(); } void LibraryInitializer::atfork_parent() { using op::custom::CustomOperator; Engine::Get()->Start(); CustomOperator::Get()->Start(); } void LibraryInitializer::atfork_child() { using op::custom::CustomOperator; // Conservative thread management for multiprocess workers this->cpu_worker_nthreads_ = this->mp_worker_nthreads_; #if MXNET_USE_OPENCV && !__APPLE__ cv::setNumThreads(mp_cv_num_threads_); #endif // MXNET_USE_OPENCV engine::OpenMP::Get()->initialize_process(); engine::OpenMP::Get()->set_thread_max(1); engine::OpenMP::Get()->set_enabled(false); Engine::Get()->Start(); CustomOperator::Get()->Start(); } void LibraryInitializer::install_pthread_atfork_handlers() { #ifndef _WIN32 engine::OpenMP::Get()->initialize_process(); // force omp to set its atfork handler first pthread_atfork(pthread_atfork_prepare, pthread_atfork_parent, pthread_atfork_child); #endif } void LibraryInitializer::init_mkl_dynamic_library() { #if !(defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)) #if MKL_USE_SINGLE_DYNAMIC_LIBRARY #if USE_INT64_TENSOR_SIZE int interface = MKL_INTERFACE_ILP64; #else int interface = MKL_INTERFACE_LP64; #endif #if defined(__INTEL_LLVM_COMPILER) || defined(__APPLE__) mkl_set_threading_layer(MKL_THREADING_INTEL); #else mkl_set_threading_layer(MKL_THREADING_GNU); interface += MKL_INTERFACE_GNU; #endif mkl_set_interface_layer(interface); #endif #endif } #if MXNET_USE_SIGNAL_HANDLER && DMLC_LOG_STACK_TRACE static inline void printStackTrace(FILE* out = stderr, const unsigned int max_frames = 63) { #if !defined(_WIN32) && !defined(_WIN64) && !defined(__WINDOWS__) // storage array for stack trace address data void* addrlist[max_frames + 1]; // retrieve current stack addresses size_t addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); if (addrlen < 5) { return; } else { addrlen = std::min(addrlen, dmlc::LogStackTraceLevel()); } fprintf(out, "Stack trace:\n"); // resolve addresses into strings containing "filename(function+address)", // Actually it will be ## program address function + offset // this array must be free()-ed char** symbollist = backtrace_symbols(addrlist, addrlen); size_t funcnamesize = 1024; char funcname[1024]; // iterate over the returned symbol lines. skip the first, it is the // address of this function. for (unsigned int i = 4; i < addrlen; i++) { char* begin_name = nullptr; char* begin_offset = nullptr; char* end_offset = nullptr; // find parentheses and +address offset surrounding the mangled name #ifdef DARWIN // OSX style stack trace for (char* p = symbollist[i]; *p; ++p) { if (*p == '_' && *(p - 1) == ' ') { begin_name = p - 1; } else if (*p == '+') { begin_offset = p - 1; } } if (begin_name && begin_offset && begin_name < begin_offset) { *begin_name++ = '\0'; *begin_offset++ = '\0'; // mangled name is now in [begin_name, begin_offset) and caller // offset in [begin_offset, end_offset). now apply // __cxa_demangle(): int status; char* ret = abi::__cxa_demangle(begin_name, &funcname[0], &funcnamesize, &status); if (status == 0) { funcname = ret; // use possibly realloc()-ed string fprintf(out, " %-30s %-40s %s\n", symbollist[i], funcname, begin_offset); } else { // demangling failed. Output function name as a C function with // no arguments. fprintf(out, " %-30s %-38s() %s\n", symbollist[i], begin_name, begin_offset); } } else { // couldn't parse the line? print the whole line. fprintf(out, " %-40s\n", symbollist[i]); } #else for (char* p = symbollist[i]; *p; ++p) { if (*p == '(') { begin_name = p; } else if (*p == '+') { begin_offset = p; } else if (*p == ')' && (begin_offset || begin_name)) { end_offset = p; } } if (begin_name && end_offset && begin_name < end_offset) { *begin_name++ = '\0'; *end_offset++ = '\0'; if (begin_offset) { *begin_offset++ = '\0'; } // mangled name is now in [begin_name, begin_offset) and caller // offset in [begin_offset, end_offset). now apply // __cxa_demangle(): int status = 0; char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status); char* fname = begin_name; if (status == 0) { fname = ret; } if (begin_offset) { fprintf( out, " %-30s ( %-40s + %-6s) %s\n", symbollist[i], fname, begin_offset, end_offset); } else { fprintf(out, " %-30s ( %-40s %-6s) %s\n", symbollist[i], fname, "", end_offset); } } else { // couldn't parse the line? print the whole line. fprintf(out, " %-40s\n", symbollist[i]); } #endif // !DARWIN - but is posix } free(symbollist); #endif } #define SIGNAL_HANDLER(SIGNAL, HANDLER_NAME, IS_FATAL) \ std::shared_ptr<void(int)> HANDLER_NAME( \ signal(SIGNAL, \ [](int signum) { \ if (IS_FATAL) { \ printf("\nFatal Error: %s\n", strsignal(SIGNAL)); \ printStackTrace(); \ signal(signum, SIG_DFL); \ raise(signum); \ } else { \ switch (signum) { \ case SIGSEGV: \ LOG(FATAL) << "InternalError: " << strsignal(SIGNAL); \ break; \ case SIGFPE: \ LOG(FATAL) << "FloatingPointError: " << strsignal(SIGNAL); \ break; \ case SIGBUS: \ LOG(FATAL) << "IOError: " << strsignal(SIGNAL); \ break; \ default: \ LOG(FATAL) << "RuntimeError: " << strsignal(SIGNAL); \ break; \ } \ } \ }), \ [](auto f) { signal(SIGNAL, f); }); SIGNAL_HANDLER(SIGSEGV, SIGSEGVHandler, true); SIGNAL_HANDLER(SIGFPE, SIGFPEHandler, false); SIGNAL_HANDLER(SIGBUS, SIGBUSHandler, false); #endif void LibraryInitializer::close_open_libs() { for (const auto& l : loaded_libs_) { lib_close(l.second, l.first); } loaded_libs_.clear(); } /** * Perform static initialization */ #ifdef __GNUC__ // In GCC we use constructor to perform initialization before any static initializer is able to run __attribute__((constructor)) static void LibraryInitializerEntry() { #pragma GCC diagnostic ignored "-Wunused-variable" volatile LibraryInitializer* library_init = LibraryInitializer::Get(); } #else static LibraryInitializer* __library_init = LibraryInitializer::Get(); #endif } // namespace mxnet