From f696107c902aac2fe5573d408979aca49a90a7ba Mon Sep 17 00:00:00 2001 From: Eladash <18193363+elad335@users.noreply.github.com> Date: Mon, 25 Dec 2023 14:35:09 +0200 Subject: [PATCH] Add savestate data validity asserts --- Utilities/Thread.cpp | 3 ++- rpcs3/Emu/IdManager.h | 47 +++++++++++++++++++++-------------- rpcs3/Emu/savestate_utils.cpp | 29 ++++++++++++++++++--- rpcs3/util/fixed_typemap.hpp | 47 +++++++++++++++++++++++++++-------- 4 files changed, 94 insertions(+), 32 deletions(-) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 2e890a4ec3..a81117e2d0 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -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(); } diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 6322cd7e41..facbfbc71b 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -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(); + 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>(), false); - const u128 type_init_pos = u128{u32{ar}} << 64 | std::bit_cast(T::savestate_init_pos); + if (tag >> 15) + { + // End + break; + } + + // ID, type hash + const u32 id = ar.pop(); + + const u128 type_init_pos = u128{ar.pop()} << 64 | std::bit_cast(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(highest, object_index + 1); + obj.first = id_key(id, static_cast(static_cast(type_init_pos >> 64))); obj.second = info->load(ar); } + + vec.resize(highest); } void save(utils::serial& ar) requires IdmSavable { - 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>(), 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>(), true); } id_map& operator=(thread_state state) noexcept requires (std::is_assignable_v) { - if (private_copy.empty()) + if (private_copy.size() != vec.size()) { + private_copy.clear(); + reader_lock lock(g_mutex); // Save all entries diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 6bc53fc7f1..4727b43487 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -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((static_cast(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); } diff --git a/rpcs3/util/fixed_typemap.hpp b/rpcs3/util/fixed_typemap.hpp index 6c42f3ad64..fac3208cfd 100644 --- a/rpcs3/util/fixed_typemap.hpp +++ b/rpcs3/util/fixed_typemap.hpp @@ -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 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 @@ -143,7 +151,7 @@ namespace stx { static_assert(!std::is_copy_assignable_v && !std::is_copy_constructible_v, "Please make sure the object cannot be accidentally copied."); - typeinfo r; + typeinfo r{}; r.create = &call_ctor; r.destroy = &call_dtor; @@ -156,6 +164,9 @@ namespace stx { r.save = &call_save; } + + r.is_trivial_and_nonsavable = std::is_trivially_default_constructible_v && !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*>[]>(stx::typelist().count()); + const usz type_count = stx::typelist().count(); + const auto order = std::make_unique*>[]>(type_count); usz pos = 0; for (const auto& type : stx::typelist()) @@ -245,14 +257,19 @@ namespace stx order[pos++] = {type.init_pos(), std::addressof(type)}; } - std::stable_sort(order.get(), order.get() + stx::typelist().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().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, std::decay_t>(); + g_tls_serialize_name = get_name(); if constexpr (Size != 0) @@ -425,10 +446,16 @@ namespace stx obj = new (m_list + stx::typeoffset>()) std::decay_t(std::forward(args)...); } + if constexpr ((std::is_same_v, utils::serial> || ...)) + { + ensure(type_info->save); + serial_breathe_and_tag(std::get<0>(std::tie(args...)), get_name(), false); + } + g_tls_serialize_name = {}; *m_order++ = obj; - *m_info++ = &stx::typedata, std::decay_t>(); + *m_info++ = type_info; return obj; }