mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-24 19:52:37 +01:00
Add savestate data validity asserts
This commit is contained in:
parent
475191cf7f
commit
f696107c90
@ -2641,9 +2641,10 @@ void thread_base::exec()
|
||||
|
||||
sig_log.fatal("Thread terminated due to fatal error: %s", reason);
|
||||
|
||||
logs::listener::sync_all();
|
||||
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
logs::listener::sync_all();
|
||||
utils::trap();
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@ constexpr auto* g_fxo = &g_fixed_typemap;
|
||||
|
||||
enum class thread_state : u32;
|
||||
|
||||
extern u16 serial_breathe_and_tag(utils::serial& ar, std::string_view name, bool tag_bit);
|
||||
|
||||
// Helper namespace
|
||||
namespace id_manager
|
||||
{
|
||||
@ -270,16 +272,22 @@ namespace id_manager
|
||||
{
|
||||
vec.resize(T::id_count);
|
||||
|
||||
u32 i = ar.pop<u32>();
|
||||
usz highest = 0;
|
||||
|
||||
ensure(i <= T::id_count);
|
||||
|
||||
while (--i != umax)
|
||||
while (true)
|
||||
{
|
||||
// ID, type hash
|
||||
const u32 id = ar;
|
||||
const u16 tag = serial_breathe_and_tag(ar, g_fxo->get_name<id_map<T>>(), false);
|
||||
|
||||
const u128 type_init_pos = u128{u32{ar}} << 64 | std::bit_cast<u64>(T::savestate_init_pos);
|
||||
if (tag >> 15)
|
||||
{
|
||||
// End
|
||||
break;
|
||||
}
|
||||
|
||||
// ID, type hash
|
||||
const u32 id = ar.pop<u32>();
|
||||
|
||||
const u128 type_init_pos = u128{ar.pop<u32>()} << 64 | std::bit_cast<u64>(T::savestate_init_pos);
|
||||
const typeinfo* info = nullptr;
|
||||
|
||||
// Search load functions for the one of this type (see make_typeinfo() for explenation about key composition reasoning)
|
||||
@ -298,22 +306,21 @@ namespace id_manager
|
||||
// Simulate construction semantics (idm::last_id() value)
|
||||
g_id = id;
|
||||
|
||||
auto& obj = vec[get_index(id, info->base, info->step, info->count, info->invl_range)];
|
||||
const usz object_index = get_index(id, info->base, info->step, info->count, info->invl_range);
|
||||
auto& obj = ::at32(vec, object_index);
|
||||
ensure(!obj.second);
|
||||
|
||||
highest = std::max<usz>(highest, object_index + 1);
|
||||
|
||||
obj.first = id_key(id, static_cast<u32>(static_cast<u64>(type_init_pos >> 64)));
|
||||
obj.second = info->load(ar);
|
||||
}
|
||||
|
||||
vec.resize(highest);
|
||||
}
|
||||
|
||||
void save(utils::serial& ar) requires IdmSavable<T>
|
||||
{
|
||||
u32 obj_count = 0;
|
||||
usz obj_count_offs = ar.pos;
|
||||
|
||||
// To be patched at the end of the function
|
||||
ar(obj_count);
|
||||
|
||||
for (const auto& p : vec)
|
||||
{
|
||||
if (!p.second) continue;
|
||||
@ -333,20 +340,24 @@ namespace id_manager
|
||||
// Save each object with needed information
|
||||
if (info && info->savable(p.second.get()))
|
||||
{
|
||||
// Create a tag for each object
|
||||
serial_breathe_and_tag(ar, g_fxo->get_name<id_map<T>>(), false);
|
||||
|
||||
ar(p.first.value(), p.first.type());
|
||||
info->save(ar, p.second.get());
|
||||
obj_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Patch object count
|
||||
ar.patch_raw_data(obj_count_offs, &obj_count, sizeof(obj_count));
|
||||
// End sequence with tag bit set
|
||||
serial_breathe_and_tag(ar, g_fxo->get_name<id_map<T>>(), true);
|
||||
}
|
||||
|
||||
id_map& operator=(thread_state state) noexcept requires (std::is_assignable_v<T&, thread_state>)
|
||||
{
|
||||
if (private_copy.empty())
|
||||
if (private_copy.size() != vec.size())
|
||||
{
|
||||
private_copy.clear();
|
||||
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
// Save all entries
|
||||
|
@ -296,14 +296,37 @@ bool load_and_check_reserved(utils::serial& ar, usz size)
|
||||
|
||||
namespace stx
|
||||
{
|
||||
extern void serial_breathe(utils::serial& ar)
|
||||
extern u16 serial_breathe_and_tag(utils::serial& ar, std::string_view name, bool tag_bit)
|
||||
{
|
||||
thread_local std::string_view s_tls_object_name = "none";
|
||||
|
||||
constexpr u16 data_mask = 0x7fff;
|
||||
|
||||
if (ar.m_file_handler && ar.m_file_handler->is_null())
|
||||
{
|
||||
return (tag_bit ? data_mask + 1 : 0);
|
||||
}
|
||||
|
||||
u16 tag = static_cast<u16>((static_cast<u16>(ar.pos / 2) & data_mask) | (tag_bit ? data_mask + 1 : 0));
|
||||
u16 saved = tag;
|
||||
ar(saved);
|
||||
|
||||
sys_log.trace("serial_breathe_and_tag(): %s, object: '%s', next-object: '%s', expected/tag: 0x%x == 0x%x", ar, s_tls_object_name, name, tag, saved);
|
||||
|
||||
if ((saved ^ tag) & data_mask)
|
||||
{
|
||||
ensure(!ar.is_writing());
|
||||
fmt::throw_exception("serial_breathe_and_tag(): %s, object: '%s', next-object: '%s', expected/tag: 0x%x != 0x%x,", ar, s_tls_object_name, name, tag, saved);
|
||||
}
|
||||
|
||||
s_tls_object_name = name;
|
||||
ar.breathe();
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
|
||||
// MSVC bug workaround, see above similar case
|
||||
extern void serial_breathe(utils::serial& ar)
|
||||
extern u16 serial_breathe_and_tag(utils::serial& ar, std::string_view name, bool tag_bit)
|
||||
{
|
||||
::stx::serial_breathe(ar);
|
||||
return ::stx::serial_breathe_and_tag(ar, name, tag_bit);
|
||||
}
|
||||
|
@ -13,10 +13,17 @@ enum class thread_state : u32;
|
||||
|
||||
extern thread_local std::string_view g_tls_serialize_name;
|
||||
|
||||
namespace utils
|
||||
{
|
||||
struct serial;
|
||||
}
|
||||
|
||||
namespace stx
|
||||
{
|
||||
struct launch_retainer{};
|
||||
|
||||
extern u16 serial_breathe_and_tag(utils::serial& ar, std::string_view name, bool tag_bit);
|
||||
|
||||
// Simplified typemap with exactly one object of each used type, non-moveable. Initialized on init(). Destroyed on clear().
|
||||
template <typename Tag /*Tag should be unique*/, u32 Size = 0, u32 Align = (Size ? 64 : __STDCPP_DEFAULT_NEW_ALIGNMENT__)>
|
||||
class alignas(Align) manual_typemap
|
||||
@ -67,6 +74,7 @@ namespace stx
|
||||
void(*thread_op)(void* ptr, thread_state) noexcept = nullptr;
|
||||
void(*save)(void* ptr, utils::serial&) noexcept = nullptr;
|
||||
void(*destroy)(void* ptr) noexcept = nullptr;
|
||||
bool is_trivial_and_nonsavable = false;
|
||||
std::string_view name;
|
||||
|
||||
template <typename T>
|
||||
@ -143,7 +151,7 @@ namespace stx
|
||||
{
|
||||
static_assert(!std::is_copy_assignable_v<T> && !std::is_copy_constructible_v<T>, "Please make sure the object cannot be accidentally copied.");
|
||||
|
||||
typeinfo r;
|
||||
typeinfo r{};
|
||||
r.create = &call_ctor<T>;
|
||||
r.destroy = &call_dtor<T>;
|
||||
|
||||
@ -156,6 +164,9 @@ namespace stx
|
||||
{
|
||||
r.save = &call_save<T>;
|
||||
}
|
||||
|
||||
r.is_trivial_and_nonsavable = std::is_trivially_default_constructible_v<T> && !r.save;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
constexpr std::string_view name = parse_type(__FUNCSIG__);
|
||||
#else
|
||||
@ -237,7 +248,8 @@ namespace stx
|
||||
}
|
||||
|
||||
// Use unique_ptr to reduce header dependencies in this commonly used header
|
||||
const auto order = std::make_unique<std::pair<double, const type_info<typeinfo>*>[]>(stx::typelist<typeinfo>().count());
|
||||
const usz type_count = stx::typelist<typeinfo>().count();
|
||||
const auto order = std::make_unique<std::pair<double, const type_info<typeinfo>*>[]>(type_count);
|
||||
|
||||
usz pos = 0;
|
||||
for (const auto& type : stx::typelist<typeinfo>())
|
||||
@ -245,14 +257,19 @@ namespace stx
|
||||
order[pos++] = {type.init_pos(), std::addressof(type)};
|
||||
}
|
||||
|
||||
std::stable_sort(order.get(), order.get() + stx::typelist<typeinfo>().count(), [](auto a, auto b)
|
||||
std::stable_sort(order.get(), order.get() + type_count, [](auto& a, auto& b)
|
||||
{
|
||||
if (a.second->is_trivial_and_nonsavable && !b.second->is_trivial_and_nonsavable)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return a.first < b.first;
|
||||
});
|
||||
|
||||
const auto info_before = m_info;
|
||||
|
||||
for (pos = 0; pos < stx::typelist<typeinfo>().count(); pos++)
|
||||
for (pos = 0; pos < type_count; pos++)
|
||||
{
|
||||
const auto& type = *order[pos].second;
|
||||
|
||||
@ -271,10 +288,9 @@ namespace stx
|
||||
*m_info++ = &type;
|
||||
m_init[id] = true;
|
||||
|
||||
if (ar)
|
||||
if (ar && type.save)
|
||||
{
|
||||
extern void serial_breathe(utils::serial& ar);
|
||||
serial_breathe(*ar);
|
||||
serial_breathe_and_tag(*ar, type.name, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -372,10 +388,13 @@ namespace stx
|
||||
// Save data in forward order
|
||||
for (u32 i = _max; i; i--)
|
||||
{
|
||||
if (auto save = (*std::prev(m_info, i))->save)
|
||||
const auto info = (*std::prev(m_info, i));
|
||||
|
||||
if (auto save = info->save)
|
||||
{
|
||||
save(*std::prev(m_order, i), ar);
|
||||
ar.breathe();
|
||||
|
||||
serial_breathe_and_tag(ar, info->name, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -414,6 +433,8 @@ namespace stx
|
||||
|
||||
As* obj = nullptr;
|
||||
|
||||
const auto type_info = &stx::typedata<typeinfo, std::decay_t<T>, std::decay_t<As>>();
|
||||
|
||||
g_tls_serialize_name = get_name<T, As>();
|
||||
|
||||
if constexpr (Size != 0)
|
||||
@ -425,10 +446,16 @@ namespace stx
|
||||
obj = new (m_list + stx::typeoffset<typeinfo, std::decay_t<T>>()) std::decay_t<As>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
if constexpr ((std::is_same_v<std::remove_cvref_t<Args>, utils::serial> || ...))
|
||||
{
|
||||
ensure(type_info->save);
|
||||
serial_breathe_and_tag(std::get<0>(std::tie(args...)), get_name<T, As>(), false);
|
||||
}
|
||||
|
||||
g_tls_serialize_name = {};
|
||||
|
||||
*m_order++ = obj;
|
||||
*m_info++ = &stx::typedata<typeinfo, std::decay_t<T>, std::decay_t<As>>();
|
||||
*m_info++ = type_info;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user