mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-27 17:31:31 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d77c14a1a4 |
-1
@@ -11,7 +11,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
||||
DRIVER_PATH("driver_path"),
|
||||
DEVICE_NAME("device_name"),
|
||||
LOG_FILTER("log_filter"),
|
||||
PROGRAM_ARGS("program_args"),
|
||||
|
||||
WEB_TOKEN("eden_token"),
|
||||
|
||||
-7
@@ -1032,13 +1032,6 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.use_auto_stub_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
StringInputSetting(
|
||||
StringSetting.LOG_FILTER,
|
||||
titleId = R.string.log_filter,
|
||||
descriptionId = R.string.log_filter_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
ShortSetting.DEBUG_KNOBS,
|
||||
|
||||
-1
@@ -1322,7 +1322,6 @@ class SettingsFragmentPresenter(
|
||||
add(HeaderSetting(R.string.log))
|
||||
|
||||
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
|
||||
add(StringSetting.LOG_FILTER.key)
|
||||
}
|
||||
|
||||
add(HeaderSetting(R.string.general))
|
||||
|
||||
@@ -47,7 +47,6 @@ import info.debatty.java.stringsimilarity.Jaccard
|
||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||
import java.util.Locale
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.doOnNextLayout
|
||||
|
||||
class GamesFragment : Fragment() {
|
||||
private var _binding: FragmentGamesBinding? = null
|
||||
@@ -59,7 +58,6 @@ class GamesFragment : Fragment() {
|
||||
private var originalHeaderLeftMargin: Int? = null
|
||||
|
||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||
private var fallbackBottomInset: Int = 0
|
||||
private var pendingPostReloadListSettle = false
|
||||
private var pendingPostReloadListSettleGeneration = 0
|
||||
private var gameListSubmitGeneration = 0
|
||||
@@ -227,12 +225,7 @@ class GamesFragment : Fragment() {
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||
}
|
||||
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||
}
|
||||
} else {
|
||||
if (savedViewType != GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||
}
|
||||
adapter = gameAdapter
|
||||
@@ -590,11 +583,6 @@ class GamesFragment : Fragment() {
|
||||
qlaunchButton.layoutParams = mlpQLaunch
|
||||
}
|
||||
|
||||
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
fallbackBottomInset = bottomInset
|
||||
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,16 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||
/**
|
||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||
* It manages overlapping cards, center snapping, custom drawing order,
|
||||
@@ -32,7 +35,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
private var overlapFactor: Float = 0f
|
||||
private var overlapPx: Int = 0
|
||||
private var bottomInset: Int = -1
|
||||
private var bottomInset: Int = 0
|
||||
private var latestWindowInsets: WindowInsetsCompat? = null
|
||||
private var cardGeometryInitialized: Boolean = false
|
||||
private var overlapDecoration: OverlappingDecoration? = null
|
||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||
private var scalingScrollListener: OnScrollListener? = null
|
||||
@@ -91,6 +96,38 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
init {
|
||||
setChildrenDrawingOrderEnabled(true)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
||||
latestWindowInsets = insets
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
ViewCompat.requestApplyInsets(this)
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
if (w != oldw || h != oldh) {
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
if (isCarouselMode) updateChildScalesAndAlpha()
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
ViewCompat.requestApplyInsets(this)
|
||||
post { updateCardGeometry() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAdapter(adapter: Adapter<*>?) {
|
||||
@@ -103,6 +140,8 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
super.setAdapter(adapter)
|
||||
|
||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||
updateCardGeometry()
|
||||
applyCarouselPadding()
|
||||
}
|
||||
|
||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||
@@ -253,40 +292,71 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyInsetsReady(newBottomInset: Int) {
|
||||
if (bottomInset != newBottomInset) {
|
||||
bottomInset = newBottomInset
|
||||
}
|
||||
|
||||
if (isCarouselMode) {
|
||||
setupCarousel(true)
|
||||
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
||||
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
||||
0
|
||||
} else {
|
||||
setupCarousel(false)
|
||||
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
||||
}
|
||||
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsetsCompat.Type.systemGestures()
|
||||
)
|
||||
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
||||
WindowInsetsCompat.Type.displayCutout()
|
||||
)
|
||||
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
}
|
||||
|
||||
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||
var gameAdapter = adapter as? GameAdapter ?: return
|
||||
var newCardSize = cardSize(bottomInset)
|
||||
if (gameAdapter.cardSize != newCardSize) {
|
||||
gameAdapter.setCardSize(newCardSize)
|
||||
}
|
||||
private fun updateCardGeometry() {
|
||||
if (!isCarouselMode || height <= 0) return
|
||||
|
||||
if (isCarouselMode) {
|
||||
setupCarousel(true)
|
||||
}
|
||||
}
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
||||
|
||||
fun cardSize(bottomInset: Int): Int {
|
||||
if (cardGeometryInitialized && !hasWindowFocus()) return
|
||||
|
||||
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val scaledHeight = height * userFactor
|
||||
val availableHeight = height - bottomInset
|
||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
||||
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
||||
val aspectFactor = ((screenWidth / screenHeight) / (20f / 9f))
|
||||
.pow(0.75f)
|
||||
.coerceIn(0.5f, 1f)
|
||||
val newCardSize = minOf(
|
||||
(height * userFactor).toInt(),
|
||||
height - newBottomInset,
|
||||
(height * aspectFactor).toInt()
|
||||
)
|
||||
if (newCardSize <= 0) return
|
||||
val insetChanged = bottomInset != newBottomInset
|
||||
val cardSizeChanged = gameAdapter.cardSize != newCardSize
|
||||
|
||||
bottomInset = newBottomInset
|
||||
cardGeometryInitialized = true
|
||||
|
||||
if (cardSizeChanged) gameAdapter.setCardSize(newCardSize)
|
||||
if (insetChanged || cardSizeChanged) setupCarousel(true)
|
||||
}
|
||||
|
||||
private fun applyCarouselPadding() {
|
||||
if (!isCarouselMode) return
|
||||
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
val cardSize = gameAdapter.cardSize
|
||||
if (cardSize <= 0 || bottomInset < 0) return
|
||||
|
||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0)
|
||||
val sidePadding = (width - cardSize) / 2
|
||||
if (paddingLeft != sidePadding || paddingTop != topPadding ||
|
||||
paddingRight != sidePadding || paddingBottom != 0
|
||||
) {
|
||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||
}
|
||||
clipToPadding = false
|
||||
}
|
||||
|
||||
fun setupCarousel(enabled: Boolean) {
|
||||
@@ -315,9 +385,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
internalFlingMultiplier
|
||||
).coerceIn(1f, 5f)
|
||||
|
||||
// Detach SnapHelper during setup
|
||||
pagerSnapHelper?.attachToRecyclerView(null)
|
||||
|
||||
// Add overlap decoration if not present
|
||||
if (overlapDecoration == null) {
|
||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||
@@ -335,12 +402,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
addOnScrollListener(scalingScrollListener!!)
|
||||
}
|
||||
|
||||
if (cardSize > 0) {
|
||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0) // Center vertically
|
||||
val sidePadding = (width - cardSize) / 2 // Center first/last card
|
||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||
clipToPadding = false
|
||||
}
|
||||
applyCarouselPadding()
|
||||
|
||||
if (pagerSnapHelper == null) {
|
||||
pagerSnapHelper = CenterPagerSnapHelper()
|
||||
@@ -362,6 +424,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
savedItemAnimator = null
|
||||
}
|
||||
cardGeometryInitialized = false
|
||||
useCustomDrawingOrder = false
|
||||
// Reset padding and fling
|
||||
setPadding(0, 0, 0, 0)
|
||||
|
||||
@@ -636,8 +636,6 @@
|
||||
<string name="log">Logging</string>
|
||||
<string name="flush_by_line">Flush debug logs by line</string>
|
||||
<string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string>
|
||||
<string name="log_filter">Log filter</string>
|
||||
<string name="log_filter_description">Controls Eden\'s log categories. Example: *:Info Service.LM:Debug</string>
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU Logging</string>
|
||||
|
||||
@@ -904,7 +904,7 @@ struct Values {
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::System,
|
||||
Category::Debugging,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
|
||||
+14
-182
@@ -3,158 +3,14 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/debug.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t GuestStringProbeBytes = 0x100;
|
||||
constexpr std::size_t StackProbeWords = 96;
|
||||
constexpr std::size_t MaxGuestStringLogs = 16;
|
||||
constexpr std::size_t MaxBacktraceFrames = 64;
|
||||
constexpr std::size_t MinGuestStringLength = 4;
|
||||
|
||||
std::string SanitizeGuestString(std::string_view text) {
|
||||
std::string sanitized;
|
||||
sanitized.reserve(text.size());
|
||||
|
||||
for (const char ch : text) {
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
sanitized += "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
sanitized += "\\\"";
|
||||
break;
|
||||
default:
|
||||
sanitized += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
bool IsUsefulGuestString(std::string_view text) {
|
||||
if (text.size() < MinGuestStringLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t alpha_numeric_count{};
|
||||
for (const char ch : text) {
|
||||
const auto byte = static_cast<unsigned char>(ch);
|
||||
if (std::isprint(byte) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::isalnum(byte) != 0) {
|
||||
alpha_numeric_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return alpha_numeric_count > 0;
|
||||
}
|
||||
|
||||
bool QueryGuestMemoryInfo(Kernel::KProcess* process, u64 address,
|
||||
Kernel::Svc::MemoryInfo* out_info) {
|
||||
if (address == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Kernel::KMemoryInfo mem_info{};
|
||||
Kernel::Svc::PageInfo page_info{};
|
||||
if (process->GetPageTable().QueryInfo(&mem_info, &page_info, address).IsFailure()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_info = mem_info.GetSvcMemoryInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LogGuestStringCandidate(Kernel::KProcess* process, u64 address, std::string_view label,
|
||||
std::vector<u64>& logged_strings) {
|
||||
if (logged_strings.size() >= MaxGuestStringLogs) {
|
||||
return;
|
||||
}
|
||||
if (std::find(logged_strings.begin(), logged_strings.end(), address) != logged_strings.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Kernel::Svc::MemoryInfo mem_info{};
|
||||
if (!QueryGuestMemoryInfo(process, address, &mem_info)) {
|
||||
return;
|
||||
}
|
||||
if (mem_info.state == Kernel::Svc::MemoryState::Free ||
|
||||
mem_info.permission == Kernel::Svc::MemoryPermission::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (address < mem_info.base_address) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 region_offset = address - mem_info.base_address;
|
||||
if (region_offset >= mem_info.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 available_bytes = mem_info.size - region_offset;
|
||||
const auto probe_size =
|
||||
static_cast<std::size_t>(std::min<u64>(GuestStringProbeBytes, available_bytes));
|
||||
if (probe_size < MinGuestStringLength ||
|
||||
!process->GetMemory().IsValidVirtualAddressRange(address, probe_size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto text = process->GetMemory().ReadCString(address, probe_size);
|
||||
if (!IsUsefulGuestString(text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logged_strings.push_back(address);
|
||||
LOG_ERROR(Core_ARM, "Guest backtrace string {:02}: {}={:016X} \"{}\"",
|
||||
logged_strings.size() - 1, label, address, SanitizeGuestString(text));
|
||||
}
|
||||
|
||||
void LogStackStringCandidates(Kernel::KProcess* process, u64 base, std::string_view label,
|
||||
std::vector<u64>& logged_strings) {
|
||||
if (base == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = process->GetMemory();
|
||||
for (std::size_t i = 0; i < StackProbeWords; i++) {
|
||||
const u64 address = base + i * sizeof(u64);
|
||||
if (!memory.IsValidVirtualAddressRange(address, sizeof(u64))) {
|
||||
break;
|
||||
}
|
||||
|
||||
u64 value{};
|
||||
if (!memory.ReadBlock(address, &value, sizeof(value))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LogGuestStringCandidate(process, value, fmt::format("{}+{:03X}", label, i * sizeof(u64)),
|
||||
logged_strings);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
|
||||
Kernel::Svc::ThreadContext ctx;
|
||||
this->GetContext(ctx);
|
||||
@@ -170,45 +26,21 @@ void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
|
||||
ctx.r[28], ctx.fp, ctx.lr, ctx.sp,
|
||||
};
|
||||
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Guest backtrace context: process=\"{}\" pid={} program_id={:016X} pc={:016X} "
|
||||
"lr={:016X} fp={:016X} sp={:016X} pstate={:08X}",
|
||||
process->GetName(), process->GetProcessId(), process->GetProgramId(), ctx.pc,
|
||||
ctx.lr, ctx.fp, ctx.sp, ctx.pstate);
|
||||
|
||||
std::vector<u64> logged_strings;
|
||||
for (size_t i = 0; i < xreg.size(); i++) {
|
||||
LogGuestStringCandidate(process, xreg[i], fmt::format("R{:02}", i), logged_strings);
|
||||
}
|
||||
LogStackStringCandidates(process, ctx.sp, "SP", logged_strings);
|
||||
if (ctx.fp != ctx.sp) {
|
||||
LogStackStringCandidates(process, ctx.fp, "FP", logged_strings);
|
||||
}
|
||||
if (logged_strings.empty()) {
|
||||
LOG_ERROR(Core_ARM, "Guest backtrace strings: none found in registers or stack");
|
||||
}
|
||||
|
||||
std::string msg = fmt::format("Backtrace @ PC={:016X}\n", ctx.pc);
|
||||
for (size_t i = 0; i < 32; i += 4)
|
||||
msg += fmt::format("R{:02}={:016X} R{:02}={:016X} R{:02}={:016X} R{:02}={:016X}\n",
|
||||
i + 0, xreg[i + 0], i + 1, xreg[i + 1],
|
||||
i + 2, xreg[i + 2], i + 3, xreg[i + 3]);
|
||||
for (size_t i = 0; i < 32; i += 2)
|
||||
msg += fmt::format("V{:02}={:016X}_{:016X} V{:02}={:016X}_{:016X}\n",
|
||||
i + 0, ctx.v[i + 0][0], ctx.v[i + 0][1],
|
||||
i + 1, ctx.v[i + 1][0], ctx.v[i + 1][1]);
|
||||
msg += fmt::format("PSTATE={:08X} FPCR={:08X} FPSR={:08X} TPIDR={:016X}\n", ctx.pstate, ctx.fpcr, ctx.fpsr, ctx.tpidr);
|
||||
msg += fmt::format("{:20}{:20}{:20}{:20}{}\n", "Module", "Address", "Original Address", "Offset", "Symbol");
|
||||
auto const backtrace = GetBacktraceFromContext(process, ctx);
|
||||
for (size_t i = 0; i < std::min(backtrace.size(), MaxBacktraceFrames); i++) {
|
||||
const auto& entry = backtrace[i];
|
||||
if (entry.original_address == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.name.empty()) {
|
||||
LOG_ERROR(Core_ARM, "Guest backtrace frame {:03}: {}+0x{:X} pc={:016X} mapped={:016X}",
|
||||
i, entry.module, entry.offset, entry.original_address, entry.address);
|
||||
} else {
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Guest backtrace frame {:03}: {}+0x{:X} pc={:016X} mapped={:016X} symbol={}",
|
||||
i, entry.module, entry.offset, entry.original_address, entry.address,
|
||||
entry.name);
|
||||
}
|
||||
}
|
||||
if (backtrace.size() > MaxBacktraceFrames) {
|
||||
LOG_ERROR(Core_ARM, "Guest backtrace truncated: logged={} total={}", MaxBacktraceFrames,
|
||||
backtrace.size());
|
||||
}
|
||||
for (auto const& entry : backtrace)
|
||||
msg += fmt::format("{:20}{:016X} {:016X} {:016X} {}\n", entry.module, entry.address, entry.original_address, entry.offset, entry.name);
|
||||
LOG_ERROR(Core_ARM, "{}", msg);
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ArmInterface::MatchingWatchpoint(
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "common/settings.h"
|
||||
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -33,15 +30,6 @@ Result IStorage::Read(
|
||||
|
||||
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
|
||||
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
|
||||
static thread_local std::chrono::steady_clock::time_point last_read_tick{};
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto period = Settings::values.debug_knobs.GetValue();
|
||||
const auto ReadInterval = std::chrono::microseconds{period};
|
||||
if (last_read_tick != std::chrono::steady_clock::time_point{} &&
|
||||
now - last_read_tick < ReadInterval) {
|
||||
std::this_thread::sleep_for(ReadInterval - (now - last_read_tick));
|
||||
}
|
||||
last_read_tick = std::chrono::steady_clock::now();
|
||||
|
||||
// Read the data from the Storage backend
|
||||
backend->Read(out_bytes.data(), length, offset);
|
||||
|
||||
Reference in New Issue
Block a user