未验证 提交 f045d3fb 编写于 作者: H haolongzhangm 提交者: GitHub

Merge pull request #486 from lyq998/support-lite-fork-debug-mode

feat(lite): support base lite ipc fork debug
......@@ -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);
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");
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,32 +256,102 @@ 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");
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");
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();
}
int LITE_forward(const LiteNetwork network) {
LITE_CAPI_BEGIN();
LITE_ASSERT(network, "The network pass to LITE api is null");
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");
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");
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");
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");
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");
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.
先完成此消息的编辑!
想要评论请 注册