提交 37d81eae 编写于 作者: L liuyuqiao 提交者: liuyuqiao

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.
上级 d4bf57d6
......@@ -14,6 +14,7 @@
#include "lite-c/tensor_c.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <model file> , just test C interface "
if (argc < 3) {
printf("usage: lite_c_examples is_enable_fork_debug_model <model file> , 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}}}
......@@ -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
......
......@@ -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
......
#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<char*>(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) {
......
#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<RemoteFuncId>(msg->remote_func_id)) {
case RemoteFuncId::LITE_MAKE_NETWORK: {
LiteNetwork network;
//! second args is const LiteConfig config
char* shm_ptr_c = static_cast<char*>(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<int*>(msg->shm_ptr);
*ret_ptr = ret;
ret_ptr++;
void* network_p = static_cast<void*>(ret_ptr);
memcpy(network_p, &network, sizeof(LiteNetwork));
}; break;
case RemoteFuncId::LITE_LOAD_MODEL_FROM_PATH: {
LiteNetwork network;
char* shm_ptr_c = static_cast<char*>(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<int*>(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<char*>(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<char*>(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<int*>(msg->shm_ptr);
*ret_ptr = ret;
ret_ptr++;
void* lite_tensor_p = static_cast<void*>(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<char*>(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<int*>(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<char*>(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<int*>(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<char*>(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<char*>(msg->shm_ptr);
LiteNetwork network;
memcpy(&network, shm_ptr_c, sizeof(LiteNetwork));
int ret = LITE_forward(network);
int* ret_ptr = static_cast<int*>(msg->shm_ptr);
*ret_ptr = ret;
}; break;
case RemoteFuncId::LITE_WAIT: {
char* shm_ptr_c = static_cast<char*>(msg->shm_ptr);
LiteNetwork network;
memcpy(&network, shm_ptr_c, sizeof(LiteNetwork));
int ret = LITE_wait(network);
int* ret_ptr = static_cast<int*>(msg->shm_ptr);
*ret_ptr = ret;
}; break;
case RemoteFuncId::LITE_GET_OUTPUT_NAME: {
char* shm_ptr_c = static_cast<char*>(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<int*>(msg->shm_ptr);
*ret_ptr = ret;
ret_ptr++;
void* p = static_cast<void*>(ret_ptr);
char* p_c = static_cast<char*>(p);
strcpy(p_c, name);
}; break;
case RemoteFuncId::LITE_COPY_SERVER_TENSOR_MEMORY: {
char* shm_ptr_c = static_cast<char*>(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<char*>(msg->shm_ptr);
LiteNetwork network;
memcpy(&network, shm_ptr_c, sizeof(LiteNetwork));
int ret = LITE_destroy_network(network);
int* ret_ptr = static_cast<int*>(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
#pragma once
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
#include <unordered_map>
#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<void*>(SHM_PTR); \
msg.remote_func_id = static_cast<size_t>(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 T>
class Singleton {
public:
Singleton() {}
static T& Instance() {
static T _;
return _;
}
};
class IpcHelper : public Singleton<IpcHelper> {
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 <shm_ptr, consumer_ptr>,
std::map<void*, void*> 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
#include "ipc_imp.h"
#if __linux__
#include <sys/prctl.h>
#include <sys/wait.h>
#endif
#ifdef __ANDROID__
#include <android/log.h>
#include <sys/system_properties.h>
#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
#pragma once
#undef _LITE_SUPPORT_IPC
#if __linux__ || __unix__ || __APPLE__
#define _LITE_SUPPORT_IPC
#endif
#ifdef _LITE_SUPPORT_IPC
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#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
#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<lite::Network>(
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<lite::Network>(
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<char*>(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<int*>(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<lite::Network*>(network)->load_model(model_path);
if (ipc_imp::is_server()) {
static_cast<lite::Network*>(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<char*>(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<int*>(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<char*>(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<int*>(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<lite::Network*>(network)->forward();
if (ipc_imp::is_server()) {
static_cast<lite::Network*>(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<char*>(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<int*>(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<lite::Network*>(network)->wait();
if (ipc_imp::is_server()) {
static_cast<lite::Network*>(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<char*>(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<int*>(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<lite::Network*>(network)->get_io_tensor(io_name, phase);
*tensor = io_tensor.get();
if (ipc_imp::is_server()) {
auto io_tensor =
static_cast<lite::Network*>(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<char*>(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<int*>(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<lite::Network*>(network))
->get_output_name(index);
if (ipc_imp::is_server()) {
*name = lite::NetworkHelper::implement(static_cast<lite::Network*>(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<char*>(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<int*>(raw_shm_ptr);
auto ret = *ret_ptr;
ret_ptr++;
void* p = static_cast<void*>(ret_ptr);
char* p_c = static_cast<char*>(p);
*name = p_c;
return ret;
}
LITE_CAPI_END();
}
......
......@@ -4,6 +4,7 @@
#include <unordered_map>
#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<lite::Tensor*>(tensor)->get_memory_ptr();
if (ipc_imp::is_server()) {
*data = static_cast<lite::Tensor*>(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<char*>(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<int*>(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<char*>(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<char*>(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<lite::Tensor*>(tensor)->get_tensor_total_size_in_byte();
if (ipc_imp::is_server()) {
*size = static_cast<lite::Tensor*>(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<char*>(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<int*>(raw_shm_ptr);
auto ret = *ret_ptr;
ret_ptr++;
memcpy(size, ret_ptr, sizeof(size_t));
return ret;
}
LITE_CAPI_END();
}
......
......@@ -11,6 +11,7 @@
#ifdef __ANDROID__
#include <android/log.h>
#include <sys/system_properties.h>
#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<LiteLogLevel>(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<LiteLogLevel>(atoi(buf));
}
#endif
return default_level;
}
LiteLogLevel current_log_level = config_default_log_level();
template <class T, size_t N>
constexpr size_t countof(T (&)[N]) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册