From 37d81eae16a9f2fca4ed9e157c4e6ff28a4fdc9d Mon Sep 17 00:00:00 2001 From: liuyuqiao Date: Fri, 9 Sep 2022 20:43:18 +0800 Subject: [PATCH] feat(lite): support base lite ipc fork debug when config it: `all` lite c api will run at fork child server process use to keep off user process env, as c++ memory issue, we can ASAN to detect issue, but at user deploy env, there are so many factors may lead to not easy to rebuild with ASAN, so we support LITE c debug api to distinguish memory issue from LITE runtime or user caller process env. more info: use LITE_enable_lite_ipc_debug api to enable fork debug mode this stage support `base` LITE c api, please refs to: lite/example/c_example/main.c will support all LITE c api later. --- lite/example/c_example/main.c | 29 +- lite/lite-c/include/lite-c/global_c.h | 9 + lite/lite-c/include/lite-c/tensor_c.h | 14 + lite/lite-c/src/global.cpp | 20 +- lite/lite-c/src/ipc_helper.cpp | 260 ++++++++++++++++++ lite/lite-c/src/ipc_helper.h | 101 +++++++ lite/lite-c/src/ipc_imp.cpp | 370 ++++++++++++++++++++++++++ lite/lite-c/src/ipc_imp.h | 63 +++++ lite/lite-c/src/network.cpp | 171 ++++++++++-- lite/lite-c/src/tensor.cpp | 87 +++++- lite/src/misc.cpp | 22 +- 11 files changed, 1120 insertions(+), 26 deletions(-) create mode 100644 lite/lite-c/src/ipc_helper.cpp create mode 100644 lite/lite-c/src/ipc_helper.h create mode 100644 lite/lite-c/src/ipc_imp.cpp create mode 100644 lite/lite-c/src/ipc_imp.h diff --git a/lite/example/c_example/main.c b/lite/example/c_example/main.c index 4b62c4f58..facd4cabe 100644 --- a/lite/example/c_example/main.c +++ b/lite/example/c_example/main.c @@ -14,6 +14,7 @@ #include "lite-c/tensor_c.h" #include +#include #include #define LITE_CAPI_CHECK(_expr) \ @@ -42,7 +43,7 @@ int basic_c_interface(const char* mode_path) { LITE_get_tensor_total_size_in_byte(c_input_tensor, &length_in_byte)); LITE_CAPI_CHECK(LITE_get_tensor_memory(c_input_tensor, &dst_ptr)); //! copy or forward data to network - memset(dst_ptr, 5, length_in_byte); + LITE_memset(dst_ptr, 5, length_in_byte); //! forward LITE_CAPI_CHECK(LITE_forward(c_network)); @@ -66,23 +67,41 @@ int basic_c_interface(const char* mode_path) { float max = -1.0f; float sum = 0.0f; + int is_enable_ipc_debug = LITE_is_enable_ipc_debug_mode(); + float* copy_ptr = NULL; + float* final_dst_ptr = (float*)output_ptr; + if (is_enable_ipc_debug) { + copy_ptr = (float*)(malloc(length_output_in_byte)); + LITE_CAPI_CHECK(LITE_copy_server_tensor_memory( + output_ptr, copy_ptr, length_output_in_byte)); + final_dst_ptr = (float*)copy_ptr; + } + for (size_t i = 0; i < out_length; i++) { - float data = ((float*)(output_ptr))[i]; + float data = final_dst_ptr[i]; sum += data; if (max < data) max = data; } printf("max=%e, sum=%e\n", max, sum); + LITE_destroy_network(c_network); + if (is_enable_ipc_debug) { + free(copy_ptr); + } return 0; } int main(int argc, char** argv) { - if (argc < 2) { - printf("usage: lite_c_examples , just test C interface " + if (argc < 3) { + printf("usage: lite_c_examples is_enable_fork_debug_model , just " + "test C interface " "build.\n"); return -1; } - return basic_c_interface(argv[1]); + if (atoi(argv[1])) { + LITE_enable_lite_ipc_debug(); + } + return basic_c_interface(argv[2]); } // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} diff --git a/lite/lite-c/include/lite-c/global_c.h b/lite/lite-c/include/lite-c/global_c.h index 2944d545e..a3362c50d 100644 --- a/lite/lite-c/include/lite-c/global_c.h +++ b/lite/lite-c/include/lite-c/global_c.h @@ -26,6 +26,15 @@ LITE_API void LITE_clear_last_error(); */ LITE_API const char* LITE_get_last_error(); +/*! \brief LITE is enable ipc debug mode or not. + * \return if enable ipc debug mode, will return 1, if not will return 0 + */ +LITE_API int LITE_is_enable_ipc_debug_mode(); + +/*! \brief LITE enable ipc debug mode. + */ +LITE_API void LITE_enable_lite_ipc_debug(); + /*! \brief Get device count * \param[in] device_type device type * \return the device count diff --git a/lite/lite-c/include/lite-c/tensor_c.h b/lite/lite-c/include/lite-c/tensor_c.h index 3693a89af..27942965e 100644 --- a/lite/lite-c/include/lite-c/tensor_c.h +++ b/lite/lite-c/include/lite-c/tensor_c.h @@ -158,6 +158,15 @@ LITE_API int LITE_tensor_share_memory_with( */ LITE_API int LITE_get_tensor_memory(const LiteTensor tensor, void** data); +/** + * \brief copy tensor memory if fork debug mode. + * \param[server_ptr] the ptr valid in lite server + * \param[client_ptr] the ptr only valid in lite client + * \param[size_in_byte] copy size + */ +LITE_API int LITE_copy_server_tensor_memory( + void* server_ptr, void* client_ptr, size_t size_in_byte); + /** * \brief get the memory pointer of a Tensor object. * \param[in] tensor The input Tensor @@ -230,6 +239,11 @@ LITE_API int LITE_tensor_concat( LiteTensor* tensors, int nr_tensor, int dim, LiteDeviceType dst_device, int device_id, LiteTensor* result_tensor); +/** + * \brief fuction like memset + */ +LITE_API void* LITE_memset(void* s, int c, size_t n); + #ifdef __cplusplus } #endif diff --git a/lite/lite-c/src/global.cpp b/lite/lite-c/src/global.cpp index 54d709bf1..fefe0a01c 100644 --- a/lite/lite-c/src/global.cpp +++ b/lite/lite-c/src/global.cpp @@ -1,5 +1,6 @@ #include "lite/global.h" #include "common.h" +#include "ipc_helper.h" #include "lite-c/global_c.h" namespace { @@ -47,7 +48,24 @@ void LITE_clear_last_error() { const char* LITE_get_last_error() { LITE_LOCK_GUARD(mtx_error); - return get_global_error().get_error_msg().c_str(); + if (ipc_imp::is_server()) { + return get_global_error().get_error_msg().c_str(); + } else { + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_GET_LAST_ERROR); + + char* ret_ptr = static_cast(raw_shm_ptr); + return ret_ptr; + } +} + +int LITE_is_enable_ipc_debug_mode() { + return ipc::IpcHelper::is_enable_fork_debug_mode(); +} + +void LITE_enable_lite_ipc_debug() { + ipc_imp::enable_lite_ipc_debug(); } int LITE_get_version(int* major, int* minor, int* patch) { diff --git a/lite/lite-c/src/ipc_helper.cpp b/lite/lite-c/src/ipc_helper.cpp new file mode 100644 index 000000000..eea1a2dc8 --- /dev/null +++ b/lite/lite-c/src/ipc_helper.cpp @@ -0,0 +1,260 @@ +#include "ipc_helper.h" + +#include "lite-c/global_c.h" +#include "lite-c/network_c.h" + +#include "misc.h" + +using namespace ipc_imp; +namespace ipc { + +static void api_remote_call_cb(struct MsgBody* msg) { + LITE_DEBUG( + "into %s: %d remote_func_id: %zu", __func__, __LINE__, msg->remote_func_id); + switch (static_cast(msg->remote_func_id)) { + case RemoteFuncId::LITE_MAKE_NETWORK: { + LiteNetwork network; + //! second args is const LiteConfig config + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteConfig config; + memcpy(&config, shm_ptr_c, sizeof(LiteConfig)); + //! third args is network_io + LiteNetworkIO network_io; + memcpy(&network_io, shm_ptr_c + sizeof(LiteConfig), sizeof(LiteNetworkIO)); + + int ret = LITE_make_network(&network, config, network_io); + + //! API is block, put ret to shm_ptr + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + ret_ptr++; + void* network_p = static_cast(ret_ptr); + memcpy(network_p, &network, sizeof(LiteNetwork)); + }; break; + case RemoteFuncId::LITE_LOAD_MODEL_FROM_PATH: { + LiteNetwork network; + char* shm_ptr_c = static_cast(msg->shm_ptr); + memcpy(&network, shm_ptr_c, sizeof(LiteNetwork)); + + int ret = + LITE_load_model_from_path(network, shm_ptr_c + sizeof(LiteNetwork)); + + //! API is block, put ret to shm_ptr + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + }; break; + case RemoteFuncId::LITE_GET_LAST_ERROR: { + auto shm_size = base_get_shm_size(); + + const char* ret = LITE_get_last_error(); + char* ret_ptr = static_cast(msg->shm_ptr); + + auto last_error_str_len = strlen(ret) + 1; + ASSERT_SHM_SIZE(shm_size, last_error_str_len); + strcpy(ret_ptr, ret); + }; break; + case RemoteFuncId::LITE_GET_IO_TENSOR: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteNetwork network; + LiteTensorPhase phase; + LiteTensor tensor; + memcpy(&network, shm_ptr_c, sizeof(LiteNetwork)); + memcpy(&phase, shm_ptr_c + sizeof(LiteNetwork), sizeof(LiteTensorPhase)); + + int ret = LITE_get_io_tensor( + network, shm_ptr_c + sizeof(LiteNetwork) + sizeof(LiteTensorPhase), + phase, &tensor); + + //! API is block, put ret to shm_ptr + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + ret_ptr++; + void* lite_tensor_p = static_cast(ret_ptr); + memcpy(lite_tensor_p, &tensor, sizeof(LiteTensor)); + }; break; + case RemoteFuncId::LITE_GET_TENSOR_TOTAL_SIZE_IN_BYTE: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteTensor tensor; + size_t size; + memcpy(&tensor, shm_ptr_c, sizeof(LiteTensor)); + + int ret = LITE_get_tensor_total_size_in_byte(tensor, &size); + + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + ret_ptr++; + memcpy(ret_ptr, &size, sizeof(size_t)); + }; break; + case RemoteFuncId::LITE_GET_TENSOR_MEMORY: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteTensor tensor; + void* data; + memcpy(&tensor, shm_ptr_c, sizeof(LiteTensor)); + + int ret = LITE_get_tensor_memory(tensor, &data); + + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + ret_ptr++; + memcpy(ret_ptr, &data, sizeof(void*)); + }; break; + case RemoteFuncId::LITE_MEMSET: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + void* s; + int c; + size_t n; + memcpy(&s, shm_ptr_c, sizeof(void*)); + memcpy(&c, shm_ptr_c + sizeof(void*), sizeof(int)); + memcpy(&n, shm_ptr_c + sizeof(void*) + sizeof(int), sizeof(size_t)); + LITE_memset(s, c, n); + }; break; + case RemoteFuncId::LITE_FORWARD: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteNetwork network; + memcpy(&network, shm_ptr_c, sizeof(LiteNetwork)); + + int ret = LITE_forward(network); + + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + }; break; + case RemoteFuncId::LITE_WAIT: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteNetwork network; + memcpy(&network, shm_ptr_c, sizeof(LiteNetwork)); + + int ret = LITE_wait(network); + + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + }; break; + case RemoteFuncId::LITE_GET_OUTPUT_NAME: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteNetwork network; + size_t index; + const char* name; + memcpy(&network, shm_ptr_c, sizeof(LiteNetwork)); + memcpy(&index, shm_ptr_c + sizeof(LiteNetwork), sizeof(size_t)); + + int ret = LITE_get_output_name(network, index, &name); + auto output_name_len = strlen(name) + 1; + auto shm_size = base_get_shm_size(); + ASSERT_SHM_SIZE(shm_size, output_name_len); + + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + ret_ptr++; + void* p = static_cast(ret_ptr); + char* p_c = static_cast(p); + strcpy(p_c, name); + }; break; + case RemoteFuncId::LITE_COPY_SERVER_TENSOR_MEMORY: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + void* server_ptr; + size_t size_in_byte; + memcpy(&server_ptr, shm_ptr_c, sizeof(void*)); + memcpy(&size_in_byte, shm_ptr_c + sizeof(void*), sizeof(size_t)); + memcpy(shm_ptr_c, server_ptr, size_in_byte); + }; break; + case RemoteFuncId::LITE_DESTROY_NETWORK: { + char* shm_ptr_c = static_cast(msg->shm_ptr); + LiteNetwork network; + memcpy(&network, shm_ptr_c, sizeof(LiteNetwork)); + + int ret = LITE_destroy_network(network); + + int* ret_ptr = static_cast(msg->shm_ptr); + *ret_ptr = ret; + }; break; + default: + LITE_THROW("code issue happened: do not handle RemoteFuncId"); + } +}; +bool IpcHelper::sm_is_enable_fork_ipc = false; +IpcHelper::IpcHelper() { + if (!is_server()) { + sm_is_enable_fork_ipc = is_enable_ipc(); + auto shm_mem_conut = base_get_shm_count(); + shm_mem_for_null_consumer_ptr = base_get_shm_ptr(0); + LITE_ASSERT( + shm_mem_for_null_consumer_ptr, "invalid shm_ptr: %p", + shm_mem_for_null_consumer_ptr); + + for (size_t i = 1; i < shm_mem_conut; i++) { + void* shm_ptr = base_get_shm_ptr(i); + LITE_ASSERT(shm_ptr, "invalid shm_ptr: %p", shm_ptr); + //! init consumer ptr as nullptr + m_shm_ptr2consumer_ptr[shm_ptr] = nullptr; + } + + shm_size = base_get_shm_size(); + register_remote_call_cb(&api_remote_call_cb); + } else { + LITE_THROW("code issue happened: can not use for client"); + } +}; + +IpcHelper::~IpcHelper() { + struct MsgBody msg; + msg.type = IPC_SERVER_EXIT; + send_ipc_msg(&msg); + join_server(); +}; + +void* IpcHelper::get_shm_ptr(void* consumer_ptr) { + LITE_LOCK_GUARD(m_mtx); + + if (!consumer_ptr) { + return shm_mem_for_null_consumer_ptr; + } + + void* ret = nullptr; + //! try find old one + for (auto&& i : m_shm_ptr2consumer_ptr) { + if (consumer_ptr == i.second) { + ret = i.first; + break; + } + } + + //! if not find, try alloc a new one + if (!ret) { + for (auto&& i : m_shm_ptr2consumer_ptr) { + if (nullptr == i.second) { + i.second = consumer_ptr; + ret = i.first; + break; + } + } + } + + if (!ret) { + LITE_ERROR( + "can not find any usable shm_mem, may config LITE_DEBUG_IPC_SHM_COUNT " + "big " + "than :%zu", + m_shm_ptr2consumer_ptr.size() + 1); + LITE_ASSERT(ret); + } + + return ret; +}; + +void IpcHelper::release_shm_ptr(void* consumer_ptr) { + LITE_LOCK_GUARD(m_mtx); + + LITE_ASSERT(consumer_ptr, "invalid consumer_ptr ptr"); + for (auto&& i : m_shm_ptr2consumer_ptr) { + if (consumer_ptr == i.second) { + //! release use of shm_mem, then other consumer can use it + i.second = nullptr; + return; + } + } + + LITE_THROW( + "error happened!!, can not find any consumer_ptr in " + "m_shm_ptr2consumer_ptr"); +}; + +} // namespace ipc diff --git a/lite/lite-c/src/ipc_helper.h b/lite/lite-c/src/ipc_helper.h new file mode 100644 index 000000000..93bcdc1b0 --- /dev/null +++ b/lite/lite-c/src/ipc_helper.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "ipc_imp.h" + +#define IPC_INSTACE() ipc::IpcHelper::Instance() + +#define IPC_HELP_REMOTE_CALL(SHM_PTR, REMOTEFUNCID) \ + struct ipc_imp::MsgBody msg; \ + msg.type = ipc_imp::IPC_CALL_REMOTE_API; \ + msg.shm_ptr = static_cast(SHM_PTR); \ + msg.remote_func_id = static_cast(REMOTEFUNCID); \ + IPC_INSTACE().send_ipc_msg(&msg); + +#define ASSERT_SHM_SIZE(SHM_SIZE, NEED_SIZE) \ + do { \ + if (SHM_SIZE < NEED_SIZE) { \ + LITE_ERROR( \ + "shm_size not enough to run this api need vs config: (%fMB " \ + "%fMB), please config it by env: LITE_DEBUG_IPC_SHM_SIZE, for " \ + "example config to 20MB by: export LITE_DEBUG_IPC_SHM_SIZE=20", \ + NEED_SIZE / 1024.0 / 1024.0, SHM_SIZE / 1024.0 / 1024.0); \ + __builtin_trap(); \ + } \ + } while (0) + +namespace ipc { + +template +class Singleton { +public: + Singleton() {} + static T& Instance() { + static T _; + return _; + } +}; + +class IpcHelper : public Singleton { +public: + IpcHelper(const IpcHelper&) = delete; + IpcHelper& operator=(const IpcHelper&) = delete; + IpcHelper(); + + ~IpcHelper(); + + //! send msg with default timeout + struct ipc_imp::MsgBody send_ipc_msg(struct ipc_imp::MsgBody* msg) { + return send_msg(msg, &tv); + } + + //! get shm ptr + void* get_shm_ptr(void* consumer_ptr); + + //! release shm_ptr, NOT free shm_ptr + void release_shm_ptr(void* consumer_ptr); + + //! check shm size + void check_shm_size(size_t need_size) { ASSERT_SHM_SIZE(shm_size, need_size); } + + //! is enable ipc fork debug mode + static bool is_enable_fork_debug_mode() { return sm_is_enable_fork_ipc; } + + static bool sm_is_enable_fork_ipc; + +private: + //! 5 minutes + struct timeval tv = {300, 0}; + + //! map of , + std::map m_shm_ptr2consumer_ptr; + + size_t shm_size = 0; + + //! shm_mem for consumer_ptr == nullptr + void* shm_mem_for_null_consumer_ptr; + + LITE_MUTEX m_mtx; +}; + +enum class RemoteFuncId : size_t { + LITE_MAKE_NETWORK = 1, + LITE_LOAD_MODEL_FROM_PATH = 2, + LITE_GET_LAST_ERROR = 3, + LITE_GET_IO_TENSOR = 4, + LITE_GET_TENSOR_TOTAL_SIZE_IN_BYTE = 5, + LITE_GET_TENSOR_MEMORY = 6, + LITE_MEMSET = 7, + LITE_FORWARD = 8, + LITE_WAIT = 9, + LITE_GET_OUTPUT_NAME = 10, + LITE_COPY_SERVER_TENSOR_MEMORY = 11, + LITE_DESTROY_NETWORK = 12, +}; + +} // namespace ipc diff --git a/lite/lite-c/src/ipc_imp.cpp b/lite/lite-c/src/ipc_imp.cpp new file mode 100644 index 000000000..fbf4c3560 --- /dev/null +++ b/lite/lite-c/src/ipc_imp.cpp @@ -0,0 +1,370 @@ +#include "ipc_imp.h" + +#if __linux__ +#include +#include +#endif + +#ifdef __ANDROID__ +#include +#include +#endif + +namespace ipc_imp { + +namespace { + +//! max count if shm +#define MAX_SHM_COUNT 15 +struct ServerConfig { +#ifdef _LITE_SUPPORT_IPC + pid_t server_id; +#else + size_t server_id; +#endif + void* cb; + int fd_s[2]; + int fd_c[2]; + fd_set select_s; + fd_set select_c; + void* shm_ptr[MAX_SHM_COUNT]; + size_t shm_mem_conut; + //! all shm use the same shm_size + size_t shm_size; +}; + +static LITE_MUTEX m_mtx; + +static ServerConfig server_config; + +#ifdef _LITE_SUPPORT_IPC +static size_t config_shm_memory() { + //! default config to 10MB + size_t shm_size = 10 * 1024 * 1024; + //! env to config LITE_DEBUG_IPC_SHM_SIZE + //! for example , export LITE_DEBUG_IPC_SHM_SIZE=20, means config SHM size 20MB + if (auto env = ::std::getenv("LITE_DEBUG_IPC_SHM_SIZE")) + shm_size = std::stoi(env) * 1024 * 1024; + +#ifdef __ANDROID__ + //! special for Android prop, attention: getprop may need permission + char buf[PROP_VALUE_MAX]; + if (__system_property_get("LITE_DEBUG_IPC_SHM_SIZE", buf) > 0) { + shm_size = std::stoi(buf) * 1024 * 1024; + } +#endif + + return shm_size; +} + +//! FIXME: detail at create_server(), at this stage, we only support enable lite fork +//! debug by: LITE_enable_lite_ipc_debug, after issue fix, may support env: +//! LITE_ENABLE_C_API_WITH_FORK_MODE +// bool config_enable_debug_fork() { +// //! debug off, we only support enable fork debug mode with env +// //! LITE_ENABLE_C_API_WITH_FORK_MODE, not support api to config +// //! as we will fork as soon as possible by __attribute__((constructor)), +// //! user may not have to chance to call any normal api before it +// bool ret = false; +// //! env to config LITE_ENABLE_C_API_WITH_FORK_MODE +// //! for example , export LITE_ENABLE_C_API_WITH_FORK_MODE=1, means enable LITE c +// api +// //! with fork mode +// if (auto env = ::std::getenv("LITE_ENABLE_C_API_WITH_FORK_MODE")) { +// if (std::stoi(env) > 0) { +// ret = true; +// } +// } +// +//#ifdef __ANDROID__ +// //! special for Android prop, attention: getprop may need permission +// char buf[PROP_VALUE_MAX]; +// if (__system_property_get("LITE_ENABLE_C_API_WITH_FORK_MODE", buf) > 0) { +// ret = std::stoi(buf); +// if (std::stoi(buf) > 0) { +// ret = true; +// } +// } +//#endif +// +// return ret; +//} +#endif + +static bool is_enable_debug_fork = false; + +//! internal recycle server when IPC_ASSERT happen +static void recycle_server() { + static struct timeval tv = {1, 0}; + struct MsgBody msg; + msg.type = IPC_SERVER_EXIT; + if (server_config.server_id > 0) { + send_msg(&msg, &tv); + } +} + +#define ipc_unlikely(v) __builtin_expect((v), 0) +#define IPC_ASSERT(expr, msg) \ + do { \ + if (ipc_unlikely(!(expr))) { \ + LITE_ERROR("ipc fatal error: assert failed: %s with msg: %s", #expr, msg); \ + recycle_server(); \ + __builtin_trap(); \ + } \ + } while (0) + +#ifdef _LITE_SUPPORT_IPC +static size_t config_shm_memory_count() { + //! default config to 2 + size_t shm_cnt = 2; + //! env to config LITE_DEBUG_IPC_SHM_COUNT + //! for example , export LITE_DEBUG_IPC_SHM_COUNT=8, means config SHM count 8 + if (auto env = ::std::getenv("LITE_DEBUG_IPC_SHM_COUNT")) + shm_cnt = std::stoi(env); + +#ifdef __ANDROID__ + //! special for Android prop, attention: getprop may need permission + char buf[PROP_VALUE_MAX]; + if (__system_property_get("LITE_DEBUG_IPC_SHM_COUNT", buf) > 0) { + shm_cnt = std::stoi(buf); + } +#endif + IPC_ASSERT( + shm_cnt >= 2 && shm_cnt <= MAX_SHM_COUNT, + "error config LITE_DEBUG_IPC_SHM_COUNT, should be range of [2, " + "MAX_SHM_COUNT]"); + + return shm_cnt; +} +#endif + +#ifdef _LITE_SUPPORT_IPC +static void handle_remote_call(struct MsgBody* msg) { + LITE_DEBUG("into %s: %d", __func__, __LINE__); + IPC_ASSERT( + server_config.cb, + "handle_remote_call failed: can not find valid " + "remote_call_cb, please call " + "register_remote_call_cb firstly!!"); + remote_call_cb cb = (remote_call_cb)server_config.cb; + cb(msg); +} + +static void* ipc_mmap( + void* addr, size_t length, int prot, int flags, int fd, off_t offset) { + void* ret = mmap(addr, length, prot, flags, fd, offset); + IPC_ASSERT(ret != MAP_FAILED, "call mmap failed"); + return ret; +} + +static int ipc_munmap(void* addr, size_t length) { + int ret = munmap(addr, length); + IPC_ASSERT(0 == ret, "call munmap failed"); + return ret; +} +#endif + +//! start server as soon as possible +//! FIXME: when use __attribute__((constructor)) on clang, will init before all +//! static class object, which will lead to flatbuffer runtime issue, env config +//! with init_priority, do not take effect on diff cpp src file on clang +// static __attribute__((constructor)) void create_server() { +void create_server() { +#ifdef _LITE_SUPPORT_IPC + LITE_LOCK_GUARD(m_mtx); + LITE_LOG("try to config lite fork debug model"); + if (is_enable_debug_fork) + return; + + is_enable_debug_fork = true; + //! is_enable_debug_fork = config_enable_debug_fork(); + //! init default server_config + server_config.server_id = 0; + + if (!is_enable_debug_fork) + return; + + server_config.cb = nullptr; + server_config.shm_size = config_shm_memory(); + server_config.shm_mem_conut = config_shm_memory_count(); + + for (size_t i = 0; i < server_config.shm_mem_conut; i++) { + server_config.shm_ptr[i] = ipc_mmap( + NULL, server_config.shm_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANON, -1, 0); + } + + IPC_ASSERT(-1 != pipe(server_config.fd_s), "create server pipe failed"); + IPC_ASSERT(-1 != pipe(server_config.fd_c), "create client pipe failed"); + + FD_ZERO(&server_config.select_s); + FD_ZERO(&server_config.select_c); + //! config server and client + FD_SET(server_config.fd_s[0], &server_config.select_s); + FD_SET(server_config.fd_c[0], &server_config.select_c); + + server_config.server_id = fork(); + + IPC_ASSERT(server_config.server_id >= 0, "call fork failed"); + + if (server_config.server_id > 0) { + LITE_LOG("create lite_ipc_server success pid is: %d", server_config.server_id); + } else { + std::string server_name = "lite_ipc_server"; + // TODO: __APPLE__ do not support PR_SET_NAME and PR_SET_PDEATHSIG + // if caller crash, no have chance to send IPC_SERVER_EXIT +#if __linux__ + LITE_LOG("start server with name: %s....", server_name.c_str()); + prctl(PR_SET_NAME, (unsigned long)server_name.c_str(), 0, 0, 0); + //! auto die if father crash + prctl(PR_SET_PDEATHSIG, SIGKILL); +#else + LITE_LOG("start server...."); +#endif + + while (1) { + LITE_DEBUG("lite_ipc_server wait msg now....."); + int res = + select(server_config.fd_s[0] + 1, &server_config.select_s, NULL, + NULL, NULL); + + IPC_ASSERT( + res > 0, + "select issue happened or timeout(but we do not support timeout)"); + + struct MsgBody msg; + size_t r_size = read(server_config.fd_s[0], &msg, sizeof(msg)); + IPC_ASSERT(r_size == sizeof(msg), "broken pipe msg"); + + struct MsgBody response; + response.type = IPC_SERVER_RESPONSE; + switch (msg.type) { + case IPC_CALL_REMOTE_API: + LITE_DEBUG("handle remote call"); + handle_remote_call(&msg); + break; + case IPC_CONFIG_REMOTE_HANDLE_API: + LITE_DEBUG("handle register remote cb"); + server_config.cb = msg.cb; + break; + default: + IPC_ASSERT(IPC_SERVER_EXIT == msg.type, "code issue happened!!"); + } + + size_t w_size = write(server_config.fd_c[1], &response, sizeof(response)); + IPC_ASSERT(w_size == sizeof(response), "write pip failed"); + + if (IPC_SERVER_EXIT == msg.type) { + LITE_DEBUG("handle exit now"); + for (size_t i = 0; i < server_config.shm_mem_conut; i++) { + ipc_munmap(server_config.shm_ptr[i], server_config.shm_size); + } + exit(0); + } + } + } +#else + //! TODO: imp for Windows with CreateProcess + server_config.server_id = 0; + LITE_ERROR("lite do not support fork debug ipc on this PLATFORM"); + __builtin_trap(); + + return; +#endif +} + +} // namespace +//////////////////////////////////////////////// api imp ///////////////////////// +void register_remote_call_cb(remote_call_cb cb) { + IPC_ASSERT(!server_config.cb, "already register remote_call_cb"); + IPC_ASSERT(cb, "invalid remote_call_cb"); + IPC_ASSERT(server_config.server_id, "register cb need server already up"); + + server_config.cb = (void*)cb; + static struct timeval tv = {5, 0}; + struct MsgBody msg; + msg.type = IPC_CONFIG_REMOTE_HANDLE_API; + msg.cb = (void*)cb; + send_msg(&msg, &tv); +} + +struct MsgBody send_msg(struct MsgBody* msg, struct timeval* timeout) { +#ifdef _LITE_SUPPORT_IPC + IPC_ASSERT(server_config.server_id > 0, "server not ready"); + if (IPC_CALL_REMOTE_API == msg->type) { + IPC_ASSERT( + server_config.cb, + "can not find valid remote_call_cb, please " + "call register_remote_call_cb firstly!!"); + } + + //! send msg to server + size_t w_size = write(server_config.fd_s[1], msg, sizeof(struct MsgBody)); + IPC_ASSERT(w_size == sizeof(struct MsgBody), "write pipe failed"); + + //! now wait server response + struct MsgBody response; + LITE_DEBUG("wait server response"); + + int res = select( + server_config.fd_c[0] + 1, &server_config.select_c, NULL, NULL, timeout); + if (0 == res) { + LITE_ERROR("wait server timeout"); + } + IPC_ASSERT(res > 0, "select issue happened or timeout"); + + size_t r_size = read(server_config.fd_c[0], &response, sizeof(response)); + IPC_ASSERT(sizeof(response) == r_size, "broken pipe msg"); + IPC_ASSERT(IPC_SERVER_RESPONSE == response.type, "error server response type"); + + return response; +#else + struct MsgBody response; + LITE_ERROR("This code should not be called"); + __builtin_trap(); + + return response; +#endif +} + +bool is_server() { + return !server_config.server_id; +} + +bool is_enable_ipc() { + return is_enable_debug_fork; +} + +void join_server() { +#ifdef _LITE_SUPPORT_IPC + if (!is_enable_debug_fork) + return; + + int ret; + waitpid(server_config.server_id, &ret, 0); + if (ret) { + //! when server crash, we mark server_config.server_id to 0 + //! to prevent handle more msg, for example recycle_server + server_config.server_id = 0; + } + IPC_ASSERT( + ret == 0, "child process exit !zero, please check server log for detail"); +#endif +} + +void* base_get_shm_ptr(size_t index) { + return server_config.shm_ptr[index]; +} + +size_t base_get_shm_count() { + return server_config.shm_mem_conut; +} + +size_t base_get_shm_size() { + return server_config.shm_size; +} + +void enable_lite_ipc_debug() { + create_server(); +} +} // namespace ipc_imp diff --git a/lite/lite-c/src/ipc_imp.h b/lite/lite-c/src/ipc_imp.h new file mode 100644 index 000000000..124c8dffd --- /dev/null +++ b/lite/lite-c/src/ipc_imp.h @@ -0,0 +1,63 @@ +#pragma once + +#undef _LITE_SUPPORT_IPC +#if __linux__ || __unix__ || __APPLE__ +#define _LITE_SUPPORT_IPC +#endif + +#ifdef _LITE_SUPPORT_IPC +#include +#include +#include +#endif + +#include "misc.h" + +namespace ipc_imp { +//! server call api ret +enum MsgType { + IPC_SERVER_RESPONSE = 1, + IPC_CALL_REMOTE_API = 2, + IPC_SERVER_EXIT = 3, + IPC_CONFIG_REMOTE_HANDLE_API = 4, +}; + +struct MsgBody { + enum MsgType type; + //! remote call handle callback + void* cb; + //! remote call function emum, define by user + size_t remote_func_id; + //! mmap region ptr + void* shm_ptr; +}; + +//! block wait server return response msg +struct MsgBody send_msg(struct MsgBody* msg, struct timeval* timeout); + +//! wait server exit +void join_server(); + +typedef void (*remote_call_cb)(struct MsgBody* msg); + +//! register remote call +void register_remote_call_cb(remote_call_cb cb); + +//! is server or not, server or do not enable ipc mode will return true +bool is_server(); + +//! is enable ipc or not +bool is_enable_ipc(); + +//! get shm ptr +void* base_get_shm_ptr(size_t index); + +//! get shm count +size_t base_get_shm_count(); + +// get shm size +size_t base_get_shm_size(); + +// enable fork ipc debug +void enable_lite_ipc_debug(); +} // namespace ipc_imp diff --git a/lite/lite-c/src/network.cpp b/lite/lite-c/src/network.cpp index 8325c73c3..149714eb3 100644 --- a/lite/lite-c/src/network.cpp +++ b/lite/lite-c/src/network.cpp @@ -1,5 +1,6 @@ #include "lite/network.h" #include "common.h" +#include "ipc_helper.h" #include "lite-c/network_c.h" #include "../../src/network_impl_base.h" @@ -204,11 +205,32 @@ int LITE_make_network( LiteNetwork* network, const LiteConfig config, const LiteNetworkIO network_io) { LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); - auto lite_network = std::make_shared( - convert_to_lite_config(config), convert_to_lite_io(network_io)); - LITE_LOCK_GUARD(mtx_network); - get_gloabl_network_holder()[lite_network.get()] = lite_network; - *network = lite_network.get(); + if (ipc_imp::is_server()) { + auto lite_network = std::make_shared( + convert_to_lite_config(config), convert_to_lite_io(network_io)); + LITE_LOCK_GUARD(mtx_network); + get_gloabl_network_holder()[lite_network.get()] = lite_network; + *network = lite_network.get(); + } else { + size_t need_size = sizeof(LiteConfig) + sizeof(LiteNetworkIO); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + //! second args is const LiteConfig config + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &config, sizeof(LiteConfig)); + //! third args is network_io + memcpy(shm_ptr_c + sizeof(LiteNetworkIO), &network_io, sizeof(LiteNetworkIO)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_MAKE_NETWORK); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + memcpy(network, ret_ptr, sizeof(LiteNetwork)); + return ret; + } LITE_CAPI_END(); } @@ -234,17 +256,53 @@ int LITE_load_model_from_path(LiteNetwork network, const char* model_path) { LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); LITE_ASSERT(model_path, "The model path pass to LITE api is null"); - static_cast(network)->load_model(model_path); + if (ipc_imp::is_server()) { + static_cast(network)->load_model(model_path); + } else { + size_t need_size = sizeof(LiteNetwork) + strlen(model_path) + 1; + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(network); + + //! first args is LiteNetwork network + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &network, sizeof(LiteNetwork)); + //! second args is const char* model_path + strcpy(shm_ptr_c + sizeof(LiteNetwork), model_path); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_LOAD_MODEL_FROM_PATH); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + return ret; + } LITE_CAPI_END(); } int LITE_destroy_network(LiteNetwork network) { LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); - LITE_LOCK_GUARD(mtx_network); - auto& global_holder = get_gloabl_network_holder(); - if (global_holder.find(network) != global_holder.end()) { - global_holder.erase(network); + if (ipc_imp::is_server()) { + LITE_LOCK_GUARD(mtx_network); + auto& global_holder = get_gloabl_network_holder(); + if (global_holder.find(network) != global_holder.end()) { + global_holder.erase(network); + } + } else { + size_t need_size = sizeof(LiteNetwork); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(network); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &network, sizeof(LiteNetwork)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_DESTROY_NETWORK); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + return ret; } LITE_CAPI_END(); } @@ -252,14 +310,48 @@ int LITE_destroy_network(LiteNetwork network) { int LITE_forward(const LiteNetwork network) { LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); - static_cast(network)->forward(); + if (ipc_imp::is_server()) { + static_cast(network)->forward(); + } else { + size_t need_size = sizeof(LiteNetwork); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(network); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &network, sizeof(LiteNetwork)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_FORWARD); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + return ret; + } LITE_CAPI_END(); } int LITE_wait(const LiteNetwork network) { LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); - static_cast(network)->wait(); + if (ipc_imp::is_server()) { + static_cast(network)->wait(); + } else { + size_t need_size = sizeof(LiteNetwork); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(network); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &network, sizeof(LiteNetwork)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_WAIT); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + return ret; + } LITE_CAPI_END(); } @@ -268,9 +360,31 @@ int LITE_get_io_tensor( LiteTensor* tensor) { LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); - auto io_tensor = - static_cast(network)->get_io_tensor(io_name, phase); - *tensor = io_tensor.get(); + if (ipc_imp::is_server()) { + auto io_tensor = + static_cast(network)->get_io_tensor(io_name, phase); + *tensor = io_tensor.get(); + } else { + size_t need_size = + sizeof(LiteNetwork) + strlen(io_name) + 1 + sizeof(LiteTensorPhase); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(network); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &network, sizeof(LiteNetwork)); + memcpy(shm_ptr_c + sizeof(LiteNetwork), &phase, sizeof(LiteTensorPhase)); + strcpy(shm_ptr_c + sizeof(LiteNetwork) + sizeof(LiteTensorPhase), io_name); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_GET_IO_TENSOR); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + memcpy(tensor, ret_ptr, sizeof(LiteTensor)); + IPC_INSTACE().release_shm_ptr(network); + return ret; + } LITE_CAPI_END(); } @@ -286,8 +400,31 @@ int LITE_get_output_name(const LiteNetwork network, size_t index, const char** n LITE_CAPI_BEGIN(); LITE_ASSERT(network, "The network pass to LITE api is null"); LITE_ASSERT(name, "The name ptr pass to LITE api is null"); - *name = lite::NetworkHelper::implement(static_cast(network)) - ->get_output_name(index); + if (ipc_imp::is_server()) { + *name = lite::NetworkHelper::implement(static_cast(network)) + ->get_output_name(index); + } else { + size_t need_size = sizeof(LiteNetwork) + sizeof(index); + IPC_INSTACE().check_shm_size(need_size); + + //! warn: we use get_shm_ptr with nullptr, not with network + //! so caller need consume this ptr as soon as possible + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &network, sizeof(LiteNetwork)); + memcpy(shm_ptr_c + sizeof(LiteNetwork), &index, sizeof(size_t)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_GET_OUTPUT_NAME); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + void* p = static_cast(ret_ptr); + char* p_c = static_cast(p); + *name = p_c; + return ret; + } LITE_CAPI_END(); } diff --git a/lite/lite-c/src/tensor.cpp b/lite/lite-c/src/tensor.cpp index b72bec769..0c57cee2b 100644 --- a/lite/lite-c/src/tensor.cpp +++ b/lite/lite-c/src/tensor.cpp @@ -4,6 +4,7 @@ #include #include "../../src/tensor_impl_base.h" #include "common.h" +#include "ipc_helper.h" #include "lite-c/tensor_c.h" const LiteLayout default_layout = { @@ -166,7 +167,70 @@ int LITE_get_tensor_memory(const LiteTensor tensor, void** data) { LITE_CAPI_BEGIN(); LITE_ASSERT(tensor, "The tensor pass to LITE c_api is null"); LITE_ASSERT(data, "The data ptr pass to LITE c_api is null"); - *data = static_cast(tensor)->get_memory_ptr(); + if (ipc_imp::is_server()) { + *data = static_cast(tensor)->get_memory_ptr(); + } else { + size_t need_size = sizeof(LiteTensor); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &tensor, sizeof(LiteTensor)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_GET_TENSOR_MEMORY); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + memcpy(data, ret_ptr, sizeof(void*)); + return ret; + } + LITE_CAPI_END(); +} + +void* LITE_memset(void* s, int c, size_t n) { + if (ipc_imp::is_server()) { + return memset(s, c, n); + } else { + size_t need_size = sizeof(void*) + sizeof(int) + sizeof(size_t); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &s, sizeof(void*)); + memcpy(shm_ptr_c + sizeof(void*), &c, sizeof(int)); + memcpy(shm_ptr_c + sizeof(void*) + sizeof(int), &n, sizeof(size_t)); + + IPC_HELP_REMOTE_CALL(raw_shm_ptr, ipc::RemoteFuncId::LITE_MEMSET); + + return s; + } +} + +int LITE_copy_server_tensor_memory( + void* server_ptr, void* client_ptr, size_t size_in_byte) { + LITE_CAPI_BEGIN(); + if (ipc_imp::is_server()) { + LITE_ASSERT( + false, "lite not in fork debug mode, please do not call this function"); + } else { + size_t need_size = sizeof(void*) + sizeof(size_t); + IPC_INSTACE().check_shm_size(need_size); + IPC_INSTACE().check_shm_size(size_in_byte); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &server_ptr, sizeof(void*)); + memcpy(shm_ptr_c + sizeof(void*), &size_in_byte, sizeof(size_t)); + + IPC_HELP_REMOTE_CALL( + raw_shm_ptr, ipc::RemoteFuncId::LITE_COPY_SERVER_TENSOR_MEMORY); + memcpy(client_ptr, raw_shm_ptr, size_in_byte); + return 0; + } LITE_CAPI_END(); } @@ -186,7 +250,26 @@ int LITE_get_tensor_total_size_in_byte(const LiteTensor tensor, size_t* size) { LITE_CAPI_BEGIN(); LITE_ASSERT(tensor, "The tensor pass to LITE c_api is null"); LITE_ASSERT(size, "The size ptr pass to LITE c_api is null"); - *size = static_cast(tensor)->get_tensor_total_size_in_byte(); + if (ipc_imp::is_server()) { + *size = static_cast(tensor)->get_tensor_total_size_in_byte(); + } else { + size_t need_size = sizeof(LiteTensor); + IPC_INSTACE().check_shm_size(need_size); + + void* raw_shm_ptr = IPC_INSTACE().get_shm_ptr(nullptr); + + char* shm_ptr_c = static_cast(raw_shm_ptr); + memcpy(shm_ptr_c, &tensor, sizeof(LiteTensor)); + + IPC_HELP_REMOTE_CALL( + raw_shm_ptr, ipc::RemoteFuncId::LITE_GET_TENSOR_TOTAL_SIZE_IN_BYTE); + + int* ret_ptr = static_cast(raw_shm_ptr); + auto ret = *ret_ptr; + ret_ptr++; + memcpy(size, ret_ptr, sizeof(size_t)); + return ret; + } LITE_CAPI_END(); } diff --git a/lite/src/misc.cpp b/lite/src/misc.cpp index ecb882fe4..9e363f3b5 100644 --- a/lite/src/misc.cpp +++ b/lite/src/misc.cpp @@ -11,6 +11,7 @@ #ifdef __ANDROID__ #include +#include #endif using namespace lite; @@ -18,7 +19,26 @@ using namespace lite; namespace lite { namespace log_detail { -LiteLogLevel current_log_level = LiteLogLevel::ERROR; +LiteLogLevel config_default_log_level() { + auto default_level = LiteLogLevel::ERROR; + //! env to config LogLevel + //! DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, NO_LOG = 4 + //! for example , export RUNTIME_OVERRIDE_LOG_LEVEL=0, means set LogLevel to + //! DEBUG + if (auto env = ::std::getenv("RUNTIME_OVERRIDE_LOG_LEVEL")) + default_level = static_cast(std::stoi(env)); + +#ifdef __ANDROID__ + //! special for Android prop, attention: getprop may need permission + char buf[PROP_VALUE_MAX]; + if (__system_property_get("RUNTIME_OVERRIDE_LOG_LEVEL", buf) > 0) { + default_level = static_cast(atoi(buf)); + } +#endif + + return default_level; +} +LiteLogLevel current_log_level = config_default_log_level(); template constexpr size_t countof(T (&)[N]) { -- GitLab