abort/error: print backtrace, abuse templates, overhaul result/diag (macos not done yet)

This commit is contained in:
Michael Scire 2022-03-10 01:15:45 -08:00
parent 5c5789eadf
commit 87764613f2
118 changed files with 2843 additions and 369 deletions

View file

@ -18,13 +18,13 @@
namespace ams::diag {
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
AMS_UNUSED(file, line, func, expr, value, format);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
AMS_UNUSED(expr, func, line, file);
ams::secmon::loader::ErrorReboot();
}
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
AMS_UNUSED(file, line, func, expr, value);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
AMS_UNUSED(expr, func, line, file, format);
ams::secmon::loader::ErrorReboot();
}

View file

@ -17,7 +17,7 @@
namespace ams::diag {
void AbortImpl() {
NORETURN void AbortImpl() {
AMS_SECMON_LOG("AbortImpl was called\n");
AMS_LOG_FLUSH();
reg::Write(0x4, 0xAAAAAAAA);

View file

@ -50,7 +50,7 @@ namespace ams::rebootstub {
namespace ams::diag {
void AbortImpl() {
NORETURN void AbortImpl() {
/* Halt the bpmp. */
rebootstub::Halt();

View file

@ -111,7 +111,7 @@ namespace ams::sc7fw {
namespace ams::diag {
void AbortImpl() {
NORETURN void AbortImpl() {
sc7fw::ExceptionHandler();
}

View file

@ -50,7 +50,7 @@ namespace ams::diag {
}
void AbortImpl() {
NORETURN void AbortImpl() {
/* Perform any necessary (typically none) debugging. */
if constexpr (SaveSystemStateForDebug) {
SaveSystemStateForDebugAbort();

View file

@ -98,7 +98,7 @@ namespace ams::warmboot {
namespace ams::diag {
void AbortImpl() {
NORETURN void AbortImpl() {
warmboot::ExceptionHandler();
}

View file

@ -18,13 +18,15 @@
namespace ams::diag {
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
AMS_UNUSED(file, line, func, expr, value, format);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
AMS_UNUSED(expr, func, line, file);
ams::nxboot::loader::ErrorStop();
}
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
AMS_UNUSED(file, line, func, expr, value);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
AMS_UNUSED(expr, func, line, file, format);
ams::nxboot::loader::ErrorStop();
}

View file

@ -27,20 +27,36 @@ namespace ams::nxboot {
namespace ams::diag {
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
AMS_UNUSED(file, line, func, expr, format);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
AMS_UNUSED(expr, func, line, file);
u32 lr;
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
nxboot::ShowFatalError("Abort called, lr=%p, value=%" PRIx64 "\n", reinterpret_cast<void *>(lr), value);
nxboot::ShowFatalError("Abort called, lr=%p\n", reinterpret_cast<void *>(lr));
}
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
AMS_UNUSED(file, line, func, expr);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
AMS_UNUSED(expr, func, line, file, format);
u32 lr;
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
nxboot::ShowFatalError("Abort called, lr=%p, value=%" PRIx64 "\n", reinterpret_cast<void *>(lr), value);
nxboot::ShowFatalError("Abort called, lr=%p\n", reinterpret_cast<void *>(lr));
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *format, ...) {
AMS_UNUSED(expr, func, line, file, result, format);
u32 lr;
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
nxboot::ShowFatalError("Abort called, lr=%p, result=0x%08" PRIX32 "\n", reinterpret_cast<void *>(lr), result != nullptr ? result->GetValue() : 0);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exception_info, const char *format, ...) {
AMS_UNUSED(expr, func, line, file, result, exception_info, format);
u32 lr;
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
nxboot::ShowFatalError("Abort called, lr=%p, result=0x%08" PRIX32 "\n", reinterpret_cast<void *>(lr), result != nullptr ? result->GetValue() : 0);
}
NORETURN void AbortImpl() {
@ -49,16 +65,16 @@ namespace ams::diag {
nxboot::ShowFatalError("Abort called, lr=%p\n", reinterpret_cast<void *>(lr));
}
NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
AMS_UNUSED(file, line, func, expr, value);
NORETURN void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
AMS_UNUSED(type, expr, func, file, line);
u32 lr;
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
nxboot::ShowFatalError("Assert called, lr=%p\n", reinterpret_cast<void *>(lr));
}
NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
AMS_UNUSED(file, line, func, expr, value, format);
NORETURN void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
AMS_UNUSED(type, expr, func, file, line, format);
u32 lr;
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
@ -66,17 +82,3 @@ namespace ams::diag {
}
}
namespace ams::result::impl {
NORETURN void OnResultAbort(const char *file, int line, const char *func, const char *expr, Result result) {
::ams::diag::AbortImpl(file, line, func, expr, result.GetValue(), "Result Abort: 2%03" PRId32 "-%04" PRId32 "", result.GetModule(), result.GetDescription());
AMS_INFINITE_LOOP();
__builtin_unreachable();
}
NORETURN void OnResultAbort(Result result) {
OnResultAbort("", 0, "", "", result);
}
}

View file

@ -1,5 +1,5 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_LINUX
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View file

@ -1,5 +1,5 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_MACOS
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View file

@ -1,5 +1,5 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_WINDOWS
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View file

@ -80,9 +80,9 @@ endif
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
export LIBS := -lstratosphere -lnx
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
export LIBS := -lstratosphere -lwinmm -lws2_32 -lbcrypt
export LIBS := -lstratosphere -lwinmm -lws2_32 -lbcrypt -lbfd -liberty -lintl -lz
else ifeq ($(ATMOSPHERE_BOARD),generic_linux)
export LIBS := -lstratosphere -pthread
export LIBS := -lstratosphere -pthread -lbfd -liberty -ldl
else ifeq ($(ATMOSPHERE_BOARD),generic_macos)
export LIBS := -lstratosphere -pthread
else

View file

@ -14,23 +14,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
void AbortImpl(const char *expr, const char *func, const char *file, int line) {
#if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
{
AMS_LOG("Abort Called\n");
AMS_LOG(" Location: %s:%d\n", file, line);
AMS_LOG(" Function: %s\n", func);
AMS_LOG(" Expression: %s\n", expr);
AMS_LOG(" Value: %016" PRIx64 "\n", value);
AMS_LOG("\n");
}
#else
AMS_UNUSED(file, line, func, expr, value);
AMS_UNUSED(file, line, func, expr);
#endif
AbortImpl();
}
void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
#if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
{
AMS_LOG("Abort Called\n");
@ -48,12 +47,35 @@ void AbortImpl(const char *file, int line, const char *func, const char *expr, u
}
}
#else
AMS_UNUSED(file, line, func, expr, value, format);
AMS_UNUSED(file, line, func, expr, format);
#endif
AbortImpl();
}
void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *format, ...) {
#if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
{
AMS_LOG("Abort Called\n");
AMS_LOG(" Location: %s:%d\n", file, line);
AMS_LOG(" Function: %s\n", func);
AMS_LOG(" Expression: %s\n", expr);
AMS_LOG(" Result: 0x%08" PRIx32 "\n", result->GetValue());
AMS_LOG("\n");
{
::std::va_list vl;
va_start(vl, format);
AMS_VLOG(format, vl);
va_end(vl);
AMS_LOG("\n");
}
}
#else
AMS_UNUSED(file, line, func, expr, result, format);
#endif
AbortImpl();
}
void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
#if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
{
AMS_LOG("Assertion Failure\n");
@ -62,14 +84,16 @@ void AssertionFailureImpl(const char *file, int line, const char *func, const ch
AMS_LOG(" Expression: %s\n", expr);
AMS_LOG(" Value: %016" PRIx64 "\n", value);
AMS_LOG("\n");
AMS_UNUSED(type);
}
#else
AMS_UNUSED(file, line, func, expr, value);
AMS_UNUSED(type, expr, func, file, line);
#endif
AbortImpl();
}
void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
#if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
{
AMS_LOG("Assertion Failure\n");
@ -85,9 +109,11 @@ void AssertionFailureImpl(const char *file, int line, const char *func, const ch
va_end(vl);
AMS_LOG("\n");
}
AMS_UNUSED(type);
}
#else
AMS_UNUSED(file, line, func, expr, value, format);
AMS_UNUSED(type, expr, func, file, line, format);
#endif
AbortImpl();
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
namespace ams::impl {
NORETURN void UnexpectedDefaultImpl(const char *func, const char *file, int line) {
::ams::diag::AbortImpl("", func, file, line);
}
}

View file

@ -26,12 +26,12 @@ namespace ams::kern {
namespace ams::diag {
NORETURN ALWAYS_INLINE void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
NORETURN ALWAYS_INLINE void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
#if defined(MESOSPHERE_ENABLE_DEBUG_PRINT)
::ams::kern::Panic(file, line, "ams::diag::AssertionFailureImpl: %s:%s 0x%016" PRIx64 "", func, expr, value);
::ams::kern::Panic(file, line, "ams::diag::OnAssertionFailure: %d %s:%s", (type == AssertionType_Audit), func, expr);
#else
::ams::kern::Panic();
AMS_UNUSED(file, line, func, expr, value);
AMS_UNUSED(type, expr, func, file, line);
#endif
}

View file

@ -20,6 +20,10 @@
#include <stratosphere/diag/impl/diag_impl_log.hpp>
#include <stratosphere/diag/diag_log.hpp>
#include <stratosphere/diag/diag_sdk_log.hpp>
#include <stratosphere/diag/diag_abort_observer.hpp>
#include <stratosphere/diag/diag_assertion_failure_handler.hpp>
#include <stratosphere/diag/impl/diag_utf8_util.hpp>
#include <stratosphere/diag/diag_backtrace.hpp>
#include <stratosphere/diag/diag_symbol.hpp>

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/diag_log_types.hpp>
namespace ams::diag {
using AbortObserver = void (*)(const AbortInfo &);
struct AbortObserverHolder {
AbortObserver observer;
AbortObserverHolder *next;
bool is_registered;
};
void InitializeAbortObserverHolder(AbortObserverHolder *holder, AbortObserver observer);
void RegisterAbortObserver(AbortObserverHolder *holder);
void UnregisterAbortObserver(AbortObserverHolder *holder);
void EnableDefaultAbortObserver(bool en);
struct SdkAbortInfo {
AbortInfo abort_info;
Result result;
const ::ams::os::UserExceptionInfo *exc_info;
};
using SdkAbortObserver = void (*)(const SdkAbortInfo &);
struct SdkAbortObserverHolder {
SdkAbortObserver observer;
SdkAbortObserverHolder *next;
bool is_registered;
};
void InitializeSdkAbortObserverHolder(SdkAbortObserverHolder *holder, SdkAbortObserver observer);
void RegisterSdkAbortObserver(SdkAbortObserverHolder *holder);
void UnregisterSdkAbortObserver(SdkAbortObserverHolder *holder);
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/diag_log_types.hpp>
namespace ams::diag {
enum AssertionFailureOperation {
AssertionFailureOperation_Abort,
AssertionFailureOperation_Continue,
};
using AssertionFailureHandler = AssertionFailureOperation (*)(const AssertionInfo &info);
void SetAssertionFailureHandler(AssertionFailureHandler handler);
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include <stratosphere/diag/impl/diag_backtrace_impl.os.horizon.hpp>
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include <stratosphere/diag/impl/diag_backtrace_impl.os.windows.hpp>
#elif defined(ATMOSPHERE_OS_LINUX)
#include <stratosphere/diag/impl/diag_backtrace_impl.os.linux.hpp>
#elif defined(ATMOSPHERE_OS_MACOS)
#include <stratosphere/diag/impl/diag_backtrace_impl.os.macos.hpp>
#else
#error "Unknown OS for diag::Backtrace"
#endif
namespace ams::diag {
size_t GetBacktrace(uintptr_t *out, size_t out_size);
#if defined(ATMOSPHERE_OS_HORIZON)
size_t GetBacktace(uintptr_t *out, size_t out_size, uintptr_t fp, uintptr_t sp, uintptr_t pc);
#endif
class Backtrace {
private:
impl::Backtrace m_impl;
public:
NOINLINE Backtrace() {
m_impl.Initialize();
m_impl.Step();
}
#if defined(ATMOSPHERE_OS_HORIZON)
Backtrace(uintptr_t fp, uintptr_t sp, uintptr_t pc) {
m_impl.Initialize(fp, sp, pc);
}
#endif
bool Step() { return m_impl.Step(); }
uintptr_t GetStackPointer() const { return m_impl.GetStackPointer(); }
uintptr_t GetReturnAddress() const { return m_impl.GetReturnAddress(); }
};
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag {
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address);
size_t GetSymbolSize(uintptr_t address);
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag::impl {
class Backtrace {
private:
static constexpr size_t MemoryInfoBufferSize = 5;
public:
struct StackInfo {
uintptr_t stack_top;
uintptr_t stack_bottom;
};
private:
s64 m_memory_info_buffer[MemoryInfoBufferSize]{};
StackInfo *m_current_stack_info = nullptr;
StackInfo m_exception_stack_info{};
StackInfo m_normal_stack_info{};
uintptr_t m_fp = 0;
uintptr_t m_prev_fp = 0;
uintptr_t m_lr = 0;
public:
Backtrace() = default;
void Initialize();
void Initialize(uintptr_t fp, uintptr_t sp, uintptr_t pc);
bool Step();
uintptr_t GetStackPointer() const;
uintptr_t GetReturnAddress() const;
private:
void SetStackInfo(uintptr_t fp, uintptr_t sp);
};
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag::impl {
class Backtrace {
private:
static constexpr size_t BacktraceEntryCountMax = 0x80;
private:
void *m_backtrace_addresses[BacktraceEntryCountMax];
size_t m_index = 0;
size_t m_size = 0;
public:
Backtrace() = default;
void Initialize();
bool Step();
uintptr_t GetStackPointer() const;
uintptr_t GetReturnAddress() const;
};
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag::impl {
class Backtrace {
private:
static constexpr size_t BacktraceEntryCountMax = 0x80;
private:
void *m_backtrace_addresses[BacktraceEntryCountMax];
size_t m_index = 0;
size_t m_size = 0;
public:
Backtrace() = default;
void Initialize();
bool Step();
uintptr_t GetStackPointer() const;
uintptr_t GetReturnAddress() const;
};
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag::impl {
class Backtrace {
private:
static constexpr size_t BacktraceEntryCountMax = 0x80;
private:
void *m_backtrace_addresses[BacktraceEntryCountMax];
size_t m_index = 0;
size_t m_size = 0;
public:
Backtrace() = default;
void Initialize();
bool Step();
uintptr_t GetStackPointer() const;
uintptr_t GetReturnAddress() const;
};
}

View file

@ -46,6 +46,7 @@
#include <stratosphere/os/os_sdk_reply_and_receive.hpp>
#include <stratosphere/os/os_thread.hpp>
#include <stratosphere/os/os_sdk_thread_api.hpp>
#include <stratosphere/os/os_sdk_thread_info.hpp>
#include <stratosphere/os/os_message_queue.hpp>
#include <stratosphere/os/os_light_event.hpp>
#include <stratosphere/os/os_light_message_queue.hpp>
@ -55,3 +56,4 @@
#include <stratosphere/os/os_multiple_wait.hpp>
#include <stratosphere/os/os_argument.hpp>
#include <stratosphere/os/os_cache.hpp>
#include <stratosphere/os/os_debug.hpp>

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_debug_types.hpp>
#include <stratosphere/os/os_debug_api.hpp>

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_debug_types.hpp>
#include <stratosphere/os/os_tick.hpp>
namespace ams::os {
void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size);
void QueryMemoryInfo(MemoryInfo *out);
Tick GetIdleTickCount();
int GetFreeThreadCount();
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
struct MemoryInfo {
u64 total_available_memory_size;
size_t total_used_memory_size;
size_t total_memory_heap_size;
size_t allocated_memory_heap_size;
size_t program_size;
size_t total_thread_stack_size;
int thread_count;
};
}

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_sdk_thread_info_types.hpp>
#include <stratosphere/os/os_sdk_thread_info_api.hpp>

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_sdk_thread_info_types.hpp>
#include <stratosphere/os/os_thread_types.hpp>
namespace ams::os {
void GetThreadStackInfo(uintptr_t *out_stack_top, size_t *out_stack_size, const ThreadType *thread);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_common_types.hpp>
namespace ams::os {
enum SdkLastThreadInfoFlag : u32 {
SdkLastThreadInfoFlag_ThreadInSystemCall = (1u << 0),
};
}

View file

@ -132,9 +132,9 @@ DEPENDS := $(OFILES:.o=.d) $(foreach hdr,$(GCH_FILES),$(notdir $(patsubst %.hpp.
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(OUTPUT) : result_get_name.o $(filter-out result_get_name.o, $(OFILES))
$(OFILES) : $(GCH_FILES)
$(filter-out result_get_name.o, $(OFILES)) : $(GCH_FILES)
$(OFILES_SRC) : $(HFILES_BIN)
@ -146,12 +146,16 @@ hos_stratosphere_api.o: CXXFLAGS += -fno-lto
init_operator_new.o: CXXFLAGS += -fno-lto
init_libnx_shim.os.horizon.o: CXXFLAGS += -fno-lto
result_get_name.o: CXXFLAGS += -fno-lto
spl_secure_monitor_api.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
fs_id_string_impl.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
ifeq ($(ATMOSPHERE_OS_NAME),windows)
os_%.o: CXXFLAGS += -fno-lto
fssystem_%.o: CXXFLAGS += -fno-lto
fssrv_%.o: CXXFLAGS += -fno-lto
fs_%.o: CXXFLAGS += -fno-lto
endif
#---------------------------------------------------------------------------------

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/diag_abort_observer_manager.hpp"
namespace ams::diag {
namespace impl {
constinit bool g_enable_default_abort_observer = true;
}
namespace {
template<typename Holder, typename Observer>
void InitializeAbortObserverHolderImpl(Holder *holder, Observer observer) {
holder->observer = observer;
holder->next = nullptr;
holder->is_registered = false;
}
}
void InitializeAbortObserverHolder(AbortObserverHolder *holder, AbortObserver observer) {
InitializeAbortObserverHolderImpl(holder, observer);
}
void RegisterAbortObserver(AbortObserverHolder *holder) {
impl::GetAbortObserverManager()->RegisterObserver(holder);
}
void UnregisterAbortObserver(AbortObserverHolder *holder) {
impl::GetAbortObserverManager()->UnregisterObserver(holder);
}
void EnableDefaultAbortObserver(bool en) {
::ams::diag::impl::g_enable_default_abort_observer = en;
}
void InitializeSdkAbortObserverHolder(SdkAbortObserverHolder *holder, SdkAbortObserver observer) {
InitializeAbortObserverHolderImpl(holder, observer);
}
void RegisterSdkAbortObserver(SdkAbortObserverHolder *holder) {
impl::GetSdkAbortObserverManager()->RegisterObserver(holder);
}
void UnregisterSdkAbortObserver(SdkAbortObserverHolder *holder) {
impl::GetSdkAbortObserverManager()->UnregisterObserver(holder);
}
}

View file

@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/diag_print_debug_string.hpp"
#include "impl/diag_invoke_abort.hpp"
namespace ams::diag {
@ -41,119 +41,193 @@ namespace ams::diag {
__builtin_unreachable();
}
inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
constinit os::SdkMutex g_assert_mutex;
constinit os::SdkMutex g_abort_mutex;
#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
constinit os::SdkRecursiveMutex g_debug_log_lock;
constinit char g_debug_buffer[0x400];
void DebugLogImpl(const char *format, ::std::va_list vl) {
std::scoped_lock lk(g_debug_log_lock);
util::VSNPrintf(g_debug_buffer, sizeof(g_debug_buffer), format, vl);
diag::impl::PrintDebugString(g_debug_buffer, strlen(g_debug_buffer));
}
void DebugLog(const char *format, ...) {
::std::va_list vl;
va_start(vl, format);
DebugLogImpl(format, vl);
va_end(vl);
}
#else
void DebugLog(const char *format, ...) { AMS_UNUSED(format); }
#endif
}
NORETURN NOINLINE WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
void PrepareAbort() {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Assertion Failure\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Assertion Failure\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
DebugLog(" Value: %016" PRIx64 "\n", value);
DebugLog("\n");
#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
{
::std::va_list vl;
va_start(vl, format);
DebugLogImpl(format, vl);
va_end(vl);
}
#else
AMS_UNUSED(format);
#endif
DebugLog("\n");
/* Get the thread local region. */
auto * const tlr = svc::GetThreadLocalRegion();
AbortWithValue(value);
}
/* Clear disable count. */
tlr->disable_count = 0;
NORETURN NOINLINE WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Assertion Failure\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Assertion Failure\n");
/* If we need to, unpin. */
if (tlr->interrupt_flag) {
svc::SynchronizePreemptionState();
}
}
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
DebugLog(" Value: %016" PRIx64 "\n", value);
DebugLog("\n");
DebugLog("\n");
AbortWithValue(value);
}
NORETURN NOINLINE WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Abort Called\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Abort Called\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
DebugLog(" Value: %016" PRIx64 "\n", value);
DebugLog("\n");
#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
AbortReason ToAbortReason(AssertionType type) {
switch (type) {
case AssertionType_Audit: return AbortReason_Audit;
case AssertionType_Assert: return AbortReason_Assert;
default:
return AbortReason_Abort;
}
}
AssertionFailureOperation DefaultAssertionFailureHandler(const AssertionInfo &) {
return AssertionFailureOperation_Abort;
}
constinit AssertionFailureHandler g_assertion_failure_handler = &DefaultAssertionFailureHandler;
void ExecuteAssertionFailureOperation(AssertionFailureOperation operation, const AssertionInfo &info) {
switch (operation) {
case AssertionFailureOperation_Continue:
break;
case AssertionFailureOperation_Abort:
{
::std::va_list vl;
va_start(vl, format);
DebugLogImpl(format, vl);
va_end(vl);
}
#else
AMS_UNUSED(format);
#endif
DebugLog("\n");
const AbortInfo abort_info = {
ToAbortReason(info.type),
info.message,
info.expr,
info.func,
info.file,
info.line,
};
AbortWithValue(value);
::ams::diag::impl::InvokeAbortObserver(abort_info);
AbortWithValue(0);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
NORETURN NOINLINE WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Abort Called\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Abort Called\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
DebugLog(" Value: %016" PRIx64 "\n", value);
DebugLog("\n");
DebugLog("\n");
AbortWithValue(value);
void InvokeAssertionFailureHandler(const AssertionInfo &info) {
const auto operation = g_assertion_failure_handler(info);
ExecuteAssertionFailureOperation(operation, info);
}
NORETURN NOINLINE WEAK_SYMBOL void AbortImpl() {
}
NOINLINE void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
/* Prepare to abort. */
PrepareAbort();
/* Acquire exclusive assert rights. */
if (g_assert_mutex.IsLockedByCurrentThread()) {
AbortWithValue(0);
}
std::scoped_lock lk(g_assert_mutex);
/* Create the assertion info. */
std::va_list vl;
va_start(vl, format);
const ::ams::diag::LogMessage message = { format, std::addressof(vl) };
const AssertionInfo info = {
type,
std::addressof(message),
expr,
func,
file,
line,
};
InvokeAssertionFailureHandler(info);
va_end(vl);
}
void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
return OnAssertionFailure(type, expr, func, file, line, "");
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
const Result res = ResultSuccess();
std::va_list vl{};
VAbortImpl(expr, func, file, line, std::addressof(res), nullptr, "", vl);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *fmt, ...) {
const Result res = ResultSuccess();
std::va_list vl;
va_start(vl, fmt);
VAbortImpl(expr, func, file, line, std::addressof(res), nullptr, fmt, vl);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
VAbortImpl(expr, func, file, line, result, nullptr, fmt, vl);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exc_info, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
VAbortImpl(expr, func, file, line, result, exc_info, fmt, vl);
}
NORETURN NOINLINE void VAbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exc_info, const char *fmt, std::va_list vl) {
/* Prepare to abort. */
PrepareAbort();
/* Acquire exclusive abort rights. */
if (g_abort_mutex.IsLockedByCurrentThread()) {
AbortWithValue(result->GetValue());
}
std::scoped_lock lk(g_abort_mutex);
/* Create abort info. */
std::va_list cvl;
va_copy(cvl, vl);
const diag::LogMessage message = { fmt, std::addressof(cvl) };
const AbortInfo abort_info = {
AbortReason_Abort,
std::addressof(message),
expr,
func,
file,
line,
};
const SdkAbortInfo sdk_abort_info = {
abort_info,
*result,
exc_info
};
/* Invoke observers. */
::ams::diag::impl::InvokeAbortObserver(abort_info);
::ams::diag::impl::InvokeSdkAbortObserver(sdk_abort_info);
/* Abort. */
AbortWithValue(result->GetValue());
}
}
namespace ams::impl {
NORETURN NOINLINE void UnexpectedDefaultImpl(const char *func, const char *file, int line) {
/* Create abort info. */
std::va_list vl{};
const ::ams::diag::LogMessage message = { "" , std::addressof(vl) };
const ::ams::diag::AbortInfo abort_info = {
::ams::diag::AbortReason_UnexpectedDefault,
std::addressof(message),
"",
func,
file,
line,
};
/* Invoke observers. */
::ams::diag::impl::InvokeAbortObserver(abort_info);
/* Abort. */
::ams::diag::AbortWithValue(0);
}
}

View file

@ -21,7 +21,19 @@
.balign 0x10
_ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE:
/* Save x27/x28. */
stp x27, x28, [sp, #0x10]
stp x27, x28, [sp, #-0x10]!
stp x0, xzr, [sp, #-0x10]!
/* Inline ams::diag::impl::PrepareAbort() */
mrs x27, tpidrro_el0
strh wzr, [x27, #0x100]
ldrh w27, [x27, #0x102]
cbz w27, 0f
svc #0x36
0: /* Restore the value from stack. */
ldr x0, [sp]
add sp, sp, #0x10
/* Put magic std::abort values into x27/x28. */
mov x28, #0xcafe
@ -31,7 +43,7 @@ _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE:
mov x27, #8
/* Abort */
0:
1:
str x28, [x27]
nop
b 0b
b 1b

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/diag_get_all_backtrace.hpp"
namespace ams::diag {
size_t GetBacktrace(uintptr_t *out, size_t out_size) {
/* Validate pre-conditions. */
AMS_ASSERT(out != nullptr);
AMS_ASSERT(out_size > 0);
/* Create the backtrace object. */
::ams::diag::Backtrace bt{};
bt.Step();
/* Get the backtrace. */
return ::ams::diag::impl::GetAllBacktrace(out, out_size, bt);
}
#if defined(ATMOSPHERE_OS_HORIZON)
size_t GetBacktace(uintptr_t *out, size_t out_size, uintptr_t fp, uintptr_t sp, uintptr_t pc) {
/* Validate pre-conditions. */
AMS_ASSERT(out != nullptr);
AMS_ASSERT(out_size > 0);
/* Create the backtrace object. */
::ams::diag::Backtrace bt{fp, sp, pc};
bt.Step();
/* Get the backtrace. */
return ::ams::diag::impl::GetAllBacktrace(out, out_size, bt);
}
#endif
}

View file

@ -45,11 +45,26 @@ namespace ams::diag::impl {
}
void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl) {
#if defined(ATMOSPHERE_OS_HORIZON)
/* Print to stack buffer. */
char msg_buffer[DebugPrintBufferLength];
/* TODO: VFormatString using utf-8 printer. */
const size_t len = util::VSNPrintf(msg_buffer, sizeof(msg_buffer), fmt, vl);
#else
/* Print to allocated buffer. */
std::va_list cvl;
va_copy(cvl, vl);
const auto out_len = util::TVSNPrintf(nullptr, 0, fmt, cvl) + 1;
va_end(cvl);
char *msg_buffer = static_cast<char *>(std::malloc(out_len));
AMS_ABORT_UNLESS(msg_buffer != nullptr);
ON_SCOPE_EXIT { std::free(msg_buffer); };
/* TODO: VFormatString using utf-8 printer. */
const size_t len = util::TVSNPrintf(msg_buffer, out_len, fmt, vl);
#endif
/* Call log observer. */
CallPrintDebugString()(meta, msg_buffer, len, true, true);

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/diag_symbol_impl.hpp"
namespace ams::diag {
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) {
AMS_ASSERT(dst != nullptr);
AMS_ASSERT(dst_size > 0);
AMS_ASSERT(address > 0);
return ::ams::diag::impl::GetSymbolNameImpl(dst, dst_size, address);
}
size_t GetSymbolSize(uintptr_t address) {
AMS_ASSERT(address > 0);
return ::ams::diag::impl::GetSymbolSizeImpl(address);
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_abort_observer_manager.hpp"
namespace ams::diag::impl {
AbortObserverManager *GetAbortObserverManager() {
AMS_FUNCTION_LOCAL_STATIC(AbortObserverManager, s_manager);
return std::addressof(s_manager);
}
SdkAbortObserverManager *GetSdkAbortObserverManager() {
AMS_FUNCTION_LOCAL_STATIC(SdkAbortObserverManager, s_manager);
return std::addressof(s_manager);
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "diag_observer_manager.hpp"
namespace ams::diag::impl {
using AbortObserverManager = ObserverManager<AbortObserverHolder, const AbortInfo &>;
using SdkAbortObserverManager = ObserverManager<SdkAbortObserverHolder, const SdkAbortInfo &>;
AbortObserverManager *GetAbortObserverManager();
SdkAbortObserverManager *GetSdkAbortObserverManager();
}

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
#include <fcntl.h>
#include <unistd.h>
#endif
namespace ams::diag::impl {
namespace {
#if defined(ATMOSPHERE_ARCH_X64)
struct StackFrame {
u64 fp; /* rbp */
u64 lr; /* rip */
};
#elif defined(ATMOSPHERE_ARCH_X86)
struct StackFrame {
u32 fp; /* ebp */
u32 lr; /* eip */
}
#elif defined(ATMOSPHERE_ARCH_ARM64)
struct StackFrame {
u64 fp;
u64 lr;
};
#elif defined(ATMOSPHERE_ARCH_ARM)
struct StackFrame {
u32 fp;
u32 lr;
}
#else
#error "Unknown architecture for generic backtrace."
#endif
bool TryRead(os::NativeHandle native_handle, void *dst, size_t size, const void *address) {
#if defined(ATMOSPHERE_OS_WINDOWS)
return ::ReadProcessMemory(native_handle, address, dst, size, nullptr);
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
s32 ret;
do {
ret = ::write(native_handle, address, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
return false;
}
std::memcpy(dst, address, size);
return true;
#else
#error "Unknown OS for Backtrace native handle"
#endif
}
}
NOINLINE void Backtrace::Initialize() {
/* Clear our size. */
m_index = 0;
m_size = 0;
/* Get the base frame pointer. */
const void *cur_fp = __builtin_frame_address(0);
/* Try to read stack frames, until we run out. */
#if defined(ATMOSPHERE_OS_WINDOWS)
const os::NativeHandle native_handle = ::GetCurrentProcess();
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
os::NativeHandle pipe_handles[2];
s32 nret;
do { nret = ::pipe(pipe_handles); } while (nret < 0 && errno == EINTR);
if (nret < 0) { return; }
do { nret = ::fcntl(pipe_handles[0], F_SETFL, O_NONBLOCK); } while (nret < 0 && errno == EINTR);
if (nret < 0) { return; }
do { nret = ::fcntl(pipe_handles[1], F_SETFL, O_NONBLOCK); } while (nret < 0 && errno == EINTR);
if (nret < 0) { return; }
ON_SCOPE_EXIT {
do { nret = ::close(pipe_handles[0]); } while (nret < 0 && errno == EINTR);
do { nret = ::close(pipe_handles[1]); } while (nret < 0 && errno == EINTR);
};
const os::NativeHandle native_handle = pipe_handles[1];
if (native_handle < 0) { return; }
#else
#error "Unknown OS for Backtrace native handle"
#endif
StackFrame frame;
while (m_size < BacktraceEntryCountMax) {
/* Clear the frame. */
frame = {};
/* Read the next frame. */
if (!TryRead(native_handle, std::addressof(frame), sizeof(frame), cur_fp)) {
break;
}
/* Add the return address. */
m_backtrace_addresses[m_size++] = reinterpret_cast<void *>(frame.lr);
/* Set the next fp. */
cur_fp = reinterpret_cast<const void *>(frame.fp);
}
}
bool Backtrace::Step() {
return (++m_index) < m_size;
}
uintptr_t Backtrace::GetStackPointer() const {
return 0;
}
uintptr_t Backtrace::GetReturnAddress() const {
return reinterpret_cast<uintptr_t>(m_backtrace_addresses[m_index]);
}
}

View file

@ -0,0 +1,217 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::diag::impl {
namespace {
uintptr_t GetAddressValue(uintptr_t address) {
if (address == 0) {
return 0;
}
if (!util::IsAligned(address, alignof(uintptr_t))) {
return 0;
}
return *reinterpret_cast<uintptr_t *>(address);
}
template<typename T, size_t N>
svc::MemoryInfo *GetMemoryInfoPointer(T (&arr)[N]) {
/* Check that there's enough space. */
static_assert(sizeof(T) * N <= sizeof(svc::MemoryInfo));
static_assert(alignof(T) >= alignof(svc::MemoryInfo));
return reinterpret_cast<svc::MemoryInfo *>(std::addressof(arr[0]));
}
bool IsValidLinkRegisterValue(uintptr_t lr, svc::MemoryInfo *info) {
/* Ensure the memory info is valid. */
Result query_res;
if (!(info->base_address <= lr && lr < info->base_address + info->size) && ({ svc::PageInfo page_info; query_res = svc::QueryMemory(info, std::addressof(page_info), lr); R_FAILED(query_res); })) {
AMS_SDK_LOG("Failed to get backtrace. Query memory failed. (lr: %p, result: %03d-%04d)\n", reinterpret_cast<void *>(lr), query_res.GetModule(), query_res.GetDescription());
return false;
}
/* Check that lr is valid. */
if (lr == 0) {
return false;
}
if (!util::IsAligned(lr, sizeof(u32))) {
AMS_SDK_LOG("Failed to get backtrace. The link register alignment is invalid. (lr: %p)\n", reinterpret_cast<void *>(lr));
return false;
}
/* Check that the lr points to code. */
if (info->permission != svc::MemoryPermission_ReadExecute) {
AMS_SDK_LOG("Failed to get backtrace. The link register points out of the code. (lr: %p)\n", reinterpret_cast<void *>(lr));
return false;
}
return true;
}
void GetNormalStackInfo(Backtrace::StackInfo *out) {
if (void * const fiber = nullptr /* TODO: os::GetCurrentFiber() */; fiber == nullptr) {
/* Get thread. */
auto * const thread = os::GetCurrentThread();
out->stack_top = reinterpret_cast<uintptr_t>(thread->stack);
out->stack_bottom = reinterpret_cast<uintptr_t>(thread->stack) + thread->stack_size;
} else {
/* TODO: Fiber. */
}
}
bool GetExceptionStackInfo(Backtrace::StackInfo *out, uintptr_t sp) {
/* Get the current stack info. */
uintptr_t cur_stack = 0;
size_t cur_stack_size = 0;
os::GetCurrentStackInfo(std::addressof(cur_stack), std::addressof(cur_stack_size));
/* Get the thread's stack info. */
uintptr_t thread_stack = 0;
size_t thread_stack_size = 0;
os::GetThreadStackInfo(std::addressof(thread_stack), std::addressof(thread_stack_size), os::GetCurrentThread());
/* If the current stack is the thread stack, exception stack isn't being used. */
if (cur_stack == thread_stack) {
AMS_ASSERT(cur_stack_size == thread_stack_size);
return false;
}
/* Check if the stack pointer is contained in the current stack. */
if (!(cur_stack <= sp && sp < cur_stack + cur_stack_size)) {
return false;
}
/* Set the output. */
out->stack_top = cur_stack;
out->stack_bottom = cur_stack + cur_stack_size;
return true;
}
}
NOINLINE void Backtrace::Initialize() {
/* Get the stack pointer/frame pointer. */
uintptr_t fp, sp;
__asm__ __volatile__(
#if defined(ATMOSPHERE_ARCH_ARM64)
"mov %[fp], fp\n"
"mov %[sp], sp\n"
#elif defined(ATMOSPHERE_ARCH_ARM)
"mov %[fp], x29\n"
"mov %[sp], sp\n"
#else
#error "Unknown architecture for Horizon fp/sp retrieval."
#endif
: [fp]"=&r"(fp), [sp]"=&r"(sp)
:
: "memory"
);
/* Set our stack info. */
this->SetStackInfo(fp, sp);
/* Step, to get our first lr. */
this->Step();
}
void Backtrace::Initialize(uintptr_t fp, uintptr_t sp, uintptr_t pc) {
/* Set our initial lr. */
m_lr = pc;
/* Set our stack info. */
this->SetStackInfo(fp, sp);
}
bool Backtrace::Step() {
/* We can't step without a frame pointer. */
if (m_fp == 0) {
if (m_current_stack_info != std::addressof(m_normal_stack_info)) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer is null.\n");
}
return false;
}
/* The frame pointer needs to be aligned. */
if (!util::IsAligned(m_fp, sizeof(uintptr_t))) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer alignment is invalid. (fp: %p)\n", reinterpret_cast<void *>(m_fp));
return false;
}
/* Ensure our current stack info is good. */
if (!(m_current_stack_info->stack_top <= m_fp && m_fp < m_current_stack_info->stack_bottom)) {
if (m_current_stack_info != std::addressof(m_exception_stack_info) || !(m_normal_stack_info.stack_top <= m_fp && m_fp < m_normal_stack_info.stack_bottom)) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer points out of the stack. (fp: %p, stack: %p-%p)\n", reinterpret_cast<void *>(m_fp), reinterpret_cast<void *>(m_current_stack_info->stack_top), reinterpret_cast<void *>(m_current_stack_info->stack_bottom));
return false;
}
m_current_stack_info = std::addressof(m_normal_stack_info);
} else if (m_fp <= m_prev_fp) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer is rewinding. (fp: %p, prev fp: %p, stack: %p-%p)\n", reinterpret_cast<void *>(m_fp), reinterpret_cast<void *>(m_prev_fp), reinterpret_cast<void *>(m_current_stack_info->stack_top), reinterpret_cast<void *>(m_current_stack_info->stack_bottom));
return false;
}
/* Update our previous fp. */
m_prev_fp = m_fp;
/* Read lr/fp. */
m_lr = GetAddressValue(m_fp + sizeof(m_fp));
m_fp = GetAddressValue(m_fp);
/* Check that lr is valid. */
if (IsValidLinkRegisterValue(m_lr, GetMemoryInfoPointer(m_memory_info_buffer))) {
return true;
} else {
m_lr = 0;
return false;
}
}
uintptr_t Backtrace::GetStackPointer() const {
if (m_fp != 0) {
return m_fp - sizeof(m_fp);
} else {
return m_current_stack_info->stack_bottom - sizeof(m_fp);
}
}
uintptr_t Backtrace::GetReturnAddress() const {
return m_lr;
}
void Backtrace::SetStackInfo(uintptr_t fp, uintptr_t sp) {
/* Get the normal stack info. */
GetNormalStackInfo(std::addressof(m_normal_stack_info));
/* Get the exception stack info. */
if (GetExceptionStackInfo(std::addressof(m_exception_stack_info), sp)) {
m_current_stack_info = std::addressof(m_exception_stack_info);
} else {
m_current_stack_info = std::addressof(m_normal_stack_info);
}
/* Set our frame pointer. */
m_fp = fp;
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_dump_stack_trace.hpp"
namespace ams::diag::impl {
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_DEBUGGING)
namespace {
constexpr const char *ToString(AbortReason reason) {
switch (reason) {
case AbortReason_Audit:
return "Auditing Assertion Failure";
case AbortReason_Assert:
return "Assertion Failure";
case AbortReason_UnexpectedDefault:
return "Unexpected Default";
case AbortReason_Abort:
default:
return "Abort";
}
}
void DefaultPrinter(const AbortInfo &info) {
/* Get the thread name. */
const char *thread_name;
if (auto *cur_thread = os::GetCurrentThread(); cur_thread != nullptr) {
thread_name = os::GetThreadNamePointer(cur_thread);
} else {
thread_name = "unknown";
}
#if defined(ATMOSPHERE_OS_HORIZON)
{
u64 process_id = 0;
u64 thread_id = 0;
svc::GetProcessId(std::addressof(process_id), svc::PseudoHandle::CurrentProcess);
svc::GetThreadId(std::addressof(thread_id), svc::PseudoHandle::CurrentThread);
AMS_SDK_LOG("%s: '%s' in %s, process=0x%02" PRIX64 ", thread=%" PRIu64 " (%s)\n%s:%d\n", ToString(info.reason), info.expr, info.func, process_id, thread_id, thread_name, info.file, info.line);
}
#elif defined(ATMOSPHERE_OS_WINDOWS)
{
DWORD process_id = ::GetCurrentProcessId();
DWORD thread_id = ::GetCurrentThreadId();
AMS_SDK_LOG("%s: '%s' in %s, process=0x%" PRIX64 ", thread=%" PRIu64 " (%s)\n%s:%d\n", ToString(info.reason), info.expr, info.func, static_cast<u64>(process_id), static_cast<u64>(thread_id), thread_name, info.file, info.line);
}
#else
{
AMS_SDK_LOG("%s: '%s' in %s, thread=%s\n%s:%d\n", ToString(info.reason), info.expr, info.func, thread_name, info.file, info.line);
}
#endif
AMS_SDK_VLOG(info.message->fmt, *(info.message->vl));
AMS_SDK_LOG("\n");
TentativeDumpStackTrace();
}
}
#endif
void DefaultAbortObserver(const AbortInfo &info) {
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
DefaultPrinter(info);
#else
AMS_UNUSED(info);
#endif
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void TentativeDumpStackTrace();
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_dump_stack_trace.hpp"
namespace ams::diag::impl {
void TentativeDumpStackTrace() {
AMS_SDK_LOG("----------------Stack Trace----------------\n");
{
/* Get the backtrace. */
constexpr size_t MaxBackTraceSize = 0x40;
uintptr_t backtrace[MaxBackTraceSize];
const size_t num_items = ::ams::diag::GetBacktrace(backtrace, MaxBackTraceSize);
/* Print each item. */
for (size_t i = 0; i < num_items; ++i) {
char symbol_name[0x200];
if (const uintptr_t symbol_base = ::ams::diag::GetSymbolName(symbol_name, sizeof(symbol_name), backtrace[i] - 1); symbol_base != 0) {
AMS_SDK_LOG("0x%016" PRIX64 " [ %s+0x%" PRIX64 " ]\n", static_cast<u64>(backtrace[i]), symbol_name, static_cast<u64>(backtrace[i] - (symbol_base + 1)));
} else {
AMS_SDK_LOG("0x%016" PRIX64 " [ unknown ]\n", static_cast<u64>(backtrace[i]));
}
}
}
AMS_SDK_LOG("-------------------------------------------\n");
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_dump_stack_trace.hpp"
namespace ams::diag::impl {
void TentativeDumpStackTrace() {
/* TODO */
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::diag::impl {
namespace {
constinit uintptr_t g_abort_impl_return_address = std::numeric_limits<uintptr_t>::max();
}
size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt) {
size_t count = 0;
do {
/* Check that we can write another return address. */
if (count >= out_size) {
break;
}
/* Get the current return address. */
const uintptr_t ret_addr = bt.GetReturnAddress();
/* If it's abort impl, reset the trace we're writing. */
if (ret_addr == g_abort_impl_return_address) {
count = 0;
}
/* Set the output pointer. */
out[count++] = ret_addr;
} while (bt.Step());
/* Return the number of addresses written. */
return count;
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void SetAbortImplAddress(uintptr_t address);
size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt);
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void InvokeAbortObserver(const AbortInfo &info);
void InvokeSdkAbortObserver(const SdkAbortInfo &info);
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_abort_observer_manager.hpp"
#include "diag_invoke_abort.hpp"
namespace ams::diag::impl {
extern bool g_enable_default_abort_observer;
void DefaultAbortObserver(const AbortInfo &info);
void InvokeAbortObserver(const AbortInfo &info) {
if (g_enable_default_abort_observer) {
DefaultAbortObserver(info);
}
GetAbortObserverManager()->InvokeAllObserver(info);
}
void InvokeSdkAbortObserver(const SdkAbortInfo &info) {
GetSdkAbortObserverManager()->InvokeAllObserver(info);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_abort_observer_manager.hpp"
#include "diag_invoke_abort.hpp"
namespace ams::diag::impl {
extern bool g_enable_default_abort_observer;
void DefaultAbortObserver(const AbortInfo &info);
void InvokeAbortObserver(const AbortInfo &info) {
if (g_enable_default_abort_observer) {
DefaultAbortObserver(info);
}
GetAbortObserverManager()->InvokeAllObserver(info);
}
void InvokeSdkAbortObserver(const SdkAbortInfo &info) {
GetSdkAbortObserverManager()->InvokeAllObserver(info);
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address);
size_t GetSymbolSizeImpl(uintptr_t address);
}

View file

@ -0,0 +1,289 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_symbol_impl.hpp"
#define PACKAGE "stratosphere"
#define PACKAGE_VERSION STRINGIFY(ATMOSPHERE_RELEASE_VERSION_MAJOR.ATMOSPHERE_RELEASE_VERSION_MINOR.ATMOSPHERE_RELEASE_VERSION_MICRO)
/* msys2 mingw64 puts headers inside binutils/ */
#if defined(ATMOSPHERE_OS_WINDOWS)
#include <binutils/bfd.h>
#include <psapi.h>
#else
#include <bfd.h>
#endif
#if defined(ATMOSPHERE_OS_LINUX)
#include <unistd.h>
#include <dlfcn.h>
#include <link.h>
extern "C" char __init_array_start;
#endif
#define HAVE_DECL_BASENAME 1
#include <libiberty/demangle.h>
namespace ams::diag::impl {
namespace {
class BfdHelper {
private:
bfd *m_handle;
asymbol **m_symbol;
size_t m_num_symbol;
size_t m_num_func_symbol;
const char *m_module_name;
uintptr_t m_module_address;
size_t m_module_size;
private:
BfdHelper() : m_handle(nullptr), m_symbol(nullptr), m_module_name(nullptr) {
/* Get the current executable name. */
char exe_path[4_KB] = {};
GetExecutablePath(exe_path, sizeof(exe_path));
/* Open bfd. */
bfd *b = ::bfd_openr(exe_path, 0);
if (b == nullptr) {
return;
}
auto bfd_guard = SCOPE_GUARD { ::bfd_close(b); };
/* Check the format. */
if (!::bfd_check_format(b, bfd_object)) {
return;
}
/* Verify the file has symbols. */
if ((bfd_get_file_flags(b) & HAS_SYMS) == 0) {
return;
}
/* Read the symbols. */
unsigned int _;
void *symbol_table;
s64 num_symbols = bfd_read_minisymbols(b, false, std::addressof(symbol_table), std::addressof(_));
if (num_symbols == 0) {
num_symbols = bfd_read_minisymbols(b, true, std::addressof(symbol_table), std::addressof(_));
if (num_symbols < 0) {
return;
}
}
/* We successfully got the symbol table. */
bfd_guard.Cancel();
m_handle = b;
m_symbol = reinterpret_cast<asymbol **>(symbol_table);
m_num_symbol = static_cast<size_t>(num_symbols);
/* Sort the symbol table. */
std::sort(m_symbol + 0, m_symbol + m_num_symbol, [] (asymbol *lhs, asymbol *rhs) {
const bool l_func = (lhs->flags & BSF_FUNCTION);
const bool r_func = (rhs->flags & BSF_FUNCTION);
if (l_func == r_func) {
return bfd_asymbol_value(lhs) < bfd_asymbol_value(rhs);
} else {
return l_func;
}
});
/* Determine number of function symbols. */
m_num_func_symbol = 0;
for (size_t i = 0; i < m_num_symbol; ++i) {
if ((m_symbol[i]->flags & BSF_FUNCTION) == 0) {
m_num_func_symbol = i;
break;
}
}
for (int i = std::strlen(exe_path) - 1; i >= 0; --i) {
if (exe_path[i] == '/' || exe_path[i] == '\\') {
m_module_name = strdup(exe_path + i + 1);
break;
}
}
/* Get our module base/size. */
#if defined(ATMOSPHERE_OS_WINDOWS)
{
MODULEINFO module_info;
if (::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandleA(nullptr), std::addressof(module_info), sizeof(module_info))) {
m_module_address = reinterpret_cast<uintptr_t>(module_info.lpBaseOfDll);
m_module_size = static_cast<size_t>(module_info.SizeOfImage);
}
}
#elif defined(ATMOSPHERE_OS_LINUX)
{
m_module_address = _r_debug.r_map->l_addr;
m_module_size = reinterpret_cast<uintptr_t>(std::addressof(__init_array_start)) - m_module_address;
}
#endif
}
~BfdHelper() {
if (m_symbol != nullptr) {
std::free(m_symbol);
}
if (m_handle != nullptr) {
::bfd_close(m_handle);
}
}
public:
static BfdHelper &GetInstance() {
AMS_FUNCTION_LOCAL_STATIC(BfdHelper, s_bfd_helper_instance);
return s_bfd_helper_instance;
}
private:
size_t GetSymbolSizeImpl(asymbol **symbol) const {
/* Do our best to guess. */
const auto vma = bfd_asymbol_value(*symbol);
if (symbol != m_symbol + m_num_func_symbol - 1) {
return bfd_asymbol_value(*(symbol + 1)) - vma;
} else {
const auto *sec = (*symbol)->section;
return (sec->vma + sec->size) - vma;
}
}
std::ptrdiff_t GetSymbolAddressDisplacement(uintptr_t address) const {
std::ptrdiff_t displacement = 0;
if (m_module_address <= address && address < m_module_address + m_module_size) {
displacement = m_module_address;
#if defined(ATMOSPHERE_OS_WINDOWS)
{
#if defined(__MINGW64__)
displacement -= UINT64_C(0x140000000);
#elif defined(__MINGW32__)
displacement -= UINT64_C(0x400000);
#else
#error "Unknown build context for windows module base!"
#endif
}
#endif
}
return displacement;
}
asymbol **GetBestSymbol(uintptr_t address) const {
/* Adjust the symbol address. */
address -= this->GetSymbolAddressDisplacement(address);
asymbol **best_symbol = std::lower_bound(m_symbol + 0, m_symbol + m_num_func_symbol, address, [](asymbol *lhs, uintptr_t rhs) {
return bfd_asymbol_value(lhs) < rhs;
});
if (best_symbol == m_symbol + m_num_func_symbol) {
return nullptr;
}
if (bfd_asymbol_value(*best_symbol) != address && best_symbol > m_symbol) {
--best_symbol;
}
const auto vma = bfd_asymbol_value(*best_symbol);
const auto end = vma + this->GetSymbolSizeImpl(best_symbol);
if (vma <= address && address < end) {
return best_symbol;
} else {
return nullptr;
}
}
public:
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) const {
/* Get the symbol. */
auto **symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
/* Print the symbol. */
const char *name = bfd_asymbol_name(*symbol);
if (auto *demangled = bfd_demangle(m_handle, name, DMGL_ANSI | DMGL_PARAMS); demangled != nullptr) {
util::TSNPrintf(dst, dst_size, "%s", demangled);
std::free(demangled);
} else {
util::TSNPrintf(dst, dst_size, "%s", name);
}
return bfd_asymbol_value(*symbol) + this->GetSymbolAddressDisplacement(address);
}
size_t GetSymbolSize(uintptr_t address) const {
/* Get the symbol. */
auto **symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
return this->GetSymbolSizeImpl(symbol);
}
private:
static void GetExecutablePath(char *dst, size_t dst_size) {
#if defined(ATMOSPHERE_OS_WINDOWS)
{
/* Get the module file name. */
wchar_t module_file_name[0x1000];
if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) {
dst[0] = 0;
return;
}
/* Convert to utf-8. */
const auto res = ::WideCharToMultiByte(CP_UTF8, 0, module_file_name, -1, dst, dst_size, nullptr, nullptr);
if (res == 0) {
dst[0] = 0;
return;
}
}
#elif defined(ATMOSPHERE_OS_LINUX)
{
if (::readlink("/proc/self/exe", dst, dst_size) == -1) {
dst[0] = 0;
return;
}
}
#elif defined(ATMOSPHERE_OS_MACOS)
{
if (_NSGetExecutablePath(dst, dst_size) != 0) {
dst[0] = 0;
return;
}
}
#else
#error "Unknown OS for BfdHelper GetExecutablePath"
#endif
}
};
}
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
return BfdHelper::GetInstance().GetSymbolName(dst, dst_size, address);
}
size_t GetSymbolSizeImpl(uintptr_t address) {
return BfdHelper::GetInstance().GetSymbolSize(address);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_symbol_impl.hpp"
namespace ams::diag::impl {
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
AMS_UNUSED(dst, dst_size, address);
AMS_ABORT("TODO");
}
size_t GetSymbolSizeImpl(uintptr_t address) {
AMS_UNUSED(address);
AMS_ABORT("TODO");
}
}

View file

@ -144,6 +144,14 @@ namespace ams::fs::impl {
}
}
template<> const char *IdString::ToString<fs::MountHostOption>(fs::MountHostOption id) {
if (id == MountHostOption::PseudoCaseSensitive) {
return "MountHostOptionFlag_PseudoCaseSensitive";
} else {
return ToValueString(static_cast<int>(id._value));
}
}
template<> const char *IdString::ToString<fs::BisPartitionId>(fs::BisPartitionId id) {
switch (id) {
using enum fs::BisPartitionId;

View file

@ -23,7 +23,7 @@
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_cache_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_cache_impl.os.linux.hpp"
#include "os_cache_impl.os.macos.hpp"
#else
#error "Unknown OS for CacheImpl"
#endif

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_debug_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_debug_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_debug_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_debug_impl.os.macos.hpp"
#else
#error "Unknown OS for DebugImpl"
#endif

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_thread_manager.hpp"
namespace ams::os::impl {
class DebugHorizonImpl {
public:
static uintptr_t GetCurrentStackPointer() {
uintptr_t v;
__asm__ __volatile__("mov %[v], sp" : [v]"=&r"(v) :: "memory");
return v;
}
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
/* Check pre-conditions. */
AMS_ASSERT(out_stack != nullptr);
AMS_ASSERT(out_size != nullptr);
/* Get the current thread. */
auto *cur_thread = os::impl::GetCurrentThread();
auto *cur_fiber = cur_thread->current_fiber;
/* Get the current stack pointer. */
uintptr_t cur_sp = GetCurrentStackPointer();
/* Determine current stack extents, TODO Fiber */
uintptr_t stack_top = reinterpret_cast<uintptr_t>(cur_fiber == nullptr ? cur_thread->stack : /* TODO: cur_fiber->stack */ nullptr);
size_t stack_size = reinterpret_cast<size_t>(cur_fiber == nullptr ? cur_thread->stack_size : /* TODO: cur_fiber->stack_size */ 0);
uintptr_t stack_bottom = stack_top + stack_size;
/* TODO: User exception handler, check if stack is out of range and use exception stack. */
/* Check that the stack pointer is in bounds. */
AMS_ABORT_UNLESS((stack_top <= cur_sp) && (cur_sp < stack_bottom));
/* Set the output. */
*out_stack = stack_top;
*out_size = stack_size;
}
static void QueryMemoryInfo(os::MemoryInfo *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO: Horizon QueryMemoryInfo");
}
static Tick GetIdleTickCount() {
u64 value;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_IdleTickCount, svc::InvalidHandle, static_cast<u64>(-1)));
return os::Tick(value);
}
static Tick GetThreadTickCount() {
u64 value;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_ThreadTickCount, svc::PseudoHandle::CurrentThread, static_cast<u64>(-1)));
return os::Tick(value);
}
static int GetFreeThreadCount() {
u64 value;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_FreeThreadCount, svc::PseudoHandle::CurrentProcess, 0));
AMS_ASSERT(value <= static_cast<u64>(std::numeric_limits<int>::max()));
return static_cast<int>(value);
}
};
using DebugImpl = DebugHorizonImpl;
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class DebugLinuxImpl {
public:
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
/* Check pre-conditions. */
AMS_ASSERT(out_stack != nullptr);
AMS_ASSERT(out_size != nullptr);
/* Get the current stack by pthread */
pthread_attr_t attr;
pthread_attr_init(std::addressof(attr));
ON_SCOPE_EXIT { pthread_attr_destroy(std::addressof(attr)); };
const auto getattr_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
AMS_ABORT_UNLESS(getattr_res == 0);
/* Get the thread satck. */
void *base = nullptr;
size_t size = 0;
const auto getstack_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
AMS_ABORT_UNLESS(getstack_res == 0);
*out_stack = reinterpret_cast<uintptr_t>(base);
*out_size = size;
}
static void QueryMemoryInfo(os::MemoryInfo *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO: Linux QueryMemoryInfo");
}
static Tick GetIdleTickCount() {
return os::Tick(0);
}
static Tick GetThreadTickCount() {
return os::Tick(0);
}
static int GetFreeThreadCount() {
return 0;
}
};
using DebugImpl = DebugLinuxImpl;
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class DebugMacosImpl {
public:
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
/* Check pre-conditions. */
AMS_ASSERT(out_stack != nullptr);
AMS_ASSERT(out_size != nullptr);
/* Get the current stack by pthread */
pthread_attr_t attr;
pthread_attr_init(std::addressof(attr));
ON_SCOPE_EXIT { pthread_attr_destroy(std::addressof(attr)); };
const auto getattr_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
AMS_ABORT_UNLESS(getattr_res == 0);
/* Get the thread satck. */
void *base = nullptr;
size_t size = 0;
const auto getstack_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
AMS_ABORT_UNLESS(getstack_res == 0);
*out_stack = reinterpret_cast<uintptr_t>(base);
*out_size = size;
}
static void QueryMemoryInfo(os::MemoryInfo *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO: macOS QueryMemoryInfo");
}
static Tick GetIdleTickCount() {
return os::Tick(0);
}
static Tick GetThreadTickCount() {
return os::Tick(0);
}
static int GetFreeThreadCount() {
return 0;
}
};
using DebugImpl = DebugMacosImpl;
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class DebugWindowsImpl {
public:
static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
/* Check pre-conditions. */
AMS_ASSERT(out_stack != nullptr);
AMS_ASSERT(out_size != nullptr);
/* Get the current stack by NT_TIB */
auto *tib = reinterpret_cast<NT_TIB *>(::NtCurrentTeb());
*out_stack = reinterpret_cast<uintptr_t>(tib->StackLimit);
*out_size = reinterpret_cast<uintptr_t>(tib->StackBase) - reinterpret_cast<uintptr_t>(tib->StackLimit);
}
static void QueryMemoryInfo(os::MemoryInfo *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO: Windows QueryMemoryInfo");
}
static Tick GetIdleTickCount() {
return os::Tick(0);
}
static Tick GetThreadTickCount() {
return os::Tick(0);
}
static int GetFreeThreadCount() {
return 0;
}
};
using DebugImpl = DebugWindowsImpl;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_debug_impl.hpp"
namespace ams::os {
void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
/* Get the current stack info. */
uintptr_t stack_top = 0;
size_t stack_size = 0;
impl::DebugImpl::GetCurrentStackInfo(std::addressof(stack_top), std::addressof(stack_size));
/* Basic sanity check. */
uintptr_t sp = reinterpret_cast<uintptr_t>(std::addressof(stack_top));
AMS_ASSERT((stack_top <= sp) && (sp < (stack_top + stack_size)));
AMS_UNUSED(sp);
/* Set the output. */
if (out_stack != nullptr) {
*out_stack = stack_top;
}
if (out_size != nullptr) {
*out_size = stack_size;
}
}
void QueryMemoryInfo(MemoryInfo *out) {
return impl::DebugImpl::QueryMemoryInfo(out);
}
Tick GetIdleTickCount() {
return impl::DebugImpl::GetIdleTickCount();
}
int GetFreeThreadCount() {
return impl::DebugImpl::GetFreeThreadCount();
}
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::result::impl {
NORETURN WEAK_SYMBOL void OnResultAbort(const char *file, int line, const char *func, const char *expr, Result result) {
::ams::diag::AbortImpl(file, line, func, expr, result.GetValue(), "Result Abort: 2%03d-%04d", result.GetModule(), result.GetDescription());
AMS_INFINITE_LOOP();
__builtin_unreachable();
}
NORETURN WEAK_SYMBOL void OnResultAbort(Result result) {
OnResultAbort("", 0, "", "", result);
}
NORETURN WEAK_SYMBOL void OnResultAssertion(const char *file, int line, const char *func, const char *expr, Result result) {
::ams::diag::AssertionFailureImpl(file, line, func, expr, result.GetValue(), "Result Assertion: 2%03d-%04d", result.GetModule(), result.GetDescription());
AMS_INFINITE_LOOP();
__builtin_unreachable();
}
NORETURN WEAK_SYMBOL void OnResultAssertion(Result result) {
OnResultAssertion("", 0, "", "", result);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::sf::hipc {
void AttachMultiWaitHolderForAccept(os::MultiWaitHolderType *, os::NativeHandle) {
AMS_ABORT("TODO: Generic ams::sf::hipc::AttachMultiWaitHolderForAccept");
}
void AttachMultiWaitHolderForReply(os::MultiWaitHolderType *, os::NativeHandle) {
AMS_ABORT("TODO: Generic ams::sf::hipc::AttachMultiWaitHolderForAccept");
}
Result Receive(ReceiveResult *, os::NativeHandle, const cmif::PointerAndSize &) {
AMS_ABORT("TODO: Generic ams::sf::hipc::Receive(ReceiveResult *, os::NativeHandle, const cmif::PointerAndSize &)");
}
Result Receive(bool *, os::NativeHandle, const cmif::PointerAndSize &) {
AMS_ABORT("TODO: Generic ams::sf::hipc::Receive(bool *, os::NativeHandle, const cmif::PointerAndSize &)");
}
Result Reply(os::NativeHandle, const cmif::PointerAndSize &) {
AMS_ABORT("TODO: Generic ams::sf::hipc::Reply");
}
Result CreateSession(os::NativeHandle *, os::NativeHandle *) {
AMS_ABORT("TODO: Generic ams::sf::hipc::CreateSession");
}
}

View file

@ -16,48 +16,103 @@
#pragma once
#include <vapours/common.hpp>
namespace ams {
class Result;
namespace os {
struct UserExceptionInfo;
}
namespace impl {
NORETURN void UnexpectedDefaultImpl(const char *func, const char *file, int line);
}
}
namespace ams::diag {
NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7)));
NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value);
enum AssertionType {
AssertionType_Audit,
AssertionType_Assert,
};
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7)));
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value);
NORETURN void AbortImpl();
struct LogMessage;
struct AssertionInfo {
AssertionType type;
const LogMessage *message;
const char *expr;
const char *func;
const char *file;
int line;
};
enum AbortReason {
AbortReason_Audit,
AbortReason_Assert,
AbortReason_Abort,
AbortReason_UnexpectedDefault,
};
struct AbortInfo {
AbortReason reason;
const LogMessage *message;
const char *expr;
const char *func;
const char *file;
int line;
};
void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) __attribute__((format(printf, 6, 7)));
void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line);
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) __attribute__((format(printf, 5, 6)));
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *format, ...) __attribute__((format(printf, 6, 7)));
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exception_info, const char *fmt, ...) __attribute__((format(printf, 7, 8)));
NORETURN void VAbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exception_info, const char *fmt, std::va_list vl);
}
#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
#define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__)
#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__)
#define AMS_CALL_ASSERT_FAIL_IMPL(type, expr, ...) ::ams::diag::OnAssertionFailure(type, expr, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__)
#define AMS_CALL_ABORT_IMPL(expr, ...) ::ams::diag::AbortImpl(expr, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__)
#define AMS_UNREACHABLE_DEFAULT_CASE() default: ::ams::impl::UnexpectedDefaultImpl(__PRETTY_FUNCTION__, __FILE__, __LINE__)
#else
#define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl("", 0, "", "", 0)
#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl(); AMS_UNUSED(cond, ## __VA_ARGS__)
#define AMS_CALL_ASSERT_FAIL_IMPL(type, expr, ...) ::ams::diag::OnAssertionFailure(type, "", "", "", 0)
#define AMS_CALL_ABORT_IMPL(expr, ...) ::ams::diag::AbortImpl("", "", "", 0); AMS_UNUSED(expr, ## __VA_ARGS__)
#define AMS_UNREACHABLE_DEFAULT_CASE() default: ::ams::impl::UnexpectedDefaultImpl("", "", 0)
#endif
#ifdef AMS_ENABLE_ASSERTIONS
#define AMS_ASSERT_IMPL(expr, ...) \
#define AMS_ASSERT_IMPL(type, expr, ...) \
{ \
if (std::is_constant_evaluated()) { \
AMS_ASSUME(static_cast<bool>(expr)); \
} else { \
if (const bool __tmp_ams_assert_val = static_cast<bool>(expr); (!__tmp_ams_assert_val)) { \
AMS_CALL_ASSERT_FAIL_IMPL(#expr, ## __VA_ARGS__); \
AMS_CALL_ASSERT_FAIL_IMPL(type, #expr, ## __VA_ARGS__); \
} \
} \
}
#elif defined(AMS_PRESERVE_ASSERTION_EXPRESSIONS)
#define AMS_ASSERT_IMPL(expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
#define AMS_ASSERT_IMPL(type, expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
#else
#define AMS_ASSERT_IMPL(expr, ...) static_cast<void>(0)
#define AMS_ASSERT_IMPL(type, expr, ...) static_cast<void>(0)
#endif
#define AMS_ASSERT(expr, ...) AMS_ASSERT_IMPL(expr, ## __VA_ARGS__)
#define AMS_ASSERT(expr, ...) AMS_ASSERT_IMPL(::ams::diag::AssertionType_Assert, expr, ## __VA_ARGS__)
#define AMS_UNREACHABLE_DEFAULT_CASE() default: AMS_CALL_ABORT_IMPL("Unreachable default case entered")
#ifdef AMS_BUILD_FOR_AUDITING
#define AMS_AUDIT(expr, ...) AMS_ASSERT(expr, ## __VA_ARGS__)
#define AMS_AUDIT(expr, ...) AMS_ASSERT_IMPL(::ams::diag::AssertionType_Audit, expr, ## __VA_ARGS__)
#elif defined(AMS_PRESERVE_AUDIT_EXPRESSIONS)
#define AMS_AUDIT(expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
#else

View file

@ -16,11 +16,10 @@
#pragma once
#include <vapours/results/results_common.hpp>
#include <vapours/results/powctl_results.hpp>
namespace ams::cal {
R_DEFINE_NAMESPACE_RESULT_MODULE(198);
R_DEFINE_ERROR_RESULT(CalibrationDataCrcError, 101);
using powctl::ResultCalibrationDataCrcError;
}

View file

@ -16,9 +16,10 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::capsrv {
R_DEFINE_NAMESPACE_RESULT_MODULE(206);
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::capsrv, 206);
namespace ams::capsrv {
R_DEFINE_ERROR_RANGE(AlbumError, 2, 99);
R_DEFINE_ERROR_RESULT(AlbumWorkMemoryError, 3);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::creport {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::creport, 168);
R_DEFINE_NAMESPACE_RESULT_MODULE(168);
namespace ams::creport {
R_DEFINE_ERROR_RESULT(UndefinedInstruction, 0);
R_DEFINE_ERROR_RESULT(InstructionAbort, 1);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::cs {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::cs, 204);
R_DEFINE_NAMESPACE_RESULT_MODULE(204);
namespace ams::cs {
R_DEFINE_ERROR_RESULT(UnknownCommand, 2);
R_DEFINE_ERROR_RESULT(OutOfResource, 4);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::dd {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::dd, 6);
R_DEFINE_NAMESPACE_RESULT_MODULE(6);
namespace ams::dd {
R_DEFINE_ERROR_RESULT(EndOfQuery, 1);
R_DEFINE_ERROR_RESULT(InvalidCurrentMemory, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::ddsf {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ddsf, 30);
R_DEFINE_NAMESPACE_RESULT_MODULE(30);
namespace ams::ddsf {
R_DEFINE_ERROR_RESULT(OutOfResource, 1);
R_DEFINE_ERROR_RESULT(NotSupported, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::dbg {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::dbg, 183);
R_DEFINE_NAMESPACE_RESULT_MODULE(183);
namespace ams::dbg {
R_DEFINE_ERROR_RESULT(CannotDebug, 1);
R_DEFINE_ERROR_RESULT(AlreadyAttached, 2);

View file

@ -17,34 +17,34 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::dmnt {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::dmnt, 13);
R_DEFINE_NAMESPACE_RESULT_MODULE(13);
namespace ams::dmnt {
R_DEFINE_ERROR_RESULT(Unknown, 1);
R_DEFINE_ERROR_RESULT(DebuggingDisabled, 2);
/* Atmosphere extension. */
namespace cheat {
// namespace cheat {
R_DEFINE_ABSTRACT_ERROR_RANGE(CheatError, 6500, 6599);
R_DEFINE_ERROR_RESULT(CheatNotAttached, 6500);
R_DEFINE_ERROR_RESULT(CheatNullBuffer, 6501);
R_DEFINE_ERROR_RESULT(CheatInvalidBuffer, 6502);
R_DEFINE_ERROR_RESULT(CheatUnknownId, 6503);
R_DEFINE_ERROR_RESULT(CheatOutOfResource, 6504);
R_DEFINE_ERROR_RESULT(CheatInvalid, 6505);
R_DEFINE_ERROR_RESULT(CheatCannotDisable, 6506);
R_DEFINE_ABSTRACT_ERROR_RANGE_NS(cheat, CheatError, 6500, 6599);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatNotAttached, 6500);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatNullBuffer, 6501);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatInvalidBuffer, 6502);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatUnknownId, 6503);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatOutOfResource, 6504);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatInvalid, 6505);
R_DEFINE_ERROR_RESULT_NS(cheat, CheatCannotDisable, 6506);
R_DEFINE_ABSTRACT_ERROR_RANGE(FrozenAddressError, 6600, 6699);
R_DEFINE_ERROR_RESULT(FrozenAddressInvalidWidth, 6600);
R_DEFINE_ERROR_RESULT(FrozenAddressAlreadyExists, 6601);
R_DEFINE_ERROR_RESULT(FrozenAddressNotFound, 6602);
R_DEFINE_ERROR_RESULT(FrozenAddressOutOfResource, 6603);
R_DEFINE_ABSTRACT_ERROR_RANGE_NS(cheat, FrozenAddressError, 6600, 6699);
R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressInvalidWidth, 6600);
R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressAlreadyExists, 6601);
R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressNotFound, 6602);
R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressOutOfResource, 6603);
R_DEFINE_ABSTRACT_ERROR_RANGE(VirtualMachineError, 6700, 6799);
R_DEFINE_ERROR_RESULT(VirtualMachineInvalidConditionDepth, 6700);
R_DEFINE_ABSTRACT_ERROR_RANGE_NS(cheat, VirtualMachineError, 6700, 6799);
R_DEFINE_ERROR_RESULT_NS(cheat, VirtualMachineInvalidConditionDepth, 6700);
}
// }
}

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::erpt {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::erpt, 147);
R_DEFINE_NAMESPACE_RESULT_MODULE(147);
namespace ams::erpt {
R_DEFINE_ERROR_RESULT(NotInitialized, 1);
R_DEFINE_ERROR_RESULT(AlreadyInitialized, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::err {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::err, 162);
R_DEFINE_NAMESPACE_RESULT_MODULE(162);
namespace ams::err {
R_DEFINE_ERROR_RESULT(ApplicationAbort, 1);
R_DEFINE_ERROR_RESULT(SystemProgramAbort, 2);

View file

@ -17,12 +17,11 @@
#pragma once
#include <vapours/results/results_common.hpp>
/* Please note: These results are all custom, and not official. */
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::exosphere, 444);
namespace ams::exosphere {
/* Please note: These results are all custom, and not official. */
R_DEFINE_NAMESPACE_RESULT_MODULE(444);
/* Result 1-1000 reserved for Atmosphere. */
R_DEFINE_ERROR_RESULT(NotPresent, 1);
R_DEFINE_ERROR_RESULT(VersionMismatch, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::fatal {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::fatal, 163);
R_DEFINE_NAMESPACE_RESULT_MODULE(163);
namespace ams::fatal {
R_DEFINE_ERROR_RESULT(AllocationFailed, 1);
R_DEFINE_ERROR_RESULT(NullGraphicsBuffer, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::fs {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::fs, 2);
R_DEFINE_NAMESPACE_RESULT_MODULE(2);
namespace ams::fs {
R_DEFINE_ERROR_RANGE(HandledByAllProcess, 0, 999);
R_DEFINE_ERROR_RESULT(PathNotFound, 1);
@ -217,7 +217,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeC, 4510);
R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeD, 4511);
R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4512, 4529);
R_DEFINE_ERROR_RESULT_CLASS_IMPL(NcaFileSystemCorrupted, 4512, 4529);
R_DEFINE_ERROR_RESULT(InvalidNcaFileSystemType, 4512);
R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513);
R_DEFINE_ERROR_RESULT(InvalidAcidSize, 4514);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::gpio {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::gpio, 102);
R_DEFINE_NAMESPACE_RESULT_MODULE(102);
namespace ams::gpio {
R_DEFINE_ERROR_RESULT(AlreadyBound, 1);
R_DEFINE_ERROR_RESULT(AlreadyOpen, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::sf::hipc {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::sf::hipc, 11);
R_DEFINE_NAMESPACE_RESULT_MODULE(11);
namespace ams::sf::hipc {
R_DEFINE_ABSTRACT_ERROR_RANGE(OutOfResource, 100, 299);
R_DEFINE_ERROR_RESULT(OutOfSessionMemory, 102);

View file

@ -16,9 +16,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::htc {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htc, 18);
R_DEFINE_NAMESPACE_RESULT_MODULE(18);
namespace ams::htc {
R_DEFINE_ERROR_RESULT(ConnectionFailure, 1);
R_DEFINE_ERROR_RESULT(NotFound, 2);

View file

@ -16,9 +16,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::htcfs {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htcfs, 31);
R_DEFINE_NAMESPACE_RESULT_MODULE(31);
namespace ams::htcfs {
R_DEFINE_ERROR_RESULT(InvalidArgument, 3);

View file

@ -16,9 +16,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::htclow {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htclow, 29);
R_DEFINE_NAMESPACE_RESULT_MODULE(29);
namespace ams::htclow {
R_DEFINE_ERROR_RESULT(ConnectionFailure, 1);
R_DEFINE_ERROR_RESULT(UnknownDriverType, 3);
@ -28,7 +28,7 @@ namespace ams::htclow {
R_DEFINE_ERROR_RESULT(ChannelNotExist, 10);
R_DEFINE_ERROR_RESULT(OutOfChannel, 151);
R_DEFINE_ERROR_RESULT(OutOfTask, 151);
R_DEFINE_ERROR_RESULT(OutOfTask, 152);
R_DEFINE_ERROR_RESULT(InvalidChannelState, 200);
R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201);

View file

@ -16,9 +16,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::htcs {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htcs, 4);
R_DEFINE_NAMESPACE_RESULT_MODULE(4);
namespace ams::htcs {
R_DEFINE_ERROR_RESULT(InvalidHandle, 9);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::i2c {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::i2c, 101);
R_DEFINE_NAMESPACE_RESULT_MODULE(101);
namespace ams::i2c {
R_DEFINE_ERROR_RESULT(NoAck, 1);
R_DEFINE_ERROR_RESULT(BusBusy, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::kvdb {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::kvdb, 20);
R_DEFINE_NAMESPACE_RESULT_MODULE(20);
namespace ams::kvdb {
R_DEFINE_ERROR_RESULT(OutOfKeyResource, 1);
R_DEFINE_ERROR_RESULT(KeyNotFound, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::ldr {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ldr, 9);
R_DEFINE_NAMESPACE_RESULT_MODULE(9);
namespace ams::ldr {
R_DEFINE_ERROR_RESULT(ArgumentOverflow, 1);
R_DEFINE_ERROR_RESULT(ArgumentCountOverflow, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::lr {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::lr, 8);
R_DEFINE_NAMESPACE_RESULT_MODULE(8);
namespace ams::lr {
R_DEFINE_ERROR_RESULT(ProgramNotFound, 2);
R_DEFINE_ERROR_RESULT(DataNotFound, 3);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::ncm {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ncm, 5);
R_DEFINE_NAMESPACE_RESULT_MODULE(5);
namespace ams::ncm {
R_DEFINE_ERROR_RESULT(InvalidContentStorageBase, 1);
R_DEFINE_ERROR_RESULT(PlaceHolderAlreadyExists, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::nim {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::nim, 137);
R_DEFINE_NAMESPACE_RESULT_MODULE(137);
namespace ams::nim {
R_DEFINE_ERROR_RESULT(HttpConnectionCanceled, 70);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::ns {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ns, 16);
R_DEFINE_NAMESPACE_RESULT_MODULE(16);
namespace ams::ns {
R_DEFINE_ERROR_RESULT(Canceled, 90);
R_DEFINE_ERROR_RESULT(OutOfMaxRunningTask, 110);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::os {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::os, 3);
R_DEFINE_NAMESPACE_RESULT_MODULE(3);
namespace ams::os {
R_DEFINE_ERROR_RESULT(Busy, 4);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::osdbg {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::osdbg, 7);
R_DEFINE_NAMESPACE_RESULT_MODULE(7);
namespace ams::osdbg {
R_DEFINE_ERROR_RESULT(CannotGetThreadInfo, 1);
R_DEFINE_ERROR_RESULT(UnsupportedThreadVersion, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::pcv {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pcv, 133);
R_DEFINE_NAMESPACE_RESULT_MODULE(133);
namespace ams::pcv {
R_DEFINE_ERROR_RESULT(IllegalRequest, 16);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::pgl {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pgl, 228);
R_DEFINE_NAMESPACE_RESULT_MODULE(228);
namespace ams::pgl {
R_DEFINE_ERROR_RESULT(NotImplemented, 1);
R_DEFINE_ERROR_RESULT(NotAvailable, 2);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::pm {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pm, 15);
R_DEFINE_NAMESPACE_RESULT_MODULE(15);
namespace ams::pm {
R_DEFINE_ERROR_RESULT(ProcessNotFound, 1);
R_DEFINE_ERROR_RESULT(AlreadyStarted, 2);

View file

@ -17,12 +17,14 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::powctl {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::powctl, 198);
R_DEFINE_NAMESPACE_RESULT_MODULE(198);
namespace ams::powctl {
R_DEFINE_ERROR_RESULT(NotSupported, 1);
R_DEFINE_ERROR_RESULT(InvalidArgument, 2);
R_DEFINE_ERROR_RESULT(NotAvailable, 3);
R_DEFINE_ERROR_RESULT(CalibrationDataCrcError, 101);
}

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::psc {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::psc, 138);
R_DEFINE_NAMESPACE_RESULT_MODULE(138);
namespace ams::psc {
R_DEFINE_ERROR_RESULT(AlreadyInitialized, 2);
R_DEFINE_ERROR_RESULT(NotInitialized, 3);

View file

@ -17,9 +17,9 @@
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::pwm {
R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pwm, 189);
R_DEFINE_NAMESPACE_RESULT_MODULE(189);
namespace ams::pwm {
R_DEFINE_ERROR_RESULT(InvalidArgument, 2);

View file

@ -20,8 +20,25 @@
namespace ams {
const char *GetResultName(int module, int description);
namespace result::impl {
#if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
struct DummyNameHolder {
static constexpr bool Exists = false;
static constexpr const char *Name = "unknown";
};
template<int Module>
struct ResultNameSpaceExistsImpl {
static constexpr bool Exists = false;
template<int Description>
using NameHolder = DummyNameHolder;
};
#endif
class ResultTraits {
public:
using BaseType = u32;
@ -113,6 +130,10 @@ namespace ams {
static_assert(sizeof(Result) == sizeof(Result::Base::BaseType), "sizeof(Result) == sizeof(Result::Base::BaseType)");
static_assert(std::is_trivially_destructible<Result>::value, "std::is_trivially_destructible<Result>::value");
ALWAYS_INLINE const char *GetResultName(const Result &result) {
return GetResultName(result.GetModule(), result.GetDescription());
}
namespace result::impl {
class ResultInternalAccessor {
@ -230,25 +251,75 @@ namespace ams {
}
/* Macros for defining new results. */
#define R_DEFINE_NAMESPACE_RESULT_MODULE(value) namespace impl::result { static constexpr inline ::ams::result::impl::ResultTraits::BaseType ResultModuleId = value; }
#define R_CURRENT_NAMESPACE_RESULT_MODULE impl::result::ResultModuleId
#if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
#define R_DEFINE_NAMESPACE_RESULT_MODULE(nmspc, value) \
namespace nmspc { \
\
namespace result_impl { \
static constexpr inline ::ams::result::impl::ResultTraits::BaseType ResultModuleId = value; \
\
template<int Description> \
struct ResultNameHolderImpl { static constexpr bool Exists = false; }; \
} \
\
} \
\
namespace ams::result::impl { \
\
template<> struct ResultNameSpaceExistsImpl<value> { \
static constexpr bool Exists = true; \
\
template<int Description> \
using NameHolder = nmspc::result_impl::ResultNameHolderImpl<Description>; \
}; \
\
}
#else
#define R_DEFINE_NAMESPACE_RESULT_MODULE(nmspc, value) \
namespace nmspc { \
\
namespace result_impl { \
static constexpr inline ::ams::result::impl::ResultTraits::BaseType ResultModuleId = value; \
} \
\
}
#endif
#define R_CURRENT_NAMESPACE_RESULT_MODULE result_impl::ResultModuleId
#define R_NAMESPACE_MODULE_ID(nmspc) nmspc::R_CURRENT_NAMESPACE_RESULT_MODULE
#define R_MAKE_NAMESPACE_RESULT(nmspc, desc) static_cast<::ams::Result>(::ams::result::impl::ResultTraits::MakeValue(R_NAMESPACE_MODULE_ID(nmspc), desc))
#define R_DEFINE_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
#if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
#define R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc_start, desc_end) \
template<> struct result_impl::ResultNameHolderImpl<desc_start> { static constexpr bool Exists = true; static constexpr const char *Name = #name; };
#else
#define R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc_start, desc_end)
#endif
#define R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, desc_start, desc_end) \
class Result##name final : public ::ams::result::impl::ResultErrorBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start>, public ::ams::result::impl::ResultErrorRangeBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start, desc_end> {}
#define R_DEFINE_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc_start, desc_end) \
R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, desc_start, desc_end)
#define R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
class Result##name final : public ::ams::result::impl::ResultErrorRangeBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start, desc_end> {}
#define R_DEFINE_ERROR_RESULT(name, desc) R_DEFINE_ERROR_RESULT_IMPL(name, desc, desc)
#define R_DEFINE_ERROR_RANGE(name, start, end) R_DEFINE_ERROR_RESULT_IMPL(name, start, end)
#define R_DEFINE_ABSTRACT_ERROR_RESULT(name, desc) R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc, desc)
#define R_DEFINE_ABSTRACT_ERROR_RANGE(name, start, end) R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, start, end)
#define R_DEFINE_ERROR_RESULT_NS(ns, name, desc) namespace ns { R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, desc, desc); } R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc, desc)
#define R_DEFINE_ERROR_RANGE_NS(ns, name, start, end) namespace ns { R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, start, end); } R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, start, end)
#define R_DEFINE_ABSTRACT_ERROR_RESULT_NS(ns, name, desc) namespace ns { R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc, desc); }
#define R_DEFINE_ABSTRACT_ERROR_RANGE_NS(ns, name, start, end) namespace ns { R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, start, end); }
/* Remove libnx macros, replace with our own. */
#ifndef R_SUCCEEDED
#error "R_SUCCEEDED not defined."
@ -384,14 +455,12 @@ namespace ams::result::impl {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_IS_STRATOSPHERE) && !defined(AMS_ENABLE_DETAILED_ASSERTIONS) && !defined(AMS_BUILD_FOR_DEBUGGING) && !defined(AMS_BUILD_FOR_AUDITING)
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
#elif defined(ATMOSPHERE_OS_HORIZON)
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
#else
#if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) ::ams::result::impl::OnResultAssertion(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, val)
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) ::ams::result::impl::OnResultAbort(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, val)
#else
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) ::ams::result::impl::OnResultAssertion("", 0, "", "", val)
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) ::ams::result::impl::OnResultAbort("", 0, "", "", val)
#endif
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
#endif
/// Evaluates an expression that returns a result, and asserts the result if it would fail.

Some files were not shown because too many files have changed in this diff Show more