Compare commits

...

Author SHA1 Message Date
  haolongzhangm f045d3fb36
Merge pull request #486 from lyq998/support-lite-fork-debug-mode 2 years ago
  liuyuqiao 37d81eae16 feat(lite): support base lite ipc fork debug 2 years ago
11 changed files with 1120 additions and 26 deletions
Split View
  1. +24
    -5
      lite/example/c_example/main.c
  2. +9
    -0
      lite/lite-c/include/lite-c/global_c.h
  3. +14
    -0
      lite/lite-c/include/lite-c/tensor_c.h
  4. +19
    -1
      lite/lite-c/src/global.cpp
  5. +260
    -0
      lite/lite-c/src/ipc_helper.cpp
  6. +101
    -0
      lite/lite-c/src/ipc_helper.h
  7. +370
    -0
      lite/lite-c/src/ipc_imp.cpp
  8. +63
    -0
      lite/lite-c/src/ipc_imp.h
  9. +154
    -17
      lite/lite-c/src/network.cpp
  10. +85
    -2
      lite/lite-c/src/tensor.cpp
  11. +21
    -1
      lite/src/misc.cpp

+ 24
- 5
lite/example/c_example/main.c View File

@@ -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}}}

+ 9
- 0
lite/lite-c/include/lite-c/global_c.h View File

@@ -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


+ 14
- 0
lite/lite-c/include/lite-c/tensor_c.h View File

@@ -159,6 +159,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
* \param[in] index The coordinate in the 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


+ 19
- 1
lite/lite-c/src/global.cpp View File

@@ -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<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) {


+ 260
- 0
lite/lite-c/src/ipc_helper.cpp View File

@@ -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<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

+ 101
- 0
lite/lite-c/src/ipc_helper.h View File

@@ -0,0 +1,101 @@
#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

+ 370
- 0
lite/lite-c/src/ipc_imp.cpp View File

@@ -0,0 +1,370 @@
#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

+ 63
- 0
lite/lite-c/src/ipc_imp.h View File

@@ -0,0 +1,63 @@
#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

+ 154
- 17
lite/lite-c/src/network.cpp View File

@@ -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<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();
}



+ 85
- 2
lite/lite-c/src/tensor.cpp View File

@@ -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();
}



+ 21
- 1
lite/src/misc.cpp View File

@@ -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]) {


Loading…
Cancel
Save