1
0
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:
Eladash 2023-12-25 14:35:09 +02:00 committed by Elad Ashkenazi
parent 475191cf7f
commit f696107c90
4 changed files with 94 additions and 32 deletions

View File

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

View File

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

View File

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

View File

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