add uart logging feature
This commit is contained in:
@@ -730,6 +730,16 @@ namespace ams::ldr {
|
||||
|
||||
/* Apply PCV and PTM patches */
|
||||
if (g_is_pcv) {
|
||||
/* Hand the pcv patcher the zero-padding after .text (memset to 0 above, mapped
|
||||
ReadExecute below) as a code cave for out-of-line trampolines. text_dst_offset
|
||||
is validated == 0. Clamp to the page-aligned .text mapping so the cave can only
|
||||
live inside the RX region. */
|
||||
const size_t pcv_text_end = static_cast<size_t>(nso_header->text_size);
|
||||
const size_t pcv_rx_end = util::AlignUp(pcv_text_end, os::MemoryPageSize);
|
||||
const size_t pcv_ro_start = static_cast<size_t>(nso_header->ro_dst_offset);
|
||||
const size_t pcv_cave_end = (pcv_rx_end < pcv_ro_start) ? pcv_rx_end : pcv_ro_start;
|
||||
hoc::pcv::g_pcv_cave = map_address + pcv_text_end;
|
||||
hoc::pcv::g_pcv_cave_size = (pcv_cave_end > pcv_text_end) ? (pcv_cave_end - pcv_text_end) : 0;
|
||||
hoc::pcv::Patch(map_address, nso_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,21 @@
|
||||
|
||||
#include <stratosphere.hpp>
|
||||
#include <vapours/results/results_common.hpp>
|
||||
#define LOGGING(fmt, ...) ((void)0)
|
||||
#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
|
||||
|
||||
#define HOC_UART_LOG 1
|
||||
#define HOC_PCV_NVLOG_PATCH 1
|
||||
#define HOC_PCV_FORCE_VERBOSITY 1
|
||||
|
||||
#include "customize.hpp"
|
||||
#include "oc_log.hpp"
|
||||
|
||||
#if HOC_UART_LOG
|
||||
#define LOGGING(fmt, ...) ::ams::ldr::hoc::UartLog(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOGGING(fmt, ...) ((void)0)
|
||||
#endif
|
||||
#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
|
||||
|
||||
#define PATCH_OFFSET(offset, value) \
|
||||
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
|
||||
*(offset) = value;
|
||||
|
||||
@@ -70,6 +70,29 @@ namespace ams::ldr::hoc {
|
||||
return rc;
|
||||
}
|
||||
|
||||
void UartLog(const char *fmt, ...) {
|
||||
char line[256];
|
||||
constexpr size_t PrefixLen = 13; /* "[HOC] " */
|
||||
std::memcpy(line, "[Horizon OC] ", PrefixLen);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int n = vsnprintf(line + PrefixLen, sizeof(line) - PrefixLen - 1, fmt, args);
|
||||
va_end(args);
|
||||
if (n < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t body = static_cast<size_t>(n);
|
||||
if (body > sizeof(line) - PrefixLen - 2) { /* vsnprintf returns the untruncated length */
|
||||
body = sizeof(line) - PrefixLen - 2;
|
||||
}
|
||||
size_t len = PrefixLen + body;
|
||||
line[len++] = '\n';
|
||||
|
||||
svc::OutputDebugString(line, len);
|
||||
}
|
||||
|
||||
struct log_ctx_t {
|
||||
u32 magic;
|
||||
u32 sz;
|
||||
|
||||
@@ -25,4 +25,8 @@ namespace ams::ldr::hoc {
|
||||
void Log(const char *data, ...);
|
||||
void ViewLog();
|
||||
|
||||
/* Emit a formatted line over the kernel debug UART via svcOutputDebugString.
|
||||
No-op unless the running kernel enables debug logging (audit/debug mesosphere). */
|
||||
void UartLog(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
}
|
||||
|
||||
@@ -159,6 +159,82 @@ namespace ams::ldr::hoc::pcv {
|
||||
return 0xF9400000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* b <target> (PC-relative, +-128MB). */
|
||||
inline auto AsmMakeB = [](uintptr_t pc, uintptr_t target) -> u32 {
|
||||
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||
return 0x14000000u | (static_cast<u32>(off) & 0x03FFFFFFu);
|
||||
};
|
||||
|
||||
/* b.<cond> <target> (cond: LO/CC=0x3, LE=0xD, NE=0x1, ...). */
|
||||
inline auto AsmMakeBCond = [](uintptr_t pc, uintptr_t target, u32 cond) -> u32 {
|
||||
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||
return 0x54000000u | ((static_cast<u32>(off) & 0x7FFFFu) << 5) | (cond & 0xFu);
|
||||
};
|
||||
|
||||
/* sub Xd,Xn,#imm12 (shift 0). */
|
||||
inline auto AsmMakeSubImm64 = [](u32 rd, u32 rn, u32 imm12) -> u32 {
|
||||
return 0xD1000000u | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu);
|
||||
};
|
||||
|
||||
/* cmp Wn,#imm12 == subs WZR,Wn,#imm12. */
|
||||
inline auto AsmMakeCmpImm32 = [](u32 rn, u32 imm12) -> u32 {
|
||||
return 0x7100001Fu | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5);
|
||||
};
|
||||
|
||||
/* str Wt,[Xn,#byteOff] (32-bit, unsigned scaled by 4). */
|
||||
inline auto AsmMakeStrImm32 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||
return 0xB9000000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* stp Xt1,Xt2,[Xn,#imm] (signed offset, scaled by 8). */
|
||||
inline auto AsmMakeStpImm64 = [](u32 rt1, u32 rt2, u32 rn, s32 imm) -> u32 {
|
||||
return 0xA9000000u | ((static_cast<u32>(imm / 8) & 0x7Fu) << 15) | ((rt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (rt1 & 0x1Fu);
|
||||
};
|
||||
|
||||
/* stp Qt1,Qt2,[Xn,#imm] (128-bit SIMD, signed offset scaled by 16). */
|
||||
inline auto AsmMakeStpqImm = [](u32 qt1, u32 qt2, u32 rn, s32 imm) -> u32 {
|
||||
return 0xAD000000u | ((static_cast<u32>(imm / 16) & 0x7Fu) << 15) | ((qt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (qt1 & 0x1Fu);
|
||||
};
|
||||
|
||||
/* str Xt,[Xn,#byteOff] (unsigned scaled by 8). */
|
||||
inline auto AsmMakeStrImm64 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||
return 0xF9000000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* movn Wd,#imm16 (no shift): loads ~imm16, e.g. movn Wd,#0x37 == -56. */
|
||||
inline auto AsmMakeMovnW = [](u32 rd, u16 imm16) -> u32 {
|
||||
return 0x12800000u | (static_cast<u32>(imm16) << 5) | (rd & 0x1Fu);
|
||||
};
|
||||
|
||||
/* bl <target> (PC-relative, +-128MB). */
|
||||
inline auto AsmMakeBl = [](uintptr_t pc, uintptr_t target) -> u32 {
|
||||
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||
return 0x94000000u | (static_cast<u32>(off) & 0x03FFFFFFu);
|
||||
};
|
||||
|
||||
/* svc #imm16. */
|
||||
inline auto AsmMakeSvc = [](u16 imm16) -> u32 {
|
||||
return 0xD4000001u | (static_cast<u32>(imm16) << 5);
|
||||
};
|
||||
|
||||
constexpr u32 RetIns = 0xD65F03C0u; /* ret (x30) */
|
||||
|
||||
/* ldrb Wt,[Xn,#imm] (unsigned byte, scale 1). */
|
||||
inline auto AsmMakeLdrbImm = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||
return 0x39400000u | ((byteOff & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* cbz Wt,<target>. */
|
||||
inline auto AsmMakeCbz = [](uintptr_t pc, uintptr_t target, u32 rt) -> u32 {
|
||||
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||
return 0x34000000u | ((static_cast<u32>(off) & 0x7FFFFu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* strb Wt,[Xn,#imm] (unsigned byte, scale 1). */
|
||||
inline auto AsmMakeStrbImm = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||
return 0x39000000u | ((byteOff & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* mov Xd,X<rm> == orr Xd,XZR,X<rm> (matches any Rd). */
|
||||
inline auto AsmIsMovReg = [](u32 ins, u32 rm) -> bool {
|
||||
return (ins & 0xFFFFFFE0u) == (0xAA0003E0u | ((rm & 0x1Fu) << 16));
|
||||
|
||||
@@ -183,6 +183,9 @@ namespace ams::ldr::hoc::pcv {
|
||||
// extra .bss location, 0 until Patch() runs.
|
||||
inline uintptr_t g_pcv_scratch = 0;
|
||||
|
||||
inline uintptr_t g_pcv_cave = 0;
|
||||
inline size_t g_pcv_cave_size = 0;
|
||||
|
||||
template<typename T>
|
||||
size_t GetDvfsTableEntryCount(T *table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
|
||||
@@ -620,6 +620,186 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
namespace {
|
||||
std::vector<u32> newEmcList;
|
||||
u32 *nsoStart;
|
||||
uintptr_t g_cave_cursor = 0;
|
||||
}
|
||||
|
||||
static uintptr_t CaveReserve(size_t count) {
|
||||
if (g_pcv_cave == 0 || g_cave_cursor == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (g_cave_cursor + count * sizeof(u32) > g_pcv_cave + g_pcv_cave_size) {
|
||||
return 0;
|
||||
}
|
||||
const uintptr_t entry = g_cave_cursor;
|
||||
g_cave_cursor += count * sizeof(u32);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void NvLogUartRedirect(uintptr_t mapped_nso, size_t nso_size) {
|
||||
const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */
|
||||
if (textEnd == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* vsnprintf(buf,size,fmt,va_list) */
|
||||
static const u32 VsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu };
|
||||
uintptr_t vsnprintf_addr = 0;
|
||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + std::size(VsnSig)) <= textEnd; ++p) {
|
||||
bool ok = true;
|
||||
for (size_t k = 0; k < std::size(VsnSig); ++k) {
|
||||
if (p[k] != VsnSig[k]) { ok = false; break; }
|
||||
}
|
||||
if (ok) { vsnprintf_addr = reinterpret_cast<uintptr_t>(p); break; }
|
||||
}
|
||||
if (vsnprintf_addr == 0) {
|
||||
LOGGING("NvLogRedirect: vsnprintf not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* NvLog via the VDD_SOC log */
|
||||
static const char Fmt[] = "%s(%s): DVFS request VDD_SOC %d mV\n";
|
||||
constexpr size_t FmtLen = sizeof(Fmt) - 1;
|
||||
uintptr_t strAddr = 0;
|
||||
{
|
||||
const char *hay = reinterpret_cast<const char *>(mapped_nso);
|
||||
for (size_t i = 0; i + FmtLen <= nso_size; ++i) {
|
||||
if (std::memcmp(hay + i, Fmt, FmtLen) == 0) { strAddr = mapped_nso + i; break; }
|
||||
}
|
||||
}
|
||||
if (strAddr == 0) {
|
||||
LOGGING("NvLogRedirect: fmt string not found (vsnprintf@+%lx)", vsnprintf_addr - mapped_nso);
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t nvlog_addr = 0;
|
||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 2) <= textEnd; ++p) {
|
||||
const uintptr_t pc = reinterpret_cast<uintptr_t>(p);
|
||||
if (!AsmIsAdrp(p[0])) {
|
||||
continue;
|
||||
}
|
||||
const uintptr_t adrpPage = (pc & ~static_cast<uintptr_t>(0xFFFu)) + static_cast<uintptr_t>(AsmAdrpPageOffset(p[0]));
|
||||
if (adrpPage != (strAddr & ~static_cast<uintptr_t>(0xFFFu))) {
|
||||
continue;
|
||||
}
|
||||
const u32 reg = asm_get_rd(p[0]);
|
||||
if (!(AsmIsAddImm64(p[1]) && asm_get_rd(p[1]) == reg && AsmGetRn(p[1]) == reg && AsmGetImm12(p[1]) == (strAddr & 0xFFFu))) {
|
||||
continue;
|
||||
}
|
||||
for (u32 k = 2; k <= 12 && (pc + (k + 1) * 4) <= textEnd; ++k) {
|
||||
if (AsmIsBl(p[k])) { nvlog_addr = AsmBranchTarget(p[k], pc + k * 4); break; }
|
||||
}
|
||||
if (nvlog_addr != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nvlog_addr == 0 || nvlog_addr < mapped_nso || nvlog_addr >= textEnd) {
|
||||
LOGGING("NvLogRedirect: NvLog entry not found (fmt@+%lx)", strAddr - mapped_nso);
|
||||
return;
|
||||
}
|
||||
|
||||
const uintptr_t helper = CaveReserve(40);
|
||||
if (helper == 0) {
|
||||
LOGGING("NvLogRedirect: cave unavailable (cave=%lx size=%lx)",
|
||||
static_cast<unsigned long>(g_pcv_cave), static_cast<unsigned long>(g_pcv_cave_size));
|
||||
return;
|
||||
}
|
||||
u32 *t = reinterpret_cast<u32 *>(helper);
|
||||
size_t n = 0;
|
||||
auto emit = [&](u32 ins) { t[n] = ins; ++n; };
|
||||
|
||||
emit(AsmMakeSubImm64(31, 31, 0x200));
|
||||
emit(AsmMakeStpImm64(0, 1, 31, 0x100));
|
||||
emit(AsmMakeStpImm64(2, 3, 31, 0x110));
|
||||
emit(AsmMakeStpImm64(4, 5, 31, 0x120));
|
||||
emit(AsmMakeStpImm64(6, 7, 31, 0x130));
|
||||
emit(AsmMakeStpqImm(0, 1, 31, 0x140));
|
||||
emit(AsmMakeStpqImm(2, 3, 31, 0x160));
|
||||
emit(AsmMakeStpqImm(4, 5, 31, 0x180));
|
||||
emit(AsmMakeStpqImm(6, 7, 31, 0x1A0));
|
||||
emit(AsmMakeStrImm64(30, 31, 0x1E0));
|
||||
emit(AsmMakeAddImm64(9, 31, 0x200)); emit(AsmMakeStrImm64(9, 31, 0x1C0)); /* __stack */
|
||||
emit(AsmMakeAddImm64(9, 31, 0x140)); emit(AsmMakeStrImm64(9, 31, 0x1C8)); /* __gr_top */
|
||||
emit(AsmMakeAddImm64(9, 31, 0x1C0)); emit(AsmMakeStrImm64(9, 31, 0x1D0)); /* __vr_top */
|
||||
emit(AsmMakeMovnW(9, 0x37)); emit(AsmMakeStrImm32(9, 31, 0x1D8)); /* __gr_offs = -56 */
|
||||
emit(AsmMakeMovnW(9, 0x7F)); emit(AsmMakeStrImm32(9, 31, 0x1DC)); /* __vr_offs = -128 */
|
||||
emit(AsmMakeAddImm64(0, 31, 0x00)); /* mov x0,sp (buf) */
|
||||
emit(AsmMakeMovzW(1, 0x100)); /* size = 0x100 */
|
||||
emit(AsmMakeLdrImm64(2, 31, 0x100)); /* fmt (saved x0) */
|
||||
emit(AsmMakeAddImm64(3, 31, 0x1C0)); /* ap */
|
||||
emit(AsmMakeBl(helper + n * 4, vsnprintf_addr));
|
||||
emit(AsmMakeMovReg(1, 0)); /* len = retval */
|
||||
emit(AsmMakeCmpImm32(1, 0x100));
|
||||
{ const size_t at = n; emit(AsmMakeBCond(helper + at * 4, helper + (at + 2) * 4, 0x3u)); } /* b.lo +2 */
|
||||
emit(AsmMakeMovzW(1, 0xFF)); /* clamp len */
|
||||
emit(AsmMakeAddImm64(0, 31, 0x00)); /* mov x0,sp (str) */
|
||||
emit(AsmMakeSvc(0x27)); /* svcOutputDebugString */
|
||||
emit(AsmMakeLdrImm64(30, 31, 0x1E0));
|
||||
emit(AsmMakeAddImm64(31, 31, 0x200));
|
||||
emit(RetIns);
|
||||
|
||||
/* Redirect the call sites as patching the actual function causes crash */
|
||||
const uintptr_t roStart = g_pcv_cave + g_pcv_cave_size; /* module .rodata start */
|
||||
size_t patchedSites = 0;
|
||||
if (HOC_PCV_NVLOG_PATCH) {
|
||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 1) <= textEnd; ++p) {
|
||||
if (!AsmIsBl(*p)) {
|
||||
continue;
|
||||
}
|
||||
const uintptr_t pc = reinterpret_cast<uintptr_t>(p);
|
||||
if (AsmBranchTarget(*p, pc) != nvlog_addr) {
|
||||
continue;
|
||||
}
|
||||
bool isFmtCall = false;
|
||||
for (u32 j = 1; j <= 8 && reinterpret_cast<uintptr_t>(p - j) >= reinterpret_cast<uintptr_t>(nsoStart); ++j) {
|
||||
const u32 w = *(p - j);
|
||||
if (AsmIsAdrp(w) && asm_get_rd(w) == 0) { /* adrp x0,<page> */
|
||||
const uintptr_t wpc = pc - j * 4;
|
||||
const uintptr_t tgtPage = (wpc & ~static_cast<uintptr_t>(0xFFFu)) + static_cast<uintptr_t>(AsmAdrpPageOffset(w));
|
||||
if (tgtPage >= (roStart & ~static_cast<uintptr_t>(0xFFFu))) { isFmtCall = true; break; }
|
||||
}
|
||||
}
|
||||
if (isFmtCall) {
|
||||
PATCH_OFFSET(p, AsmMakeBl(pc, helper));
|
||||
++patchedSites;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOGGING("NvLogRedirect: stub@+%lx vsnprintf@+%lx helper@+%lx instr=%zu sites=%zu",
|
||||
nvlog_addr - mapped_nso, vsnprintf_addr - mapped_nso, helper - mapped_nso, n, patchedSites);
|
||||
}
|
||||
|
||||
/* Force GetEffectiveVerbosityLevel to return a non-zero level so all NvLog runs */
|
||||
static void ForceVerbosity() {
|
||||
const uintptr_t textEnd = g_pcv_cave;
|
||||
if (textEnd == 0 || HOC_PCV_FORCE_VERBOSITY == 0) {
|
||||
return;
|
||||
}
|
||||
size_t patched = 0;
|
||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 11) <= textEnd; ++p) {
|
||||
if (p[0] != 0xA9BE7BFDu || p[1] != 0xF9000BF3u || p[2] != 0x910003FDu) { /* stp/str/mov x29,sp */
|
||||
continue;
|
||||
}
|
||||
if (!(AsmIsAddImm64(p[3]) && asm_get_rd(p[3]) == 0 && AsmGetRn(p[3]) == 29)) continue; /* add x0,x29,#imm */
|
||||
if (!(AsmIsAddImm64(p[4]) && asm_get_rd(p[4]) == 19 && AsmGetRn(p[4]) == 29)) continue; /* add x19,x29,#imm */
|
||||
if (AsmGetImm12(p[3]) != AsmGetImm12(p[4]) || !AsmIsBl(p[5])) {
|
||||
continue;
|
||||
}
|
||||
bool hasCmp = false;
|
||||
for (u32 j = 6; j <= 10; ++j) {
|
||||
if (p[j] == 0x7100001Fu) { /* cmp w0,#0 */
|
||||
hasCmp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasCmp) {
|
||||
continue;
|
||||
}
|
||||
PATCH_OFFSET(&p[0], AsmMakeMovzW(0, static_cast<u16>(HOC_PCV_FORCE_VERBOSITY)));
|
||||
PATCH_OFFSET(&p[1], RetIns);
|
||||
++patched;
|
||||
}
|
||||
LOGGING("ForceVerbosity: patched %zu getter(s) to level %d", patched, HOC_PCV_FORCE_VERBOSITY);
|
||||
}
|
||||
|
||||
/* Widen InitDram for a >32-entry EMC DVFS list. Freq array can be dropped to free 264 bytes, relocate the Soc LUT to that space */
|
||||
@@ -1293,6 +1473,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||
|
||||
g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize;
|
||||
g_cave_cursor = g_pcv_cave; /* start the .text-cave bump allocator (0 if unavailable) */
|
||||
|
||||
NvLogUartRedirect(mapped_nso, nso_size);
|
||||
ForceVerbosity();
|
||||
|
||||
MtcGenerateFreqTables();
|
||||
|
||||
|
||||
@@ -83,6 +83,24 @@ namespace fileUtils {
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
char buff[0xfff];
|
||||
int len = vsnprintf(buff, sizeof(buff), format, args);
|
||||
va_end(args);
|
||||
|
||||
if (len < 0) {
|
||||
return;
|
||||
}
|
||||
// Leave room for the newline + NUL.
|
||||
if ((size_t)len >= sizeof(buff) - 1) {
|
||||
len = sizeof(buff) - 2;
|
||||
}
|
||||
buff[len++] = '\n';
|
||||
buff[len] = '\0';
|
||||
|
||||
// Debug UART log
|
||||
svcOutputDebugString(buff, len);
|
||||
|
||||
if (g_has_initialized) {
|
||||
RefreshFlags(false);
|
||||
|
||||
@@ -93,14 +111,11 @@ namespace fileUtils {
|
||||
timespec now = {};
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "[%luls] ", now.tv_sec - bootTimeS);
|
||||
vfprintf(file, format, args);
|
||||
fprintf(file, "\n");
|
||||
fprintf(file, "[%luls] %s", now.tv_sec - bootTimeS, buff);
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void WriteContextToCsv(const HocClkContext *context) {
|
||||
|
||||
Reference in New Issue
Block a user