blob: cfad17d314ab419c747c617392ca716e22c100b1 [file] [log] [blame]
/*
* 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