feat add hot_reloader
Some checks failed
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 1m21s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Failing after 1m35s
linux-arm-gcc / linux-gcc-armhf (push) Failing after 1m44s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 1m59s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 2m1s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 2m16s
Some checks failed
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 1m21s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Failing after 1m35s
linux-arm-gcc / linux-gcc-armhf (push) Failing after 1m44s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 1m59s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 2m1s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 2m16s
This commit is contained in:
parent
ebea797d67
commit
5b45b01611
@ -80,6 +80,7 @@ target_sources(
|
|||||||
src/sled/synchronization/sequence_checker.cc
|
src/sled/synchronization/sequence_checker.cc
|
||||||
src/sled/synchronization/thread_local.cc
|
src/sled/synchronization/thread_local.cc
|
||||||
src/sled/system/location.cc
|
src/sled/system/location.cc
|
||||||
|
src/sled/system/hot_reloader.cc
|
||||||
src/sled/system/pid.cc
|
src/sled/system/pid.cc
|
||||||
src/sled/system/thread.cc
|
src/sled/system/thread.cc
|
||||||
src/sled/system/thread_pool.cc
|
src/sled/system/thread_pool.cc
|
||||||
@ -211,6 +212,21 @@ if(SLED_BUILD_TESTS)
|
|||||||
sled_add_test(
|
sled_add_test(
|
||||||
NAME sled_cache_test SRCS src/sled/cache/lru_cache_test.cc
|
NAME sled_cache_test SRCS src/sled/cache/lru_cache_test.cc
|
||||||
src/sled/cache/fifo_cache_test.cc src/sled/cache/expire_cache_test.cc)
|
src/sled/cache/fifo_cache_test.cc src/sled/cache/expire_cache_test.cc)
|
||||||
|
|
||||||
|
add_library(hot_reloader_test_dynamic SHARED
|
||||||
|
src/sled/system/hot_reloader_test_dynamic.cc)
|
||||||
|
target_link_libraries(hot_reloader_test_dynamic PRIVATE sled)
|
||||||
|
message(
|
||||||
|
STATUS "HOT_RELOADER_TEST_DYNAMIC_LOCATION ${hot_reloader_test_dynamic}")
|
||||||
|
get_target_property(HOT_RELOADER_TEST_DYNAMIC_LOCATION
|
||||||
|
hot_reloader_test_dynamic LINK_LIBRARIES)
|
||||||
|
|
||||||
|
sled_add_test(
|
||||||
|
NAME sled_hot_reloader_test SRCS src/sled/system/hot_reloader_test.cc LIBS
|
||||||
|
hot_reloader_test_dynamic)
|
||||||
|
target_compile_definitions(
|
||||||
|
sled_hot_reloader_test
|
||||||
|
PRIVATE "TEST_BIN_PATH=\"${HOT_RELOADER_TEST_DYNAMIC_LOCATION}\"")
|
||||||
endif(SLED_BUILD_TESTS)
|
endif(SLED_BUILD_TESTS)
|
||||||
|
|
||||||
if(SLED_BUILD_FUZZ)
|
if(SLED_BUILD_FUZZ)
|
||||||
|
@ -7,6 +7,36 @@
|
|||||||
#else
|
#else
|
||||||
#define SLED_THREAD_ANNOTATION_ATTRIBUTE__(x)// no-op
|
#define SLED_THREAD_ANNOTATION_ATTRIBUTE__(x)// no-op
|
||||||
#endif
|
#endif
|
||||||
|
//
|
||||||
|
// Global compiler specific defines/customizations
|
||||||
|
//
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define SLED_EXPORT extern "C" __declspec(dllexport)
|
||||||
|
#define SLED_IMPORT extern "C" __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
#define SLED_EXPORT __declspec(dllexport)
|
||||||
|
#define SLED_IMPORT __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#endif// defined(_MSC_VER)
|
||||||
|
|
||||||
|
#if defined(__GNUC__)// clang & gcc
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define SLED_EXPORT extern "C" __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define SLED_EXPORT __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
#define SLED_IMPORT
|
||||||
|
#endif// defined(__GNUC__)
|
||||||
|
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
#undef SLED_EXPORT
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
#define SLED_EXPORT extern "C" __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define SLED_EXPORT __declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SLED_NODISCARD SLED_THREAD_ANNOTATION_ATTRIBUTE__(__warn_unused_result__)
|
#define SLED_NODISCARD SLED_THREAD_ANNOTATION_ATTRIBUTE__(__warn_unused_result__)
|
||||||
#define SLED_DEPRECATED SLED_THREAD_ANNOTATION_ATTRIBUTE__(deprecated)
|
#define SLED_DEPRECATED SLED_THREAD_ANNOTATION_ATTRIBUTE__(deprecated)
|
||||||
|
@ -865,11 +865,11 @@ nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472)
|
|||||||
pos >= size() ? npos
|
pos >= size() ? npos
|
||||||
: to_pos(
|
: to_pos(
|
||||||
#if nssv_CPP11_OR_GREATER && !nssv_CPP17_OR_GREATER
|
#if nssv_CPP11_OR_GREATER && !nssv_CPP17_OR_GREATER
|
||||||
detail::search(substr(pos), v)
|
detail::search(substr(pos), v)
|
||||||
#else
|
#else
|
||||||
std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq)
|
std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq)
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
nssv_constexpr size_type find(CharT c, size_type pos = 0) const nssv_noexcept// (2)
|
nssv_constexpr size_type find(CharT c, size_type pos = 0) const nssv_noexcept// (2)
|
||||||
|
1954
src/sled/system/detail/cr.h
Normal file
1954
src/sled/system/detail/cr.h
Normal file
File diff suppressed because it is too large
Load Diff
136
src/sled/system/hot_reloader.cc
Normal file
136
src/sled/system/hot_reloader.cc
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "sled/system/hot_reloader.h"
|
||||||
|
#include "sled/log/log.h"
|
||||||
|
|
||||||
|
#define CR_HOST CR_SAFEST
|
||||||
|
#define CR_MAIN_FUNC "sled_hot_loader_main"
|
||||||
|
#include "sled/system/detail/cr.h"
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
namespace detail {
|
||||||
|
HotReloader::Failure
|
||||||
|
TranslateFailure(cr_failure failure)
|
||||||
|
{
|
||||||
|
switch (failure) {
|
||||||
|
case CR_NONE:
|
||||||
|
return HotReloader::Failure::kOk;
|
||||||
|
case CR_SEGFAULT:
|
||||||
|
return HotReloader::Failure::kSegmentationFault;
|
||||||
|
case CR_ILLEGAL:
|
||||||
|
return HotReloader::Failure::kIllegal;
|
||||||
|
case CR_ABORT:
|
||||||
|
return HotReloader::Failure::kAbort;
|
||||||
|
case CR_MISALIGN:
|
||||||
|
return HotReloader::Failure::kMisalign;
|
||||||
|
case CR_BOUNDS:
|
||||||
|
return HotReloader::Failure::kBounds;
|
||||||
|
case CR_STACKOVERFLOW:
|
||||||
|
return HotReloader::Failure::kStackOverflow;
|
||||||
|
case CR_STATE_INVALIDATED:
|
||||||
|
return HotReloader::Failure::kStateInvalidated;
|
||||||
|
case CR_BAD_IMAGE:
|
||||||
|
return HotReloader::Failure::kBadImage;
|
||||||
|
case CR_INITIAL_FAILURE:
|
||||||
|
return HotReloader::Failure::kInitialFailure;
|
||||||
|
case CR_OTHER:
|
||||||
|
return HotReloader::Failure::kOther;
|
||||||
|
default:
|
||||||
|
return HotReloader::Failure::kOther;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
std::string
|
||||||
|
DynamicLibraryName(sled::string_view base_name)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
return sled::to_string(base_name) + ".dll";
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return "lib" + sled::to_string(base_name) + ".dylib";
|
||||||
|
#elif defined(__linux__)
|
||||||
|
return "lib" + sled::to_string(base_name) + ".so";
|
||||||
|
#else
|
||||||
|
#error "Unsupported platform"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
class HotReloader::Impl {
|
||||||
|
public:
|
||||||
|
Impl(sled::string_view bin_path) : bin_path_(sled::to_string(bin_path)) {}
|
||||||
|
|
||||||
|
~Impl() { cr_plugin_close(ctx_); }
|
||||||
|
|
||||||
|
bool Initialize()
|
||||||
|
{
|
||||||
|
if (cr_plugin_open(ctx_, bin_path_.c_str())) { return true; }
|
||||||
|
LOGV("HotReloader", "Failed to load plugin: {}", bin_path_);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sled::optional<Failure> Update(bool force_reload)
|
||||||
|
{
|
||||||
|
if (cr_plugin_update(ctx_, force_reload) == 0) { return sled::nullopt; }
|
||||||
|
return detail::TranslateFailure(ctx_.failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_userdata(void *data) { ctx_.userdata = data; }
|
||||||
|
|
||||||
|
sled::string_view name() const { return bin_path_; }
|
||||||
|
|
||||||
|
VersionType version() const { return ctx_.version; }
|
||||||
|
|
||||||
|
VersionType last_working_version() const { return ctx_.last_working_version; }
|
||||||
|
|
||||||
|
VersionType next_version() const { return ctx_.next_version; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string bin_path_;
|
||||||
|
cr_plugin ctx_;
|
||||||
|
};
|
||||||
|
|
||||||
|
HotReloader::HotReloader(sled::string_view bin_path) : impl_(new Impl(bin_path)) {}
|
||||||
|
|
||||||
|
HotReloader::~HotReloader() {}
|
||||||
|
|
||||||
|
bool
|
||||||
|
HotReloader::Initialize()
|
||||||
|
{
|
||||||
|
return impl_->Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
sled::optional<HotReloader::Failure>
|
||||||
|
HotReloader::UpdateOrError(bool force_reload)
|
||||||
|
{
|
||||||
|
return impl_->Update(force_reload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HotReloader::set_userdata(void *data)
|
||||||
|
{
|
||||||
|
impl_->set_userdata(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
sled::string_view
|
||||||
|
HotReloader::name() const
|
||||||
|
{
|
||||||
|
return impl_->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
HotReloader::VersionType
|
||||||
|
HotReloader::version() const
|
||||||
|
{
|
||||||
|
return impl_->version();
|
||||||
|
}
|
||||||
|
|
||||||
|
HotReloader::VersionType
|
||||||
|
HotReloader::last_working_version() const
|
||||||
|
{
|
||||||
|
return impl_->last_working_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
HotReloader::VersionType
|
||||||
|
HotReloader::next_version() const
|
||||||
|
{
|
||||||
|
return impl_->next_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace sled
|
94
src/sled/system/hot_reloader.h
Normal file
94
src/sled/system/hot_reloader.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#ifndef SLED_SYSTEM_HOT_LOADER_H
|
||||||
|
#define SLED_SYSTEM_HOT_LOADER_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "sled/nonstd/string_view.h"
|
||||||
|
#include "sled/optional.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// hot reload library should implement
|
||||||
|
// sled_hot_loader_main function
|
||||||
|
// if success return 0,
|
||||||
|
// otherwise return negative value (reversed -1, -2)
|
||||||
|
// int sled_hot_loader_main(void* ctx, int op) {
|
||||||
|
// switch(op) {
|
||||||
|
// case 0: on_load(...);
|
||||||
|
// case 2: on_unload(...);
|
||||||
|
// case 3: on_close(...);
|
||||||
|
// }
|
||||||
|
// if (failure) {
|
||||||
|
// return -3;
|
||||||
|
// }
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
namespace sled {
|
||||||
|
// Windows return <base_name>.dll
|
||||||
|
// linux return lib<base_name>.so
|
||||||
|
// macos return lib<base_name>.dylib
|
||||||
|
std::string DynamicLibraryName(sled::string_view base_name);
|
||||||
|
|
||||||
|
class HotReloader {
|
||||||
|
public:
|
||||||
|
using VersionType = unsigned int;
|
||||||
|
|
||||||
|
// cr_mode defines how much we validate global state transfer between
|
||||||
|
// instances. The default is CR_UNSAFE, you can choose another mode by
|
||||||
|
// defining CR_HOST, ie.: #define CR_HOST CR_SAFEST
|
||||||
|
|
||||||
|
enum class LoadMode {
|
||||||
|
kSafest = 0, // validate address and size of the state section,
|
||||||
|
// if anything changes the load will rollback
|
||||||
|
kSafe = 1, // validate only the size of the state section,
|
||||||
|
// if it changes the load will rollback
|
||||||
|
kUnsafe = 2, // don't validate anything but that the size of section fits
|
||||||
|
// may not be identical though
|
||||||
|
kDisable = 3,// disable the auto state transfer
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Operation {
|
||||||
|
kLoad = 0,
|
||||||
|
kStep = 1,
|
||||||
|
kUnload = 2,
|
||||||
|
kClose = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Failure {
|
||||||
|
kOk = 0,
|
||||||
|
kSegmentationFault = 1,// SIGSEGV
|
||||||
|
kIllegal = 2,// SIGILL
|
||||||
|
kAbort = 3,// SIGBRT
|
||||||
|
kMisalign = 4,// SIGBUS
|
||||||
|
kBounds = 5,// ARRAY BOUNDS EXEEDED
|
||||||
|
kStackOverflow = 6,
|
||||||
|
kStateInvalidated = 7,
|
||||||
|
kBadImage = 8,// invalid binary
|
||||||
|
kInitialFailure = 9,
|
||||||
|
kOther = 10,
|
||||||
|
kUser = 0x100,
|
||||||
|
};
|
||||||
|
|
||||||
|
HotReloader(sled::string_view bin_path);
|
||||||
|
~HotReloader();
|
||||||
|
|
||||||
|
bool Initialize();
|
||||||
|
/**
|
||||||
|
* @brief 如果 Update失败,再次Update将会回滚
|
||||||
|
* @force_reload: 如果当前库已经加载,是否reload当前库
|
||||||
|
* @ status: 如果不为nullptr,返回当前加载状态
|
||||||
|
**/
|
||||||
|
sled::optional<Failure> UpdateOrError(bool force_reload = true);
|
||||||
|
// bool Rollback();
|
||||||
|
|
||||||
|
void set_userdata(void *data);
|
||||||
|
sled::string_view name() const;
|
||||||
|
VersionType version() const;
|
||||||
|
VersionType last_working_version() const;
|
||||||
|
VersionType next_version() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
};
|
||||||
|
}// namespace sled
|
||||||
|
|
||||||
|
#endif// SLED_SYSTEM_HOT_LOADER_H
|
64
src/sled/system/hot_reloader_test.cc
Normal file
64
src/sled/system/hot_reloader_test.cc
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <sled/sled.h>
|
||||||
|
#include <sled/system/hot_reloader.h>
|
||||||
|
#ifndef TEST_BIN_PATH
|
||||||
|
#error "must define TEST_BIN_PATH"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_SUITE("Hot Loader")
|
||||||
|
{
|
||||||
|
TEST_CASE("DynamicLibraryName")
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
CHECK_EQ(sled::DynamicLibraryName("test"), "test.dll");
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
CHECK_EQ(sled::DynamicLibraryName("test"), "libtest.dylib");
|
||||||
|
#elif defined(__linux__)
|
||||||
|
CHECK_EQ(sled::DynamicLibraryName("test"), "libtest.so");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("crash_test")
|
||||||
|
{
|
||||||
|
sled::HotReloader reloader(sled::DynamicLibraryName("hot_reloader_test_dynamic"));
|
||||||
|
CHECK_MESSAGE(reloader.Initialize(), "Initialize failed, ", reloader.name());
|
||||||
|
CHECK_EQ(reloader.version(), 0);
|
||||||
|
CHECK_EQ(reloader.next_version(), 1);
|
||||||
|
CHECK_EQ(reloader.last_working_version(), 0);
|
||||||
|
|
||||||
|
int op;
|
||||||
|
reloader.set_userdata(&op);
|
||||||
|
|
||||||
|
SUBCASE("OnLoad Crash")
|
||||||
|
{
|
||||||
|
// OnLoad crash test
|
||||||
|
op = 0;
|
||||||
|
sled::optional<sled::HotReloader::Failure> error = reloader.UpdateOrError();
|
||||||
|
REQUIRE(error);
|
||||||
|
CHECK_EQ(*error, sled::HotReloader::Failure::kSegmentationFault);
|
||||||
|
CHECK_EQ(op, -1);
|
||||||
|
|
||||||
|
CHECK_EQ(reloader.version(), 0);
|
||||||
|
CHECK_EQ(reloader.next_version(), 2);
|
||||||
|
CHECK_EQ(reloader.last_working_version(), 0);
|
||||||
|
}
|
||||||
|
SUBCASE("OnClose crash")
|
||||||
|
{
|
||||||
|
// OnClose crash test
|
||||||
|
op = 3;
|
||||||
|
sled::optional<sled::HotReloader::Failure> error = reloader.UpdateOrError();
|
||||||
|
REQUIRE_FALSE(error);
|
||||||
|
CHECK_EQ(op, -1);
|
||||||
|
CHECK_EQ(reloader.version(), 1);
|
||||||
|
CHECK_EQ(reloader.next_version(), 2);
|
||||||
|
CHECK_EQ(reloader.last_working_version(), 0);
|
||||||
|
}
|
||||||
|
SUBCASE("OnClose crash")
|
||||||
|
{
|
||||||
|
// OnStep crash test
|
||||||
|
op = 1;
|
||||||
|
sled::optional<sled::HotReloader::Failure> error = reloader.UpdateOrError();
|
||||||
|
REQUIRE_FALSE(error);
|
||||||
|
CHECK_EQ(op, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/sled/system/hot_reloader_test_dynamic.cc
Normal file
50
src/sled/system/hot_reloader_test_dynamic.cc
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <sled/sled.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
crash()
|
||||||
|
{
|
||||||
|
int *addr = nullptr;
|
||||||
|
(void) ++*addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SLED_EXPORT int
|
||||||
|
sled_hot_loader_main(void *ctx, int op)
|
||||||
|
{
|
||||||
|
struct ctx_impl {
|
||||||
|
void *p;
|
||||||
|
void *userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
void *userdata = reinterpret_cast<ctx_impl *>(ctx)->userdata;
|
||||||
|
int *value_ptr = reinterpret_cast<int *>(userdata);
|
||||||
|
int crash_type = *value_ptr;
|
||||||
|
*value_ptr = -1;
|
||||||
|
LOGD("plugin ", "sled_hot_loader_main: op={}, crash={}", op, crash_type);
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
// On Load
|
||||||
|
case 0: {
|
||||||
|
if (crash_type == 0) { crash(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// On Step
|
||||||
|
case 1: {
|
||||||
|
if (crash_type == 1) { crash(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// OnUnload
|
||||||
|
case 2: {
|
||||||
|
if (crash_type == 2) { crash(); }
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// OnClose
|
||||||
|
case 3: {
|
||||||
|
if (crash_type == 3) { crash(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user