mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-28 12:30:17 +00:00
LogManager: implement system module, client api, logging api (#1617)
Some notes: * Unless `atmosphere!enable_log_manager` is true, Nintendo's log manager will be used instead. * This prevents paying memory costs for LM when not enabling logging. * To facilitate this, Atmosphere's log manager has a different program id from Nintendo's. * `atmosphere!enable_htc` implies `atmosphere!enable_log_manager`. * LogManager logs to tma, and the SD card (if `lm!enable_sd_card_logging` is true, which it is by default). * Binary logs are saved to `lm!sd_card_log_output_directory`, which is `atmosphere/binlogs` by default.
This commit is contained in:
parent
a1fb8a91c8
commit
e9849c74cf
94 changed files with 5595 additions and 45 deletions
2
Makefile
2
Makefile
|
@ -110,6 +110,7 @@ dist-no-debug: all
|
|||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420
|
||||
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp
|
||||
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp
|
||||
cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B/exefs.nsp
|
||||
|
@ -119,6 +120,7 @@ dist-no-debug: all
|
|||
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp
|
||||
cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C/exefs.nsp
|
||||
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
|
||||
cp stratosphere/LogManager/LogManager.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp
|
||||
@build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs
|
||||
rm -r atmosphere-$(AMSVER)/stratosphere_romfs
|
||||
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
; Control whether RO should ease its validation of NROs.
|
||||
; (note: this is normally not necessary, and ips patches can be used.)
|
||||
; ease_nro_restriction = u8!0x1
|
||||
[lm]
|
||||
; Control whether lm should log to the SD card.
|
||||
; Note that this setting does nothing when log manager is not enabled.
|
||||
; enable_sd_card_logging = u8!0x1
|
||||
; Control the output directory for SD card logs.
|
||||
; Note that this setting does nothing when log manager is not enabled/sd card logging is not enabled.
|
||||
; sd_card_log_output_directory = str!atmosphere/binlogs
|
||||
; Atmosphere custom settings
|
||||
[atmosphere]
|
||||
; Reboot from fatal automatically after some number of milliseconds.
|
||||
|
@ -53,6 +60,10 @@
|
|||
; Controls whether htc is enabled
|
||||
; 0 = Disabled, 1 = Enabled
|
||||
; enable_htc = u8!0x0
|
||||
; Controls whether atmosphere's log manager is enabled
|
||||
; Note that this setting is ignored (and treated as 1) when htc is enabled.
|
||||
; 0 = Disabled, 1 = Enabled
|
||||
; enable_log_manager = u8!0x0
|
||||
[hbloader]
|
||||
; Controls the size of the homebrew heap when running as applet.
|
||||
; If set to zero, all available applet memory is used as heap.
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
/* libvapours (pulls in util, svc, results). */
|
||||
#include <vapours.hpp>
|
||||
|
||||
/* Libstratosphere diagnostics. */
|
||||
#include <stratosphere/diag.hpp>
|
||||
|
||||
/* Libstratosphere definitions. */
|
||||
#include <stratosphere/ams/impl/ams_system_thread_definitions.hpp>
|
||||
|
||||
|
@ -62,6 +65,7 @@
|
|||
#include <stratosphere/kvdb.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/lr.hpp>
|
||||
#include <stratosphere/lm.hpp>
|
||||
#include <stratosphere/map.hpp>
|
||||
#include <stratosphere/ncm.hpp>
|
||||
#include <stratosphere/nim.hpp>
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace ams::impl {
|
|||
};
|
||||
|
||||
#define AMS_DEFINE_SYSTEM_THREAD(__AMS_THREAD_PRIORITY__, __AMS_MODULE__, __AMS_THREAD_NAME__) \
|
||||
constexpr inline const ::ams::impl::SystemThreadDefinition SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__ = { __AMS_THREAD_PRIORITY__, "ams." # __AMS_MODULE__ "." #__AMS_THREAD_NAME__ }
|
||||
constexpr inline const ::ams::impl::SystemThreadDefinition SystemThreadDefinition_##__AMS_MODULE__##_##__AMS_THREAD_NAME__ = { __AMS_THREAD_PRIORITY__, "ams." # __AMS_MODULE__ "." #__AMS_THREAD_NAME__ }
|
||||
|
||||
/* sm. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(-1, sm, Main);
|
||||
|
@ -69,6 +69,12 @@ namespace ams::impl {
|
|||
/* boot2. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
|
||||
|
||||
/* LogManager. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(10, LogManager, MainThread);
|
||||
AMS_DEFINE_SYSTEM_THREAD(10, lm, IpcServer);
|
||||
AMS_DEFINE_SYSTEM_THREAD(10, lm, Flush);
|
||||
AMS_DEFINE_SYSTEM_THREAD(10, lm, HtcsConnection);
|
||||
|
||||
/* dmnt. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(-3, dmnt, MultiCoreEventManager);
|
||||
AMS_DEFINE_SYSTEM_THREAD(-1, dmnt, CheatDebugEvents);
|
||||
|
@ -167,5 +173,5 @@ namespace ams::impl {
|
|||
|
||||
}
|
||||
|
||||
#define AMS_GET_SYSTEM_THREAD_PRIORITY(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__.priority
|
||||
#define AMS_GET_SYSTEM_THREAD_NAME(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__.name
|
||||
#define AMS_GET_SYSTEM_THREAD_PRIORITY(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition_##__AMS_MODULE__##_##__AMS_THREAD_NAME__.priority
|
||||
#define AMS_GET_SYSTEM_THREAD_NAME(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition_##__AMS_MODULE__##_##__AMS_THREAD_NAME__.name
|
||||
|
|
25
libraries/libstratosphere/include/stratosphere/diag.hpp
Normal file
25
libraries/libstratosphere/include/stratosphere/diag.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/diag/diag_log_types.hpp>
|
||||
#include <stratosphere/diag/diag_log_observer.hpp>
|
||||
#include <stratosphere/diag/impl/diag_impl_log.hpp>
|
||||
#include <stratosphere/diag/diag_log.hpp>
|
||||
#include <stratosphere/diag/diag_sdk_log.hpp>
|
||||
|
||||
#include <stratosphere/diag/impl/diag_utf8_util.hpp>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/impl/diag_impl_structured_log.hpp>
|
||||
|
||||
#if defined(AMS_IMPL_ENABLE_LOG)
|
||||
|
||||
#define AMS_LOG(...) AMS_IMPL_STRUCTURED_LOG_IMPL("", ::ams::diag::LogSeverity_Info, 0, __VA_ARGS__)
|
||||
#define AMS_VLOG(_FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL("", ::ams::diag::LogSeverity_Info, 0, _FMT_, _VL_)
|
||||
#define AMS_PUT(_MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL("", ::ams::diag::LogSeverity_Info, 0, _MSG_, _ML_)
|
||||
|
||||
#define AMS_STRUCTURED_LOG(_MOD_, _SEV_, _VER_, ...) AMS_IMPL_STRUCTURED_LOG_IMPL(_MOD_, _SEV_, _VER_, __VA_ARGS__)
|
||||
#define AMS_STRUCTURED_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL(_MOD_, _SEV_, _VER_, _FMT_, _VL_)
|
||||
#define AMS_STRUCTURED_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL(_MOD_, _SEV_, _VER_, _MSG_, _ML_)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_LOG(...) do { /* ... */ } while (false)
|
||||
#define AMS_VLOG(_FMT_, _VL_) do { /* ... */ } while (false)
|
||||
#define AMS_PUT(_MSG_, _ML_) do { /* ... */ } while (false)
|
||||
|
||||
#define AMS_STRUCTURED_LOG(_MOD_, _SEV_, _VER_, ...) do { /* ... */ } while (false)
|
||||
#define AMS_STRUCTURED_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) do { /* ... */ } while (false)
|
||||
#define AMS_STRUCTURED_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) do { /* ... */ } while (false)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 LogObserver = void (*)(const LogMetaData &meta, const LogBody &body, void *arg);
|
||||
|
||||
struct LogObserverHolder {
|
||||
LogObserver log_observer;
|
||||
LogObserverHolder *next;
|
||||
bool is_registered;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
constexpr inline void InitializeLogObserverHolder(LogObserverHolder *holder, LogObserver observer, void *arg) {
|
||||
holder->log_observer = observer;
|
||||
holder->next = nullptr;
|
||||
holder->is_registered = false;
|
||||
holder->arg = arg;
|
||||
}
|
||||
|
||||
void RegisterLogObserver(LogObserverHolder *holder);
|
||||
void UnregisterLogObserver(LogObserverHolder *holder);
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
enum LogSeverity {
|
||||
LogSeverity_Trace = 0,
|
||||
LogSeverity_Info = 1,
|
||||
LogSeverity_Warn = 2,
|
||||
LogSeverity_Error = 3,
|
||||
LogSeverity_Fatal = 4,
|
||||
};
|
||||
|
||||
struct SourceInfo {
|
||||
int line_number;
|
||||
const char *file_name;
|
||||
const char *function_name;
|
||||
};
|
||||
|
||||
struct LogMetaData {
|
||||
SourceInfo source_info;
|
||||
const char *module_name;
|
||||
LogSeverity severity;
|
||||
int verbosity;
|
||||
bool use_default_locale_charset;
|
||||
void *additional_data;
|
||||
size_t additional_data_size;
|
||||
};
|
||||
|
||||
struct LogBody {
|
||||
const char *message;
|
||||
size_t message_size;
|
||||
bool is_head;
|
||||
bool is_tail;
|
||||
};
|
||||
|
||||
struct LogMessage {
|
||||
const char *fmt;
|
||||
std::va_list *vl;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/impl/diag_impl_structured_log.hpp>
|
||||
|
||||
#if defined(AMS_IMPL_ENABLE_SDK_LOG)
|
||||
|
||||
#define AMS_SDK_LOG(...) AMS_IMPL_STRUCTURED_LOG_IMPL("$", ::ams::diag::LogSeverity_Info, 0, __VA_ARGS__)
|
||||
#define AMS_SDK_VLOG(_FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL("$", ::ams::diag::LogSeverity_Info, 0, _FMT_, _VL_)
|
||||
#define AMS_SDK_PUT(_MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL("$", ::ams::diag::LogSeverity_Info, 0, _MSG_, _ML_)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDK_LOG(...) do { /* ... */ } while (false)
|
||||
#define AMS_SDK_VLOG(_FMT_, _VL_) do { /* ... */ } while (false)
|
||||
#define AMS_SDK_PUT(_MSG_, _ML_) do { /* ... */ } while (false)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
|
||||
#define AMS_IMPL_ENABLE_SDK_LOG
|
||||
#else
|
||||
//#define AMS_IMPL_ENABLE_SDK_LOG
|
||||
#endif
|
||||
|
||||
#if defined(AMS_ENABLE_LOG)
|
||||
#define AMS_IMPL_ENABLE_LOG
|
||||
|
||||
#if defined(AMS_DISABLE_LOG)
|
||||
#error "Incoherent log configuration"
|
||||
#endif
|
||||
#elif defined(AMS_DISABLE_LOG)
|
||||
|
||||
#elif defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
|
||||
#define AMS_IMPL_ENABLE_LOG
|
||||
#else
|
||||
//#define AMS_IMPL_ENABLE_LOG
|
||||
#endif
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::impl {
|
||||
|
||||
void LogImpl(const LogMetaData &meta, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl);
|
||||
void PutImpl(const LogMetaData &meta, const char *msg, size_t msg_size);
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/impl/diag_impl_build_config.hpp>
|
||||
#include <stratosphere/diag/impl/diag_impl_log.hpp>
|
||||
|
||||
#if defined(AMS_IMPL_ENABLE_LOG) || defined(AMS_IMPL_ENABLE_SDK_LOG)
|
||||
|
||||
#define AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_) \
|
||||
(::ams::diag::LogMetaData { \
|
||||
{ __LINE__, __FILE__, AMS_CURRENT_FUNCTION_NAME }, \
|
||||
_MOD_, \
|
||||
_SEV_, \
|
||||
_VER_, \
|
||||
false, \
|
||||
nullptr, \
|
||||
0, \
|
||||
})
|
||||
|
||||
#define AMS_IMPL_STRUCTURED_LOG_IMPL(_MOD_, _SEV_, _VER_, ...) ::ams::diag::impl::LogImpl(AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_), __VA_ARGS__)
|
||||
#define AMS_IMPL_STRUCTURED_VLOG_IMPL(_MOD_, _SEV_, _VER_, _FMT_, _VL_) ::ams::diag::impl::VLogImpl(AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_), _FMT_, _VL_)
|
||||
#define AMS_IMPL_STRUCTURED_PUT_IMPL(_MOD_, _SEV_, _VER_, _MSG_, _ML_) ::ams::diag::impl::PutImpl(AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_), _MSG_, _ML_)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_IMPL_STRUCTURED_LOG_IMPL(_MOD_, _SEV_, _VER_, ...) do { /* ... */ } while (false)
|
||||
#define AMS_IMPL_STRUCTURED_VLOG_IMPL(_MOD_, _SEV_, _VER_, _FMT_, _VL_) do { /* ... */ } while (false)
|
||||
#define AMS_IMPL_STRUCTURED_PUT_IMPL(_MOD_, _SEV_, _VER_, _MSG_, _ML_) do { /* ... */ } while (false)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/impl/diag_impl_build_config.hpp>
|
||||
#include <stratosphere/diag/impl/diag_impl_structured_log.hpp>
|
||||
|
||||
#if defined(AMS_IMPL_ENABLE_SDK_LOG)
|
||||
|
||||
#define AMS_IMPL_STRUCTURED_SDK_LOG(_MOD_, _SEV_, _VER_, ...) AMS_IMPL_STRUCTURED_LOG_IMPL("$" #_MOD_ , ::ams::diag::LogSeverity_##_SEV_, _VER_, __VA_ARGS__)
|
||||
#define AMS_IMPL_STRUCTURED_SDK_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL("$" #_MOD_ , ::ams::diag::LogSeverity_##_SEV_, _VER_, _FMT_, _VL_)
|
||||
#define AMS_IMPL_STRUCTURED_SDK_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL("$" #_MOD_ , ::ams::diag::LogSeverity_##_SEV_, _VER_, _MSG_, _ML_)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_IMPL_STRUCTURED_SDK_LOG(_MOD_, _SEV_, _VER_, ...) do { /* ... */ } while (false)
|
||||
#define AMS_IMPL_STRUCTURED_SDK_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) do { /* ... */ } while (false)
|
||||
#define AMS_IMPL_STRUCTURED_SDK_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) do { /* ... */ } while (false)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
int GetValidSizeAsUtf8String(const char *str, size_t len);
|
||||
|
||||
}
|
21
libraries/libstratosphere/include/stratosphere/lm.hpp
Normal file
21
libraries/libstratosphere/include/stratosphere/lm.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/lm/lm_types.hpp>
|
||||
#include <stratosphere/lm/lm_api.hpp>
|
||||
#include <stratosphere/lm/lm_log_getter.hpp>
|
26
libraries/libstratosphere/include/stratosphere/lm/lm_api.hpp
Normal file
26
libraries/libstratosphere/include/stratosphere/lm/lm_api.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm {
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
void SetDestination(u32 destination);
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm {
|
||||
|
||||
void StartLogging();
|
||||
void StopLogging();
|
||||
|
||||
void GetLog(char *dst, size_t size, s64 *out_write_size, u32 *out_drop_count);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm {
|
||||
|
||||
enum LogDestination {
|
||||
LogDestination_TargetManager = (1 << 0),
|
||||
LogDestination_Uart = (1 << 1),
|
||||
LogDestination_UartIfSleep = (1 << 2),
|
||||
|
||||
LogDestination_All = 0xFFFF,
|
||||
};
|
||||
|
||||
}
|
|
@ -116,12 +116,14 @@ namespace ams::ncm {
|
|||
}
|
||||
|
||||
static const AtmosphereProgramId Mitm;
|
||||
static const AtmosphereProgramId AtmosphereLogManager;
|
||||
};
|
||||
|
||||
inline constexpr const AtmosphereProgramId AtmosphereProgramId::Mitm = { 0x010041544D530000ul };
|
||||
inline constexpr const AtmosphereProgramId AtmosphereProgramId::AtmosphereLogManager = { 0x0100000000000420ul };
|
||||
|
||||
inline constexpr bool IsAtmosphereProgramId(const ProgramId &program_id) {
|
||||
return program_id == AtmosphereProgramId::Mitm;
|
||||
return program_id == AtmosphereProgramId::Mitm || program_id == AtmosphereProgramId::AtmosphereLogManager;
|
||||
}
|
||||
|
||||
inline constexpr bool IsSystemProgramId(const AtmosphereProgramId &program_id) {
|
||||
|
|
|
@ -88,6 +88,17 @@ namespace ams::os {
|
|||
|
||||
using WaitAnyFunction = WaitableHolderType * (*)(WaitableManagerType *);
|
||||
|
||||
class NotBoolButInt {
|
||||
private:
|
||||
int m_value;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE NotBoolButInt(int v) : m_value(v) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE operator int() const { return m_value; }
|
||||
|
||||
explicit operator bool() const = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename... Args> requires (sizeof...(Args) > 0)
|
||||
|
@ -100,4 +111,14 @@ namespace ams::os {
|
|||
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::WaitAny), std::forward<Args>(args)...).second;
|
||||
}
|
||||
|
||||
template<typename... Args> requires (sizeof...(Args) > 0)
|
||||
inline std::pair<WaitableHolderType *, int> TryWaitAny(WaitableManagerType *manager, Args &&... args) {
|
||||
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::TryWaitAny), manager, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args> requires (sizeof...(Args) > 0)
|
||||
inline impl::NotBoolButInt TryWaitAny(Args &&... args) {
|
||||
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::TryWaitAny), std::forward<Args>(args)...).second;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,12 +20,13 @@
|
|||
namespace ams::psc {
|
||||
|
||||
enum PmState {
|
||||
PmState_Awake = 0,
|
||||
PmState_ReadyAwaken = 1,
|
||||
PmState_ReadySleep = 2,
|
||||
PmState_ReadySleepCritical = 3,
|
||||
PmState_ReadyAwakenCritical = 4,
|
||||
PmState_ReadyShutdown = 5,
|
||||
PmState_FullAwake = 0,
|
||||
PmState_MinimumAwake = 1,
|
||||
PmState_SleepReady = 2,
|
||||
PmState_EssentialServicesSleepReady = 3,
|
||||
PmState_EssentialServicesAwake = 4,
|
||||
PmState_ShutdownReady = 5,
|
||||
PmState_Unknown = 6,
|
||||
};
|
||||
|
||||
constexpr inline u32 MaximumDependencyLevels = 20;
|
||||
|
|
|
@ -19,3 +19,6 @@
|
|||
#include <stratosphere/ro/ro_types.hpp>
|
||||
#include <stratosphere/ro/impl/ro_ro_interface.hpp>
|
||||
#include <stratosphere/ro/impl/ro_debug_monitor_interface.hpp>
|
||||
#include <stratosphere/ro/impl/ro_ro_exception_info.hpp>
|
||||
|
||||
#include <stratosphere/rocrt/rocrt.hpp>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::ro::impl {
|
||||
|
||||
struct ExceptionInfo {
|
||||
uintptr_t module_address;
|
||||
size_t module_size;
|
||||
uintptr_t info_offset;
|
||||
size_t info_size;
|
||||
};
|
||||
|
||||
bool GetExceptionInfo(ExceptionInfo *out, uintptr_t pc);
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::rocrt {
|
||||
|
||||
constexpr inline const u32 ModuleHeaderVersion = util::FourCC<'M','O','D','0'>::Code;
|
||||
|
||||
struct ModuleHeader {
|
||||
u32 signature;
|
||||
u32 dynamic_offset;
|
||||
u32 bss_start_offset;
|
||||
u32 bss_end_offset;
|
||||
u32 exception_info_start_offset;
|
||||
u32 exception_info_end_offset;
|
||||
u32 module_offset;
|
||||
};
|
||||
|
||||
struct ModuleHeaderLocation {
|
||||
u32 pad;
|
||||
u32 header_offset;
|
||||
};
|
||||
|
||||
constexpr inline u32 CheckModuleHeaderSignature(const ModuleHeader *header) {
|
||||
if (header->signature == ModuleHeaderVersion) {
|
||||
return header->signature;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline ModuleHeader *GetModuleHeader(const ModuleHeaderLocation *loc) {
|
||||
return reinterpret_cast<ModuleHeader *>(reinterpret_cast<uintptr_t>(loc) + loc->header_offset);
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetDynamicOffset(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
|
||||
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->dynamic_offset;
|
||||
}
|
||||
|
||||
|
||||
constexpr inline uintptr_t GetBssStartAddress(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
|
||||
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->bss_start_offset;
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetBssEndAddress(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
|
||||
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->bss_end_offset;
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetModuleOffset(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
|
||||
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->module_offset;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,9 +17,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <stratosphere/time/time_common.hpp>
|
||||
#include <stratosphere/time/time_posix_time.hpp>
|
||||
#include <stratosphere/time/time_steady_clock_time_point.hpp>
|
||||
#include <stratosphere/time/time_api.hpp>
|
||||
#include <stratosphere/time/time_posix_time.hpp>
|
||||
#include <stratosphere/time/time_calendar_time.hpp>
|
||||
#include <stratosphere/time/time_timezone_api.hpp>
|
||||
#include <stratosphere/time/time_steady_clock_time_point.hpp>
|
||||
#include <stratosphere/time/time_standard_steady_clock.hpp>
|
||||
#include <stratosphere/time/time_standard_user_system_clock.hpp>
|
||||
#include <stratosphere/time/time_standard_network_system_clock.hpp>
|
||||
|
|
|
@ -23,4 +23,15 @@ namespace ams::time::impl::util {
|
|||
|
||||
Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to);
|
||||
|
||||
bool IsLeapYear(int year);
|
||||
bool IsValidDate(int year, int month, int day);
|
||||
|
||||
int GetDaysInMonth(int year, int month);
|
||||
|
||||
int DateToDays(int year, int month, int day);
|
||||
void DaysToDate(int *out_year, int *out_month, int *out_day, int days);
|
||||
|
||||
CalendarTime ToCalendarTimeInUtc(const PosixTime &posix_time);
|
||||
PosixTime ToPosixTimeFromUtc(const CalendarTime &calendar_time);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace ams::time {
|
|||
|
||||
bool IsInitialized();
|
||||
|
||||
bool IsValidDate(int year, int month, int day);
|
||||
|
||||
Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/time/time_common.hpp>
|
||||
|
||||
namespace ams::time {
|
||||
|
||||
struct CalendarTime {
|
||||
s16 year;
|
||||
s8 month;
|
||||
s8 day;
|
||||
s8 hour;
|
||||
s8 minute;
|
||||
s8 second;
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
CalendarTime &operator+=(const TimeSpan &ts);
|
||||
CalendarTime &operator-=(const TimeSpan &ts);
|
||||
|
||||
friend CalendarTime operator+(const CalendarTime &lhs, const TimeSpan &rhs);
|
||||
friend CalendarTime operator-(const CalendarTime &lhs, const TimeSpan &rhs);
|
||||
|
||||
friend TimeSpan operator-(const CalendarTime &lhs, const CalendarTime &rhs);
|
||||
|
||||
constexpr friend bool operator==(const CalendarTime &lhs, const CalendarTime &rhs) { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second; }
|
||||
constexpr friend bool operator!=(const CalendarTime &lhs, const CalendarTime &rhs) { return !(lhs == rhs); }
|
||||
|
||||
constexpr friend bool operator<=(const CalendarTime &lhs, const CalendarTime &rhs) { return !(rhs < lhs); }
|
||||
constexpr friend bool operator>=(const CalendarTime &lhs, const CalendarTime &rhs) { return !(lhs < rhs); }
|
||||
constexpr friend bool operator> (const CalendarTime &lhs, const CalendarTime &rhs) { return rhs < lhs; }
|
||||
|
||||
constexpr friend bool operator< (const CalendarTime &lhs, const CalendarTime &rhs) {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
AMS_ASSERT(lhs.IsValid());
|
||||
AMS_ASSERT(rhs.IsValid());
|
||||
}
|
||||
|
||||
constexpr auto ToUint64 = [] ALWAYS_INLINE_LAMBDA (const time::CalendarTime &time) {
|
||||
return (static_cast<u64>(time.year) << 40) |
|
||||
(static_cast<u64>(time.month) << 32) |
|
||||
(static_cast<u64>(time.day) << 24) |
|
||||
(static_cast<u64>(time.hour) << 16) |
|
||||
(static_cast<u64>(time.minute) << 8) |
|
||||
(static_cast<u64>(time.second) << 0);
|
||||
};
|
||||
|
||||
return ToUint64(lhs) < ToUint64(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == sizeof(u64));
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/time/time_calendar_time.hpp>
|
||||
#include <stratosphere/time/time_posix_time.hpp>
|
||||
|
||||
namespace ams::time {
|
||||
|
||||
CalendarTime ToCalendarTimeInUtc(const PosixTime &posix_time);
|
||||
PosixTime ToPosixTimeFromUtc(const CalendarTime &calendar_time);
|
||||
|
||||
}
|
|
@ -13,9 +13,9 @@
|
|||
* 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 "util/util_uuid_api.hpp"
|
||||
#include "util/util_compression.hpp"
|
||||
#include "util/util_ini.hpp"
|
||||
#include <stratosphere/util/util_uuid_api.hpp>
|
||||
#include <stratosphere/util/util_compression.hpp>
|
||||
#include <stratosphere/util/util_ini.hpp>
|
||||
#include <stratosphere/util/util_singleton_traits.hpp>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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_sdk_mutex.hpp>
|
||||
|
||||
#define AMS_SINGLETON_TRAITS(_CLASSNAME_) \
|
||||
private: \
|
||||
NON_COPYABLE(_CLASSNAME_); \
|
||||
NON_MOVEABLE(_CLASSNAME_); \
|
||||
private: \
|
||||
_CLASSNAME_ (); \
|
||||
public: \
|
||||
static _CLASSNAME_ &GetInstance() { \
|
||||
/* Declare singleton instance variables. */ \
|
||||
static constinit ::ams::util::TypedStorage<_CLASSNAME_> s_singleton_storage; \
|
||||
static constinit ::ams::os::SdkMutex s_singleton_mutex; \
|
||||
static constinit bool s_initialized_singleton = false; \
|
||||
\
|
||||
/* Ensure the instance is created. */ \
|
||||
if (AMS_UNLIKELY(!s_initialized_singleton)) { \
|
||||
std::scoped_lock lk(s_singleton_mutex); \
|
||||
\
|
||||
if (AMS_LIKELY(!s_initialized_singleton)) { \
|
||||
new (::ams::util::GetPointer(s_singleton_storage)) _CLASSNAME_; \
|
||||
s_initialized_singleton = true; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return ::ams::util::GetReference(s_singleton_storage); \
|
||||
}
|
|
@ -40,7 +40,7 @@ namespace ams::boot2 {
|
|||
ncm::SystemProgramId::Vi, /* vi */
|
||||
ncm::SystemProgramId::Pgl, /* pgl */
|
||||
ncm::SystemProgramId::Ns, /* ns */
|
||||
ncm::SystemProgramId::LogManager, /* lm */
|
||||
//ncm::SystemProgramId::LogManager, /* lm */
|
||||
ncm::SystemProgramId::Ppc, /* ppc */
|
||||
ncm::SystemProgramId::Ptm, /* ptm */
|
||||
ncm::SystemProgramId::Hid, /* hid */
|
||||
|
@ -83,7 +83,7 @@ namespace ams::boot2 {
|
|||
ncm::SystemProgramId::Vi, /* vi */
|
||||
ncm::SystemProgramId::Pgl, /* pgl */
|
||||
ncm::SystemProgramId::Ns, /* ns */
|
||||
ncm::SystemProgramId::LogManager, /* lm */
|
||||
//ncm::SystemProgramId::LogManager, /* lm */
|
||||
ncm::SystemProgramId::Ppc, /* ppc */
|
||||
ncm::SystemProgramId::Ptm, /* ptm */
|
||||
ncm::SystemProgramId::Hid, /* hid */
|
||||
|
@ -183,11 +183,22 @@ namespace ams::boot2 {
|
|||
}
|
||||
|
||||
bool IsHtcEnabled() {
|
||||
u8 enable_htc = 1;
|
||||
u8 enable_htc = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(&enable_htc, sizeof(enable_htc), "atmosphere", "enable_htc");
|
||||
return enable_htc != 0;
|
||||
}
|
||||
|
||||
bool IsAtmosphereLogManagerEnabled() {
|
||||
/* If htc is enabled, ams log manager is enabled. */
|
||||
if (IsHtcEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 enable_ams_lm = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(&enable_ams_lm, sizeof(enable_ams_lm), "atmosphere", "enable_log_manager");
|
||||
return enable_ams_lm != 0;
|
||||
}
|
||||
|
||||
bool IsMaintenanceMode() {
|
||||
/* Contact set:sys, retrieve boot!force_maintenance. */
|
||||
if (IsForceMaintenance()) {
|
||||
|
@ -390,7 +401,7 @@ namespace ams::boot2 {
|
|||
/* Check for and forward declare non-atmosphere mitm modules. */
|
||||
DetectAndDeclareFutureMitms();
|
||||
|
||||
/* Device whether to launch tma or htc. */
|
||||
/* Decide whether to launch tma or htc. */
|
||||
if (IsHtcEnabled()) {
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0);
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0);
|
||||
|
@ -398,6 +409,13 @@ namespace ams::boot2 {
|
|||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
|
||||
}
|
||||
|
||||
/* Decide whether to launch atmosphere or nintendo's log manager. */
|
||||
if (IsAtmosphereLogManagerEnabled()) {
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::AtmosphereProgramId::AtmosphereLogManager, ncm::StorageId::None), 0);
|
||||
} else {
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::LogManager, ncm::StorageId::None), 0);
|
||||
}
|
||||
|
||||
/* Launch additional programs. */
|
||||
if (maintenance) {
|
||||
LaunchList(AdditionalMaintenanceLaunchPrograms, NumAdditionalMaintenanceLaunchPrograms);
|
||||
|
|
62
libraries/libstratosphere/source/diag/diag_log.cpp
Normal file
62
libraries/libstratosphere/source/diag/diag_log.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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_log_impl.hpp"
|
||||
|
||||
namespace ams::diag::impl {
|
||||
|
||||
void CallAllLogObserver(const LogMetaData &meta, const LogBody &body);
|
||||
|
||||
namespace {
|
||||
|
||||
struct CallPrintDebugString {
|
||||
void operator()(const LogMetaData &meta, const char *msg, size_t size, bool head, bool tail) {
|
||||
const LogBody body = {
|
||||
.message = msg,
|
||||
.message_size = size,
|
||||
.is_head = head,
|
||||
.is_tail = tail
|
||||
};
|
||||
|
||||
CallAllLogObserver(meta, body);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void LogImpl(const LogMetaData &meta, const char *fmt, ...) {
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
VLogImpl(meta, fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl) {
|
||||
/* 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);
|
||||
|
||||
/* Call log observer. */
|
||||
CallPrintDebugString()(meta, msg_buffer, len, true, true);
|
||||
}
|
||||
|
||||
void PutImpl(const LogMetaData &meta, const char *msg, size_t msg_size) {
|
||||
CallPrintDebugString()(meta, msg, msg_size, true, true);
|
||||
}
|
||||
|
||||
}
|
27
libraries/libstratosphere/source/diag/diag_log_impl.hpp
Normal file
27
libraries/libstratosphere/source/diag/diag_log_impl.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
constexpr inline size_t DebugPrintBufferLength = 0x80;
|
||||
|
||||
}
|
||||
|
||||
}
|
166
libraries/libstratosphere/source/diag/diag_log_observer.cpp
Normal file
166
libraries/libstratosphere/source/diag/diag_log_observer.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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_log_impl.hpp"
|
||||
#include "impl/diag_observer_manager.hpp"
|
||||
#include "impl/diag_print_debug_string.hpp"
|
||||
|
||||
namespace ams::diag {
|
||||
|
||||
namespace impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t DecorationStringLengthMax = 0x61;
|
||||
|
||||
constexpr inline const char *EscapeSequencesForSeverity[] = {
|
||||
"\x1B[90m", /* Dark Gray (Trace) */
|
||||
nullptr, /* None (Info) */
|
||||
"\x1B[33m", /* Yellow (Warn) */
|
||||
"\x1B[31m", /* Red (Error) */
|
||||
"\x1B[41m\x1B[37m", /* White-on-red (Fatal) */
|
||||
};
|
||||
|
||||
constexpr inline const char EscapeSequenceReset[] = "\x1B[0m";
|
||||
|
||||
constexpr inline size_t PrintBufferLength = DecorationStringLengthMax + impl::DebugPrintBufferLength + 1;
|
||||
|
||||
constinit os::SdkMutex g_print_buffer_mutex;
|
||||
constinit char g_print_buffer[PrintBufferLength];
|
||||
|
||||
inline void GetCurrentTime(int *h, int *m, int *s, int *ms) {
|
||||
/* Get the current time. */
|
||||
const auto cur_time = os::GetSystemTick().ToTimeSpan();
|
||||
|
||||
/* Extract fields. */
|
||||
const s64 hours = cur_time.GetHours();
|
||||
const s64 minutes = cur_time.GetMinutes();
|
||||
const s64 seconds = cur_time.GetSeconds();
|
||||
const s64 milliseconds = cur_time.GetMilliSeconds();
|
||||
|
||||
/* Set out fields. */
|
||||
*h = static_cast<int>(hours);
|
||||
*m = static_cast<int>(minutes - hours * 60);
|
||||
*s = static_cast<int>(seconds - minutes * 60);
|
||||
*ms = static_cast<int>(milliseconds - seconds * 1000);
|
||||
}
|
||||
|
||||
void TentativeDefaultLogObserver(const LogMetaData &meta, const LogBody &body, void *) {
|
||||
/* Acquire access to the print buffer */
|
||||
std::scoped_lock lk(g_print_buffer_mutex);
|
||||
|
||||
/* Get the escape sequence. */
|
||||
const char *escape = nullptr;
|
||||
if (LogSeverity_Trace <= meta.severity && meta.severity <= LogSeverity_Fatal) {
|
||||
escape = EscapeSequencesForSeverity[meta.severity];
|
||||
}
|
||||
|
||||
/* Declare message variables. */
|
||||
const char *msg = nullptr;
|
||||
size_t msg_size = 0;
|
||||
|
||||
/* Handle structured logs. */
|
||||
const bool structured = meta.module_name != nullptr && std::strlen(meta.module_name) >= 2;
|
||||
if (escape || structured) {
|
||||
/* Insert timestamp, if head. */
|
||||
if (structured && body.is_head) {
|
||||
/* Get current timestamp. */
|
||||
int hours, minutes, seconds, milliseconds;
|
||||
GetCurrentTime(std::addressof(hours), std::addressof(minutes), std::addressof(seconds), std::addressof(milliseconds));
|
||||
|
||||
/* Print the timestamp/header. */
|
||||
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%s%d:%02d:%02d.%03d [%-5.63s] ", escape ? escape : "", hours, minutes, seconds, milliseconds, meta.module_name[0] == '$' ? meta.module_name + 1 : meta.module_name + 0);
|
||||
|
||||
AMS_AUDIT(msg_size <= DecorationStringLengthMax);
|
||||
} else if (escape) {
|
||||
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%s", escape);
|
||||
}
|
||||
|
||||
/* Determine maximum remaining size. */
|
||||
const size_t max_msg_size = PrintBufferLength - msg_size - (escape ? sizeof(EscapeSequenceReset) - 1 : 0);
|
||||
|
||||
/* Determine printable size. */
|
||||
size_t printable_size = std::min<size_t>(body.message_size, max_msg_size);
|
||||
|
||||
/* Determine newline status. */
|
||||
bool new_line = false;
|
||||
if (body.message_size > 0 && body.message[body.message_size - 1] == '\n') {
|
||||
--printable_size;
|
||||
new_line = true;
|
||||
}
|
||||
|
||||
/* Print the messsage. */
|
||||
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%.*s%s%s", static_cast<int>(printable_size), body.message, escape ? EscapeSequenceReset : "", new_line ? "\n" : "");
|
||||
|
||||
/* Set the message. */
|
||||
msg = g_print_buffer;
|
||||
} else {
|
||||
/* Use the body's message directly. */
|
||||
msg = body.message;
|
||||
msg_size = body.message_size;
|
||||
}
|
||||
|
||||
/* Print the string. */
|
||||
impl::PrintDebugString(msg, msg_size);
|
||||
}
|
||||
|
||||
struct LogObserverContext {
|
||||
const LogMetaData &meta;
|
||||
const LogBody &body;
|
||||
};
|
||||
|
||||
using LogObserverManager = ObserverManagerWithDefaultHolder<LogObserverHolder, LogObserverContext>;
|
||||
|
||||
constinit LogObserverManager g_log_observer_manager(::ams::diag::InitializeLogObserverHolder, TentativeDefaultLogObserver, nullptr);
|
||||
|
||||
}
|
||||
|
||||
void CallAllLogObserver(const LogMetaData &meta, const LogBody &body) {
|
||||
/* Create context. */
|
||||
const LogObserverContext context = { .meta = meta, .body = body };
|
||||
|
||||
/* Invoke the log observer. */
|
||||
g_log_observer_manager.InvokeAllObserver(context, [] (const LogObserverHolder &holder, const LogObserverContext &context) {
|
||||
holder.log_observer(context.meta, context.body, holder.arg);
|
||||
});
|
||||
}
|
||||
|
||||
void ReplaceDefaultLogObserver(LogObserver observer) {
|
||||
/* Get the default observer. */
|
||||
auto *default_holder = std::addressof(g_log_observer_manager.GetDefaultObserverHolder());
|
||||
|
||||
/* Unregister, replace, and re-register. */
|
||||
UnregisterLogObserver(default_holder);
|
||||
InitializeLogObserverHolder(default_holder, observer, nullptr);
|
||||
RegisterLogObserver(default_holder);
|
||||
}
|
||||
|
||||
void ResetDefaultLogObserver() {
|
||||
/* Restore the default observer. */
|
||||
ReplaceDefaultLogObserver(TentativeDefaultLogObserver);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RegisterLogObserver(LogObserverHolder *holder) {
|
||||
impl::g_log_observer_manager.RegisterObserver(holder);
|
||||
}
|
||||
|
||||
void UnregisterLogObserver(LogObserverHolder *holder) {
|
||||
impl::g_log_observer_manager.UnregisterObserver(holder);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
constexpr inline uintptr_t ModulePathLengthOffset = 4;
|
||||
constexpr inline uintptr_t ModulePathOffset = 8;
|
||||
|
||||
}
|
||||
|
||||
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address) {
|
||||
/* Check for null address. */
|
||||
if (address == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get module info. */
|
||||
ro::impl::ExceptionInfo exception_info;
|
||||
if (!ro::impl::GetExceptionInfo(std::addressof(exception_info), address)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Locate the path in the first non-read-execute segment. */
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
auto cur_address = exception_info.module_address;
|
||||
while (cur_address < exception_info.module_address + exception_info.module_size) {
|
||||
if (R_FAILED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address))) {
|
||||
return 0;
|
||||
}
|
||||
if (mem_info.perm != svc::MemoryPermission_ReadExecute) {
|
||||
break;
|
||||
}
|
||||
cur_address += mem_info.size;
|
||||
}
|
||||
|
||||
/* Set output info. */
|
||||
*out_path = reinterpret_cast<const char *>(cur_address + ModulePathOffset);
|
||||
*out_path_length = *reinterpret_cast<const u32 *>(cur_address + ModulePathLengthOffset);
|
||||
*out_module_size = exception_info.module_size;
|
||||
|
||||
return exception_info.module_address;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
template<typename Holder, typename Context>
|
||||
class ObserverManager {
|
||||
NON_COPYABLE(ObserverManager);
|
||||
NON_MOVEABLE(ObserverManager);
|
||||
private:
|
||||
Holder *m_observer_list_head;
|
||||
Holder **m_observer_list_tail;
|
||||
os::ReadWriteLock m_lock;
|
||||
public:
|
||||
constexpr ObserverManager() : m_observer_list_head(nullptr), m_observer_list_tail(std::addressof(m_observer_list_head)), m_lock() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
constexpr ~ObserverManager() {
|
||||
if (std::is_constant_evaluated()) {
|
||||
this->UnregisterAllObserverLocked();
|
||||
} else {
|
||||
this->UnregisterAllObserver();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterObserver(Holder *holder) {
|
||||
/* Acquire a write hold on our lock. */
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
this->RegisterObserverLocked(holder);
|
||||
}
|
||||
|
||||
void UnregisterObserver(Holder *holder) {
|
||||
/* Acquire a write hold on our lock. */
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
/* Check that we can unregister. */
|
||||
AMS_ASSERT(holder->is_registered);
|
||||
|
||||
/* Remove the holder. */
|
||||
if (m_observer_list_head == holder) {
|
||||
m_observer_list_head = holder->next;
|
||||
if (m_observer_list_tail == std::addressof(holder->next)) {
|
||||
m_observer_list_tail = std::addressof(m_observer_list_head);
|
||||
}
|
||||
} else {
|
||||
for (auto *cur = m_observer_list_head; cur != nullptr; cur = cur->next) {
|
||||
if (cur->next == holder) {
|
||||
cur->next = holder->next;
|
||||
|
||||
if (m_observer_list_tail == std::addressof(holder->next)) {
|
||||
m_observer_list_tail = std::addressof(cur->next);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set unregistered. */
|
||||
holder->next = nullptr;
|
||||
}
|
||||
|
||||
void UnregisterAllObserver() {
|
||||
/* Acquire a write hold on our lock. */
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
this->UnregisterAllObserverLocked();
|
||||
}
|
||||
|
||||
void InvokeAllObserver(const Context &context) {
|
||||
/* Use the holder's observer. */
|
||||
InvokeAllObserver(context, [] (const Holder &holder, const Context &context) {
|
||||
holder.observer(context);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Observer>
|
||||
void InvokeAllObserver(const Context &context, Observer observer) {
|
||||
/* Acquire a read hold on our lock. */
|
||||
std::shared_lock lk(m_lock);
|
||||
|
||||
/* Invoke all observers. */
|
||||
for (const auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
|
||||
observer(*holder, context);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
constexpr void RegisterObserverLocked(Holder *holder) {
|
||||
/* Check that we can register. */
|
||||
AMS_ASSERT(!holder->is_registered);
|
||||
|
||||
/* Insert the holder. */
|
||||
*m_observer_list_tail = holder;
|
||||
m_observer_list_tail = std::addressof(holder->next);
|
||||
|
||||
/* Set registered. */
|
||||
holder->next = nullptr;
|
||||
holder->is_registered = true;
|
||||
}
|
||||
|
||||
constexpr void UnregisterAllObserverLocked() {
|
||||
/* Unregister all observers. */
|
||||
for (auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
|
||||
holder->is_registered = false;
|
||||
}
|
||||
|
||||
/* Reset head/fail. */
|
||||
m_observer_list_head = nullptr;
|
||||
m_observer_list_tail = std::addressof(m_observer_list_head);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Holder, typename Context>
|
||||
class ObserverManagerWithDefaultHolder : public ObserverManager<Holder, Context> {
|
||||
private:
|
||||
Holder m_default_holder;
|
||||
public:
|
||||
template<typename Initializer, typename... Args>
|
||||
constexpr ObserverManagerWithDefaultHolder(Initializer initializer, Args &&... args) : ObserverManager<Holder, Context>(), m_default_holder{} {
|
||||
/* Initialize the default observer. */
|
||||
initializer(std::addressof(m_default_holder), std::forward<Args>(args)...);
|
||||
|
||||
/* Register the default observer. */
|
||||
if (std::is_constant_evaluated()) {
|
||||
this->RegisterObserverLocked(std::addressof(m_default_holder));
|
||||
} else {
|
||||
this->RegisterObserver(std::addressof(m_default_holder));
|
||||
}
|
||||
}
|
||||
|
||||
Holder &GetDefaultObserverHolder() {
|
||||
return m_default_holder;
|
||||
}
|
||||
|
||||
const Holder &GetDefaulObservertHolder() const {
|
||||
return m_default_holder;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 PrintDebugString(const char *msg, size_t size);
|
||||
void PrintDebugString(const char *msg);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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_print_debug_string.hpp"
|
||||
|
||||
namespace ams::diag::impl {
|
||||
|
||||
void PrintDebugString(const char *msg, size_t size) {
|
||||
AMS_AUDIT(msg != nullptr || size == 0);
|
||||
|
||||
if (size != 0) {
|
||||
svc::OutputDebugString(msg, size);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintDebugString(const char *msg) {
|
||||
AMS_AUDIT(msg != nullptr);
|
||||
|
||||
PrintDebugString(msg, std::strlen(msg));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
/* TODO: Rename, if we change to e.g. use amsMain? */
|
||||
extern "C" int main(int argc, char **argv);
|
||||
|
||||
namespace ams::diag::impl {
|
||||
|
||||
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address);
|
||||
|
||||
namespace {
|
||||
|
||||
const char *GetLastCharacterPointer(const char *str, size_t len, char c) {
|
||||
for (const char *last = str + len - 1; last >= str; --last) {
|
||||
if (*last == c) {
|
||||
return last;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GetFileNameWithoutExtension(const char **out, size_t *out_size, const char *path, size_t path_length) {
|
||||
const auto last_sep1 = GetLastCharacterPointer(path, path_length, '\\');
|
||||
const auto last_sep2 = GetLastCharacterPointer(path, path_length, '/');
|
||||
const auto ext = GetLastCharacterPointer(path, path_length, '.');
|
||||
|
||||
/* Handle last-separator. */
|
||||
if (last_sep1 && last_sep2) {
|
||||
if (last_sep1 > last_sep2) {
|
||||
*out = last_sep1 + 1;
|
||||
} else {
|
||||
*out = last_sep2 + 1;
|
||||
}
|
||||
} else if (last_sep1) {
|
||||
*out = last_sep1 + 1;
|
||||
} else if (last_sep2) {
|
||||
*out = last_sep2 + 1;
|
||||
} else {
|
||||
*out = path;
|
||||
}
|
||||
|
||||
/* Handle extension. */
|
||||
if (ext && ext >= *out) {
|
||||
*out_size = ext - *out;
|
||||
} else {
|
||||
*out_size = (path + path_length) - *out;
|
||||
}
|
||||
}
|
||||
|
||||
constinit const char *g_process_name = nullptr;
|
||||
constinit size_t g_process_name_size = 0;
|
||||
constinit os::SdkMutex g_process_name_lock;
|
||||
constinit bool g_got_process_name = false;
|
||||
|
||||
void EnsureProcessNameCached() {
|
||||
/* Ensure process name. */
|
||||
if (AMS_UNLIKELY(!g_got_process_name)) {
|
||||
std::scoped_lock lk(g_process_name_lock);
|
||||
if (AMS_LIKELY(!g_got_process_name)) {
|
||||
const char *path;
|
||||
size_t path_length;
|
||||
size_t module_size;
|
||||
|
||||
if (GetModuleInfoForHorizon(std::addressof(path), std::addressof(path_length), std::addressof(module_size), reinterpret_cast<uintptr_t>(main)) != 0) {
|
||||
GetFileNameWithoutExtension(std::addressof(g_process_name), std::addressof(g_process_name_size), path, path_length);
|
||||
AMS_ASSERT(g_process_name_size == 0 || util::VerifyUtf8String(g_process_name, g_process_name_size));
|
||||
} else {
|
||||
g_process_name = "";
|
||||
g_process_name_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GetProcessNamePointer(const char **out, size_t *out_size) {
|
||||
/* Ensure process name is cached. */
|
||||
EnsureProcessNameCached();
|
||||
|
||||
/* Get cached process name. */
|
||||
*out = g_process_name;
|
||||
*out_size = g_process_name_size;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
bool IsHeadOfCharacter(u8 c) {
|
||||
return (c & 0xC0) != 0x80;
|
||||
}
|
||||
|
||||
size_t GetCharacterSize(u8 c) {
|
||||
if ((c & 0x80) == 0) {
|
||||
return 1;
|
||||
} else if ((c & 0xE0) == 0xC0) {
|
||||
return 2;
|
||||
} else if ((c & 0xF0) == 0xE0) {
|
||||
return 3;
|
||||
} else if ((c & 0xF8) == 0xF0) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *FindLastCharacterPointer(const char *str, size_t len) {
|
||||
/* Find the head of the last character. */
|
||||
const char *cur;
|
||||
for (cur = str + len - 1; cur >= str && !IsHeadOfCharacter(*reinterpret_cast<const u8 *>(cur)); --cur) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* Return the last character. */
|
||||
if (AMS_LIKELY(cur >= str)) {
|
||||
return cur;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int GetValidSizeAsUtf8String(const char *str, size_t len) {
|
||||
/* Check pre-condition. */
|
||||
AMS_ASSERT(str != nullptr);
|
||||
|
||||
/* Check if we have no data. */
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the last character pointer. */
|
||||
const auto *last_char_ptr = FindLastCharacterPointer(str, len);
|
||||
if (last_char_ptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get sizes. */
|
||||
const size_t actual_size = (str + len) - last_char_ptr;
|
||||
const size_t last_char_size = GetCharacterSize(*reinterpret_cast<const u8 *>(last_char_ptr));
|
||||
if (last_char_size == 0) {
|
||||
return -1;
|
||||
} else if (actual_size >= last_char_size) {
|
||||
if (actual_size == last_char_size) {
|
||||
return len;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (actual_size >= len) {
|
||||
AMS_ASSERT(actual_size == len);
|
||||
return -1;
|
||||
} else {
|
||||
return static_cast<int>(len - actual_size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -114,14 +114,14 @@ namespace ams::erpt::srv {
|
|||
pm_module.GetEventPointer()->Clear();
|
||||
if (R_SUCCEEDED(pm_module.GetRequest(std::addressof(pm_state), std::addressof(pm_flags)))) {
|
||||
switch (pm_state) {
|
||||
case psc::PmState_Awake:
|
||||
case psc::PmState_ReadyAwaken:
|
||||
case psc::PmState_FullAwake:
|
||||
case psc::PmState_MinimumAwake:
|
||||
Stream::EnableFsAccess(true);
|
||||
break;
|
||||
case psc::PmState_ReadyShutdown:
|
||||
case psc::PmState_ShutdownReady:
|
||||
FinalizeForcedShutdownDetection();
|
||||
[[fallthrough]];
|
||||
case psc::PmState_ReadySleep:
|
||||
case psc::PmState_SleepReady:
|
||||
Stream::EnableFsAccess(false);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -60,16 +60,16 @@ namespace ams::htc::server {
|
|||
|
||||
/* Update sleeping state. */
|
||||
switch (pm_state) {
|
||||
case psc::PmState_Awake:
|
||||
case psc::PmState_FullAwake:
|
||||
if (g_is_asleep) {
|
||||
g_htclow_manager->NotifyAwake();
|
||||
g_is_asleep = false;
|
||||
}
|
||||
break;
|
||||
case psc::PmState_ReadyAwaken:
|
||||
case psc::PmState_ReadySleep:
|
||||
case psc::PmState_ReadySleepCritical:
|
||||
case psc::PmState_ReadyAwakenCritical:
|
||||
case psc::PmState_MinimumAwake:
|
||||
case psc::PmState_SleepReady:
|
||||
case psc::PmState_EssentialServicesSleepReady:
|
||||
case psc::PmState_EssentialServicesAwake:
|
||||
if (!g_is_asleep) {
|
||||
g_htclow_manager->NotifyAsleep();
|
||||
g_is_asleep = true;
|
||||
|
@ -81,16 +81,16 @@ namespace ams::htc::server {
|
|||
|
||||
/* Update suspend state. */
|
||||
switch (pm_state) {
|
||||
case psc::PmState_Awake:
|
||||
case psc::PmState_ReadyAwaken:
|
||||
case psc::PmState_FullAwake:
|
||||
case psc::PmState_MinimumAwake:
|
||||
if (g_is_suspended) {
|
||||
g_htclow_manager->Resume();
|
||||
g_is_suspended = false;
|
||||
}
|
||||
break;
|
||||
case psc::PmState_ReadySleep:
|
||||
case psc::PmState_ReadySleepCritical:
|
||||
case psc::PmState_ReadyAwakenCritical:
|
||||
case psc::PmState_SleepReady:
|
||||
case psc::PmState_EssentialServicesSleepReady:
|
||||
case psc::PmState_EssentialServicesAwake:
|
||||
if (!g_is_suspended) {
|
||||
g_htclow_manager->Suspend();
|
||||
g_is_suspended = true;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::impl {
|
||||
|
||||
enum LogDataChunkKey {
|
||||
LogDataChunkKey_LogSessionBegin = 0,
|
||||
LogDataChunkKey_LogSessionEnd = 1,
|
||||
LogDataChunkKey_TextLog = 2,
|
||||
LogDataChunkKey_LineNumber = 3,
|
||||
LogDataChunkKey_FileName = 4,
|
||||
LogDataChunkKey_FunctionName = 5,
|
||||
LogDataChunkKey_ModuleName = 6,
|
||||
LogDataChunkKey_ThreadName = 7,
|
||||
LogDataChunkKey_LogPacketDropCount = 8,
|
||||
LogDataChunkKey_UserSystemClock = 9,
|
||||
LogDataChunkKey_ProcessName = 10,
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::impl {
|
||||
|
||||
constexpr inline size_t LogPacketHeaderSize = 0x18;
|
||||
|
||||
class LogPacketHeader {
|
||||
private:
|
||||
u64 m_process_id;
|
||||
u64 m_thread_id;
|
||||
u8 m_flags;
|
||||
u8 m_padding;
|
||||
u8 m_severity;
|
||||
u8 m_verbosity;
|
||||
u32 m_payload_size;
|
||||
public:
|
||||
constexpr u64 GetProcessId() const { return m_process_id; }
|
||||
constexpr void SetProcessId(u64 v) { m_process_id = v; }
|
||||
|
||||
constexpr u64 GetThreadId() const { return m_thread_id; }
|
||||
constexpr void SetThreadId(u64 v) { m_thread_id = v; }
|
||||
|
||||
constexpr bool IsHead() const { return (m_flags & (1 << 0)) != 0; }
|
||||
constexpr void SetHead(bool v) { m_flags = (m_flags & ~(1 << 0)) | ((v ? 1 : 0) << 0); }
|
||||
|
||||
constexpr bool IsTail() const { return (m_flags & (1 << 1)) != 0; }
|
||||
constexpr void SetTail(bool v) { m_flags = (m_flags & ~(1 << 1)) | ((v ? 1 : 0) << 1); }
|
||||
|
||||
constexpr bool IsLittleEndian() const { return (m_flags & (1 << 2)) != 0; }
|
||||
constexpr void SetLittleEndian(bool v) { m_flags = (m_flags & ~(1 << 2)) | ((v ? 1 : 0) << 2); }
|
||||
|
||||
constexpr u8 GetSeverity() const { return m_severity; }
|
||||
constexpr void SetSeverity(u8 v) { m_severity = v; }
|
||||
|
||||
constexpr u8 GetVerbosity() const { return m_verbosity; }
|
||||
constexpr void SetVerbosity(u8 v) { m_verbosity = v; }
|
||||
|
||||
constexpr u32 GetPayloadSize() const { return m_payload_size; }
|
||||
constexpr void SetPayloadSize(u32 v) { m_payload_size = v; }
|
||||
};
|
||||
static_assert(sizeof(LogPacketHeader) == LogPacketHeaderSize);
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_packet_transmitter_base.hpp"
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
class LogPacketTransmitter : public LogPacketTransmitterBase {
|
||||
public:
|
||||
LogPacketTransmitter(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail)
|
||||
: LogPacketTransmitterBase(buffer, buffer_size, flush_func, severity, verbosity, process_id, head, tail) { /* ... */ }
|
||||
|
||||
void PushLogSessionBegin() {
|
||||
bool value = true;
|
||||
this->PushDataChunk(LogDataChunkKey_LogSessionBegin, std::addressof(value), sizeof(value));
|
||||
}
|
||||
|
||||
void PushLogSessionEnd() {
|
||||
bool value = true;
|
||||
this->PushDataChunk(LogDataChunkKey_LogSessionEnd, std::addressof(value), sizeof(value));
|
||||
}
|
||||
|
||||
void PushTextLog(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_TextLog, str, len);
|
||||
}
|
||||
|
||||
void PushLineNumber(u32 line) {
|
||||
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(line), sizeof(line));
|
||||
}
|
||||
|
||||
void PushFileName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_FileName, str, len);
|
||||
}
|
||||
|
||||
void PushFunctionName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_FunctionName, str, len);
|
||||
}
|
||||
|
||||
void PushModuleName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_ModuleName, str, len);
|
||||
}
|
||||
|
||||
void PushThreadName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_ThreadName, str, len);
|
||||
}
|
||||
|
||||
void PushLogPacketDropCount(u64 count) {
|
||||
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(count), sizeof(count));
|
||||
}
|
||||
|
||||
void PushUserSystemClock(s64 posix_time) {
|
||||
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(posix_time), sizeof(posix_time));
|
||||
}
|
||||
|
||||
void PushProcessName(const char *str, size_t len) {
|
||||
this->PushDataChunk(LogDataChunkKey_ProcessName, str, len);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_packet_transmitter_base.hpp"
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
LogPacketTransmitterBase::LogPacketTransmitterBase(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(buffer != nullptr);
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), alignof(LogPacketHeader)));
|
||||
AMS_ASSERT(buffer_size >= LogPacketHeaderSize);
|
||||
AMS_ASSERT(flush_func != nullptr);
|
||||
|
||||
/* Construct log packet header. */
|
||||
m_header = std::construct_at(static_cast<LogPacketHeader *>(buffer));
|
||||
|
||||
/* Set fields. */
|
||||
m_start = static_cast<u8 *>(buffer);
|
||||
m_end = m_start + buffer_size;
|
||||
m_payload = m_start + LogPacketHeaderSize;
|
||||
m_current = m_payload;
|
||||
|
||||
m_is_tail = tail;
|
||||
|
||||
m_flush_function = flush_func;
|
||||
|
||||
/* Set header fields. */
|
||||
m_header->SetProcessId(process_id);
|
||||
m_header->SetThreadId(os::GetThreadId(os::GetCurrentThread()));
|
||||
m_header->SetHead(head);
|
||||
m_header->SetLittleEndian(util::IsLittleEndian());
|
||||
m_header->SetSeverity(severity);
|
||||
m_header->SetVerbosity(verbosity);
|
||||
}
|
||||
|
||||
bool LogPacketTransmitterBase::Flush(bool is_tail) {
|
||||
/* Check if we're already flushed. */
|
||||
if (m_current == m_payload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Flush the data. */
|
||||
m_header->SetTail(is_tail);
|
||||
m_header->SetPayloadSize(static_cast<u32>(m_current - m_payload));
|
||||
const auto result = m_flush_function(m_start, static_cast<size_t>(m_current - m_start));
|
||||
m_header->SetHead(false);
|
||||
|
||||
/* Reset. */
|
||||
m_current = m_payload;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t LogPacketTransmitterBase::GetRemainSize() {
|
||||
return static_cast<size_t>(m_end - m_current);
|
||||
}
|
||||
|
||||
size_t LogPacketTransmitterBase::GetPushableDataSize(size_t uleb_size) {
|
||||
const size_t remain = this->GetRemainSize();
|
||||
if (remain < uleb_size + 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t cmp = remain - uleb_size;
|
||||
u64 mask = 0x7F;
|
||||
size_t n;
|
||||
for (n = 1; mask + n < cmp; ++n) {
|
||||
mask |= mask << 7;
|
||||
}
|
||||
|
||||
return cmp - n;
|
||||
}
|
||||
|
||||
size_t LogPacketTransmitterBase::GetRequiredSizeToPushUleb128(u64 v) {
|
||||
/* Determine bytes needed for uleb128 value. */
|
||||
size_t required = 0;
|
||||
do {
|
||||
++required;
|
||||
v >>= 7;
|
||||
} while (v > 0);
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
void LogPacketTransmitterBase::PushUleb128(u64 v) {
|
||||
const u32 Mask = 0x7F;
|
||||
const u32 InverseMask = ~Mask;
|
||||
do {
|
||||
/* Check we're within bounds. */
|
||||
AMS_ASSERT(m_current < m_end);
|
||||
|
||||
/* Write byte. */
|
||||
*(m_current++) = static_cast<u8>(v & Mask) | (((v & InverseMask) != 0) ? 0x80 : 0x00);
|
||||
|
||||
/* Adjust remaining bit range. */
|
||||
v >>= 7;
|
||||
} while (v > 0);
|
||||
}
|
||||
|
||||
void LogPacketTransmitterBase::PushDataChunkImpl(LogDataChunkKey key, const void *data, size_t data_size, bool is_text) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(data != nullptr);
|
||||
|
||||
/* Push as much data as we can, until the chunk is complete. */
|
||||
const u8 *cur = static_cast<const u8 *>(data);
|
||||
const u8 * const end = cur + data_size;
|
||||
const size_t required_key = this->GetRequiredSizeToPushUleb128(key);
|
||||
do {
|
||||
/* Get the pushable size. */
|
||||
size_t pushable_size = this->GetPushableDataSize(required_key);
|
||||
size_t required_size = is_text ? 4 : 1;
|
||||
if (pushable_size < required_size) {
|
||||
this->Flush(false);
|
||||
pushable_size = this->GetPushableDataSize(required_key);
|
||||
}
|
||||
AMS_ASSERT(pushable_size >= required_size);
|
||||
|
||||
/* Determine the current size. */
|
||||
size_t current_size = std::min<size_t>(pushable_size, end - cur);
|
||||
if (is_text) {
|
||||
const auto valid_size = diag::impl::GetValidSizeAsUtf8String(reinterpret_cast<const char *>(cur), current_size);
|
||||
if (valid_size >= 0) {
|
||||
current_size = static_cast<size_t>(valid_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push data. */
|
||||
this->PushUleb128(key);
|
||||
this->PushUleb128(current_size);
|
||||
this->PushData(cur, current_size);
|
||||
|
||||
/* Advance. */
|
||||
cur = cur + current_size;
|
||||
} while (cur < end);
|
||||
|
||||
/* Check that we pushed all the data. */
|
||||
AMS_ASSERT(cur == end);
|
||||
}
|
||||
|
||||
void LogPacketTransmitterBase::PushData(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(size <= this->GetRemainSize());
|
||||
|
||||
/* Push the data. */
|
||||
if (size > 0) {
|
||||
std::memcpy(m_current, data, size);
|
||||
m_current += size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_packet_header.hpp"
|
||||
#include "lm_log_data_chunk.hpp"
|
||||
|
||||
namespace ams::lm::impl {
|
||||
|
||||
class LogPacketTransmitterBase {
|
||||
public:
|
||||
using FlushFunction = bool (*)(const u8 *data, size_t size);
|
||||
private:
|
||||
LogPacketHeader *m_header;
|
||||
u8 *m_start;
|
||||
u8 *m_end;
|
||||
u8 *m_payload;
|
||||
u8 *m_current;
|
||||
bool m_is_tail;
|
||||
FlushFunction m_flush_function;
|
||||
protected:
|
||||
LogPacketTransmitterBase(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail);
|
||||
|
||||
~LogPacketTransmitterBase() {
|
||||
this->Flush(m_is_tail);
|
||||
}
|
||||
|
||||
void PushDataChunk(LogDataChunkKey key, const void *data, size_t size) {
|
||||
this->PushDataChunkImpl(key, data, size, false);
|
||||
}
|
||||
|
||||
void PushDataChunk(LogDataChunkKey key, const char *str, size_t size) {
|
||||
this->PushDataChunkImpl(key, str, size, true);
|
||||
}
|
||||
public:
|
||||
bool Flush(bool is_tail);
|
||||
private:
|
||||
size_t GetRemainSize();
|
||||
size_t GetPushableDataSize(size_t uleb_size);
|
||||
size_t GetRequiredSizeToPushUleb128(u64 v);
|
||||
|
||||
void PushUleb128(u64 v);
|
||||
void PushDataChunkImpl(LogDataChunkKey key, const void *data, size_t size, bool is_text);
|
||||
void PushData(const void *data, size_t size);
|
||||
};
|
||||
|
||||
}
|
121
libraries/libstratosphere/source/lm/lm_api.cpp
Normal file
121
libraries/libstratosphere/source/lm/lm_api.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_remote_log_service.hpp"
|
||||
#include "impl/lm_log_packet_header.hpp"
|
||||
#include "impl/lm_log_packet_transmitter.hpp"
|
||||
|
||||
namespace ams::diag::impl {
|
||||
|
||||
void ReplaceDefaultLogObserver(LogObserver observer);
|
||||
void ResetDefaultLogObserver();
|
||||
|
||||
void GetProcessNamePointer(const char **out, size_t *out_len);
|
||||
|
||||
}
|
||||
|
||||
namespace ams::lm {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit sf::SharedPointer<ILogger> g_logger = nullptr;
|
||||
|
||||
constexpr inline size_t TransmissionBufferSize = 1_KB;
|
||||
constexpr inline size_t TransmissionBufferAlign = alignof(impl::LogPacketHeader);
|
||||
|
||||
constinit os::SdkMutex g_transmission_buffer_mutex;
|
||||
|
||||
alignas(TransmissionBufferAlign) constinit char g_transmission_buffer[TransmissionBufferSize];
|
||||
|
||||
using PushTextFunction = void (impl::LogPacketTransmitter::*)(const char *, size_t);
|
||||
|
||||
bool LogPacketTransmitterFlushFunction(const u8 *data, size_t size) {
|
||||
const Result result = g_logger->Log(sf::InAutoSelectBuffer(data, size));
|
||||
R_ABORT_UNLESS(result);
|
||||
return R_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void InvokePushTextWithUtf8Sanitizing(impl::LogPacketTransmitter &transmitter, PushTextFunction push_func, const char *str) {
|
||||
/* Get the string length. */
|
||||
const auto len = std::strlen(str);
|
||||
|
||||
if (len == 0 || util::VerifyUtf8String(str, len)) {
|
||||
(transmitter.*push_func)(str, len);
|
||||
} else {
|
||||
(transmitter.*push_func)("(Invalid UTF8 string)", sizeof("(Invalid UTF8 string)") - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void LogManagerLogObserver(const diag::LogMetaData &meta, const diag::LogBody &body, void *) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(!meta.use_default_locale_charset);
|
||||
|
||||
/* Acquire access to the transmission buffer. */
|
||||
std::scoped_lock lk(g_transmission_buffer_mutex);
|
||||
|
||||
/* Create transmitter. */
|
||||
impl::LogPacketTransmitter transmitter(g_transmission_buffer, TransmissionBufferSize, LogPacketTransmitterFlushFunction, static_cast<u8>(meta.severity), static_cast<u8>(meta.verbosity), 0, body.is_head, body.is_tail);
|
||||
|
||||
/* Push head-only logs. */
|
||||
if (body.is_head) {
|
||||
transmitter.PushUserSystemClock(os::GetSystemTick().ToTimeSpan().GetSeconds());
|
||||
transmitter.PushLineNumber(meta.source_info.line_number);
|
||||
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushFileName, meta.source_info.file_name);
|
||||
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushFunctionName, meta.source_info.function_name);
|
||||
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushModuleName, meta.module_name);
|
||||
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushThreadName, os::GetThreadNamePointer(os::GetCurrentThread()));
|
||||
|
||||
const char *process_name;
|
||||
size_t process_name_len;
|
||||
diag::impl::GetProcessNamePointer(std::addressof(process_name), std::addressof(process_name_len));
|
||||
|
||||
transmitter.PushProcessName(process_name, process_name_len);
|
||||
}
|
||||
|
||||
/* Push the actual log. */
|
||||
transmitter.PushTextLog(body.message, body.message_size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
AMS_ABORT_UNLESS(g_logger == nullptr);
|
||||
|
||||
/* Create the logger. */
|
||||
{
|
||||
auto service = CreateLogService();
|
||||
R_ABORT_UNLESS(service->OpenLogger(std::addressof(g_logger), sf::ClientProcessId{}));
|
||||
}
|
||||
|
||||
/* Replace the default log observer. */
|
||||
diag::impl::ReplaceDefaultLogObserver(LogManagerLogObserver);
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
AMS_ABORT_UNLESS(g_logger != nullptr);
|
||||
|
||||
/* Reset the default log observer. */
|
||||
diag::impl::ResetDefaultLogObserver();
|
||||
|
||||
/* Destroy the logger. */
|
||||
g_logger = nullptr;
|
||||
}
|
||||
|
||||
void SetDestination(u32 destination) {
|
||||
g_logger->SetDestination(destination);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_remote_log_service.hpp"
|
||||
#include "lm_service_name.hpp"
|
||||
|
||||
namespace ams::lm {
|
||||
|
||||
namespace {
|
||||
|
||||
struct LmRemoteLogServiceTag;
|
||||
using RemoteAllocator = ams::sf::ExpHeapStaticAllocator<0x80, LmRemoteLogServiceTag>;
|
||||
using RemoteObjectFactory = ams::sf::ObjectFactory<typename RemoteAllocator::Policy>;
|
||||
|
||||
class StaticAllocatorInitializer {
|
||||
public:
|
||||
StaticAllocatorInitializer() {
|
||||
RemoteAllocator::Initialize(lmem::CreateOption_None);
|
||||
}
|
||||
} g_static_allocator_initializer;
|
||||
|
||||
}
|
||||
|
||||
Result RemoteLogService::OpenLogger(sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id) {
|
||||
/* Send libnx command. */
|
||||
::Service logger_srv;
|
||||
{
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
R_TRY(serviceDispatch(&m_srv, 0,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &logger_srv,
|
||||
));
|
||||
#undef NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
}
|
||||
|
||||
/* Open logger. */
|
||||
out.SetValue(RemoteObjectFactory::CreateSharedEmplaced<::ams::lm::ILogger, RemoteLogger>(logger_srv));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
sf::SharedPointer<ILogService> CreateLogService() {
|
||||
::Service srv;
|
||||
R_ABORT_UNLESS(sm::GetService(std::addressof(srv), LogServiceName));
|
||||
|
||||
return RemoteObjectFactory::CreateSharedEmplaced<::ams::lm::ILogService, RemoteLogService>(srv);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "sf/lm_i_log_service.hpp"
|
||||
|
||||
namespace ams::lm {
|
||||
|
||||
/* TODO: Real libnx primitives? */
|
||||
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
|
||||
class RemoteLogger {
|
||||
private:
|
||||
::Service m_srv;
|
||||
public:
|
||||
RemoteLogger(::Service &s) : m_srv(s) { /* ... */ }
|
||||
~RemoteLogger() { ::serviceClose(std::addressof(m_srv)); }
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result Log(const sf::InAutoSelectBuffer &message) {
|
||||
return serviceDispatch(&m_srv, 0,
|
||||
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcAutoSelect },
|
||||
.buffers = { { message.GetPointer(), message.GetSize() } },
|
||||
);
|
||||
}
|
||||
|
||||
Result SetDestination(u32 destination) {
|
||||
return serviceDispatchIn(&m_srv, 1, destination);
|
||||
}
|
||||
};
|
||||
static_assert(lm::IsILogger<RemoteLogger>);
|
||||
|
||||
class RemoteLogService {
|
||||
private:
|
||||
::Service m_srv;
|
||||
public:
|
||||
RemoteLogService(::Service &s) : m_srv(s) { /* ... */ }
|
||||
~RemoteLogService() { ::serviceClose(std::addressof(m_srv)); }
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result OpenLogger(sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id);
|
||||
};
|
||||
static_assert(lm::IsILogService<RemoteLogService>);
|
||||
|
||||
#undef NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
|
||||
sf::SharedPointer<ILogService> CreateLogService();
|
||||
|
||||
}
|
25
libraries/libstratosphere/source/lm/lm_service_name.hpp
Normal file
25
libraries/libstratosphere/source/lm/lm_service_name.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm {
|
||||
|
||||
constexpr inline const sm::ServiceName LogServiceName = sm::ServiceName::Encode("lm");
|
||||
|
||||
constexpr inline const sm::ServiceName LogGetterServiceName = sm::ServiceName::Encode("lm:get");
|
||||
|
||||
}
|
24
libraries/libstratosphere/source/lm/sf/lm_i_log_getter.hpp
Normal file
24
libraries/libstratosphere/source/lm/sf/lm_i_log_getter.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
#define AMS_LM_I_LOG_GETTER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, StartLogging, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, StopLogging, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetLog, (const sf::OutAutoSelectBuffer &message, sf::Out<s64> out_size, sf::Out<u32> out_drop_count), (message, out_size, out_drop_count))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::lm, ILogGetter, AMS_LM_I_LOG_GETTER_INTERFACE_INFO)
|
28
libraries/libstratosphere/source/lm/sf/lm_i_log_service.hpp
Normal file
28
libraries/libstratosphere/source/lm/sf/lm_i_log_service.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
#define AMS_LM_I_LOGGER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, Log, (const sf::InAutoSelectBuffer &message), (message)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, SetDestination, (u32 destination), (destination))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::lm, ILogger, AMS_LM_I_LOGGER_INTERFACE_INFO)
|
||||
|
||||
#define AMS_LM_I_LOG_SERVICE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenLogger, (sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id), (out, client_process_id))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::lm, ILogService, AMS_LM_I_LOG_SERVICE_INTERFACE_INFO)
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_custom_sink_buffer.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
bool CustomSinkBuffer::TryPush(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(size <= m_buffer_size);
|
||||
AMS_ASSERT(data || size == 0);
|
||||
|
||||
/* If we have nothing to push, succeed. */
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check that we can push the data. */
|
||||
if (size > m_buffer_size - m_used_buffer_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Push the data. */
|
||||
std::memcpy(m_buffer + m_used_buffer_size, data, size);
|
||||
m_used_buffer_size += size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CustomSinkBuffer::TryFlush() {
|
||||
/* Check that we have data to flush. */
|
||||
if (m_used_buffer_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to flush the data. */
|
||||
if (!m_flush_function(m_buffer, m_used_buffer_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Clear our used size. */
|
||||
m_used_buffer_size = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::srv {
|
||||
|
||||
class CustomSinkBuffer {
|
||||
NON_COPYABLE(CustomSinkBuffer);
|
||||
NON_MOVEABLE(CustomSinkBuffer);
|
||||
public:
|
||||
using FlushFunction = bool (*)(const u8 *buffer, size_t buffer_size);
|
||||
private:
|
||||
u8 *m_buffer;
|
||||
size_t m_buffer_size;
|
||||
size_t m_used_buffer_size;
|
||||
FlushFunction m_flush_function;
|
||||
public:
|
||||
constexpr explicit CustomSinkBuffer(void *buffer, size_t buffer_size, FlushFunction f) : m_buffer(static_cast<u8 *>(buffer)), m_buffer_size(buffer_size), m_used_buffer_size(0), m_flush_function(f) {
|
||||
AMS_ASSERT(m_buffer != nullptr);
|
||||
AMS_ASSERT(m_buffer_size > 0);
|
||||
AMS_ASSERT(m_flush_function != nullptr);
|
||||
}
|
||||
|
||||
bool TryPush(const void *data, size_t size);
|
||||
bool TryFlush();
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_event_log_transmitter.hpp"
|
||||
#include "lm_log_buffer.hpp"
|
||||
#include "../impl/lm_log_packet_header.hpp"
|
||||
#include "../impl/lm_log_packet_transmitter.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t TransmitterBufferAlign = alignof(impl::LogPacketHeader);
|
||||
constexpr inline size_t TransmitterBufferSizeForSessionInfo = impl::LogPacketHeaderSize + 3;
|
||||
constexpr inline size_t TransmitterBufferSizeForDropCount = impl::LogPacketHeaderSize + 10;
|
||||
|
||||
bool DefaultFlushFunction(const u8 *data, size_t size) {
|
||||
return LogBuffer::GetDefaultInstance().TryPush(data, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EventLogTransmitter &EventLogTransmitter::GetDefaultInstance() {
|
||||
static constinit EventLogTransmitter s_default_event_log_transmitter(DefaultFlushFunction);
|
||||
return s_default_event_log_transmitter;
|
||||
}
|
||||
|
||||
bool EventLogTransmitter::PushLogSessionBegin(u64 process_id) {
|
||||
/* Create a transmitter. */
|
||||
alignas(TransmitterBufferAlign) u8 buffer[TransmitterBufferSizeForSessionInfo];
|
||||
impl::LogPacketTransmitter transmitter(buffer, sizeof(buffer), m_flush_function, static_cast<u8>(diag::LogSeverity_Info), 0, process_id, true, true);
|
||||
|
||||
/* Push session begin. */
|
||||
transmitter.PushLogSessionBegin();
|
||||
|
||||
/* Flush the data. */
|
||||
const bool success = transmitter.Flush(true);
|
||||
|
||||
/* Update drop count. */
|
||||
if (!success) {
|
||||
++m_log_packet_drop_count;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EventLogTransmitter::PushLogSessionEnd(u64 process_id) {
|
||||
/* Create a transmitter. */
|
||||
alignas(TransmitterBufferAlign) u8 buffer[TransmitterBufferSizeForSessionInfo];
|
||||
impl::LogPacketTransmitter transmitter(buffer, sizeof(buffer), m_flush_function, static_cast<u8>(diag::LogSeverity_Info), 0, process_id, true, true);
|
||||
|
||||
/* Push session end. */
|
||||
transmitter.PushLogSessionEnd();
|
||||
|
||||
/* Flush the data. */
|
||||
const bool success = transmitter.Flush(true);
|
||||
|
||||
/* Update drop count. */
|
||||
if (!success) {
|
||||
++m_log_packet_drop_count;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EventLogTransmitter::PushLogPacketDropCountIfExists() {
|
||||
/* Acquire exclusive access. */
|
||||
std::scoped_lock lk(m_log_packet_drop_count_mutex);
|
||||
|
||||
/* If we have no dropped packets, nothing to push. */
|
||||
if (m_log_packet_drop_count == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Create a transmitter. */
|
||||
alignas(TransmitterBufferAlign) u8 buffer[TransmitterBufferSizeForDropCount];
|
||||
impl::LogPacketTransmitter transmitter(buffer, sizeof(buffer), m_flush_function, static_cast<u8>(diag::LogSeverity_Info), 0, 0, true, true);
|
||||
|
||||
/* Push log packet drop count. */
|
||||
transmitter.PushLogPacketDropCount(m_log_packet_drop_count);
|
||||
|
||||
/* Flush the data. */
|
||||
const bool success = transmitter.Flush(true);
|
||||
|
||||
/* Update drop count. */
|
||||
if (success) {
|
||||
m_log_packet_drop_count = 0;
|
||||
} else {
|
||||
++m_log_packet_drop_count;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void EventLogTransmitter::IncreaseLogPacketDropCount() {
|
||||
/* Acquire exclusive access. */
|
||||
std::scoped_lock lk(m_log_packet_drop_count_mutex);
|
||||
|
||||
/* Increase the dropped packet count. */
|
||||
++m_log_packet_drop_count;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::srv {
|
||||
|
||||
class EventLogTransmitter {
|
||||
NON_COPYABLE(EventLogTransmitter);
|
||||
NON_MOVEABLE(EventLogTransmitter);
|
||||
public:
|
||||
using FlushFunction = bool (*)(const u8 *data, size_t size);
|
||||
private:
|
||||
FlushFunction m_flush_function;
|
||||
size_t m_log_packet_drop_count;
|
||||
os::SdkMutex m_log_packet_drop_count_mutex;
|
||||
public:
|
||||
constexpr explicit EventLogTransmitter(FlushFunction f) : m_flush_function(f), m_log_packet_drop_count(0), m_log_packet_drop_count_mutex() {
|
||||
AMS_ASSERT(f != nullptr);
|
||||
}
|
||||
|
||||
static EventLogTransmitter &GetDefaultInstance();
|
||||
|
||||
bool PushLogSessionBegin(u64 process_id);
|
||||
bool PushLogSessionEnd(u64 process_id);
|
||||
|
||||
bool PushLogPacketDropCountIfExists();
|
||||
|
||||
void IncreaseLogPacketDropCount();
|
||||
};
|
||||
|
||||
}
|
179
libraries/libstratosphere/source/lm/srv/lm_flush_thread.cpp
Normal file
179
libraries/libstratosphere/source/lm/srv/lm_flush_thread.cpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_server_proxy.hpp"
|
||||
#include "lm_sd_card_logger.hpp"
|
||||
#include "lm_log_buffer.hpp"
|
||||
#include "lm_event_log_transmitter.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
bool IsSleeping();
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(os::ThreadStackAlignment) u8 g_flush_thread_stack[8_KB];
|
||||
|
||||
constinit u8 g_fs_heap[32_KB];
|
||||
constinit lmem::HeapHandle g_fs_heap_handle;
|
||||
|
||||
constinit os::ThreadType g_flush_thread;
|
||||
|
||||
os::Event g_stop_event(os::EventClearMode_ManualClear);
|
||||
os::Event g_sd_logging_event(os::EventClearMode_ManualClear);
|
||||
os::Event g_host_connection_event(os::EventClearMode_ManualClear);
|
||||
|
||||
constinit std::unique_ptr<fs::IEventNotifier> g_sd_card_detection_event_notifier;
|
||||
os::SystemEvent g_sd_card_detection_event;
|
||||
|
||||
void *AllocateForFs(size_t size) {
|
||||
return lmem::AllocateFromExpHeap(g_fs_heap_handle, size);
|
||||
}
|
||||
|
||||
void DeallocateForFs(void *ptr, size_t size) {
|
||||
return lmem::FreeToExpHeap(g_fs_heap_handle, ptr);
|
||||
}
|
||||
|
||||
void HostConnectionObserver(bool is_connected) {
|
||||
/* Update the host connection event. */
|
||||
if (is_connected) {
|
||||
g_host_connection_event.Signal();
|
||||
} else {
|
||||
g_host_connection_event.Clear();
|
||||
|
||||
/* Potentially cancel the log buffer push. */
|
||||
if (!g_sd_logging_event.TryWait()) {
|
||||
LogBuffer::GetDefaultInstance().CancelPush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdLoggingObserver(bool is_available) {
|
||||
/* Update the SD card logging event. */
|
||||
if (is_available) {
|
||||
g_sd_logging_event.Signal();
|
||||
} else {
|
||||
g_sd_logging_event.Clear();
|
||||
|
||||
/* Potentially cancel the log buffer push. */
|
||||
if (!g_host_connection_event.TryWait()) {
|
||||
LogBuffer::GetDefaultInstance().CancelPush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForFlush() {
|
||||
while (true) {
|
||||
/* Wait for something to be signaled. */
|
||||
os::WaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase(), g_sd_card_detection_event.GetBase());
|
||||
|
||||
/* If we're stopping, no flush. */
|
||||
if (g_stop_event.TryWait()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If host is connected/we're logging to sd, flush. */
|
||||
if (g_host_connection_event.TryWait() || g_sd_logging_event.TryWait()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the sd card is newly inserted, flush. */
|
||||
if (g_sd_card_detection_event.TryWait()) {
|
||||
g_sd_card_detection_event.Clear();
|
||||
|
||||
if (fs::IsSdCardInserted()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlushThreadFunction(void *) {
|
||||
/* Disable abort. */
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
/* Create fs heap. */
|
||||
g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap, sizeof(g_fs_heap), lmem::CreateOption_None);
|
||||
AMS_ABORT_UNLESS(g_fs_heap_handle != nullptr);
|
||||
|
||||
/* Set fs allocation functions. */
|
||||
fs::SetAllocator(AllocateForFs, DeallocateForFs);
|
||||
|
||||
/* Create SD card detection event notifier. */
|
||||
R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(g_sd_card_detection_event_notifier)));
|
||||
R_ABORT_UNLESS(g_sd_card_detection_event_notifier->BindEvent(g_sd_card_detection_event.GetBase(), os::EventClearMode_ManualClear));
|
||||
|
||||
/* Set connection observers. */
|
||||
SdCardLogger::GetInstance().SetLoggingObserver(SdLoggingObserver);
|
||||
LogServerProxy::GetInstance().SetConnectionObserver(HostConnectionObserver);
|
||||
|
||||
/* Do flush loop. */
|
||||
do {
|
||||
if (LogBuffer::GetDefaultInstance().Flush()) {
|
||||
EventLogTransmitter::GetDefaultInstance().PushLogPacketDropCountIfExists();
|
||||
}
|
||||
} while (WaitForFlush());
|
||||
|
||||
/* Clear connection observer. */
|
||||
LogServerProxy::GetInstance().SetConnectionObserver(nullptr);
|
||||
|
||||
/* Finalize the SD card logger. */
|
||||
SdCardLogger::GetInstance().Finalize();
|
||||
SdCardLogger::GetInstance().SetLoggingObserver(nullptr);
|
||||
|
||||
/* Destroy the fs heap. */
|
||||
lmem::DestroyExpHeap(g_fs_heap_handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsFlushAvailable() {
|
||||
/* If we're sleeping, we can't flush. */
|
||||
if (IsSleeping()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to wait for an event. */
|
||||
if (os::TryWaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase()) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return whether we're not stopping. */
|
||||
return !os::TryWaitEvent(g_stop_event.GetBase());
|
||||
}
|
||||
|
||||
void InitializeFlushThread() {
|
||||
/* Create the flush thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_flush_thread), FlushThreadFunction, nullptr, g_flush_thread_stack, sizeof(g_flush_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, Flush)));
|
||||
os::SetThreadNamePointer(std::addressof(g_flush_thread), AMS_GET_SYSTEM_THREAD_NAME(lm, Flush));
|
||||
|
||||
/* Clear the stop event. */
|
||||
g_stop_event.Clear();
|
||||
|
||||
/* Start the flush thread. */
|
||||
os::StartThread(std::addressof(g_flush_thread));
|
||||
}
|
||||
|
||||
void FinalizeFlushThread() {
|
||||
/* Signal the flush thread to stop. */
|
||||
g_stop_event.Signal();
|
||||
|
||||
/* Wait for the flush thread to stop. */
|
||||
os::WaitThread(std::addressof(g_flush_thread));
|
||||
os::DestroyThread(std::addressof(g_flush_thread));
|
||||
}
|
||||
|
||||
}
|
145
libraries/libstratosphere/source/lm/srv/lm_ipc_server.cpp
Normal file
145
libraries/libstratosphere/source/lm/srv/lm_ipc_server.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "../lm_service_name.hpp"
|
||||
#include "lm_log_service_impl.hpp"
|
||||
#include "lm_log_getter.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t LogSessionCountMax = 42;
|
||||
constexpr inline size_t LogGetterSessionCountMax = 1;
|
||||
|
||||
constexpr inline size_t SessionCountMax = LogSessionCountMax + LogGetterSessionCountMax;
|
||||
|
||||
constexpr inline size_t PortCountMax = 2;
|
||||
|
||||
struct ServerManagerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x400;
|
||||
static constexpr size_t MaxDomains = 31;
|
||||
static constexpr size_t MaxDomainObjects = 61;
|
||||
};
|
||||
|
||||
using ServerManager = sf::hipc::ServerManager<PortCountMax, ServerManagerOptions, SessionCountMax>;
|
||||
|
||||
constinit util::TypedStorage<ServerManager> g_server_manager_storage;
|
||||
constinit ServerManager *g_server_manager = nullptr;
|
||||
|
||||
constinit util::TypedStorage<psc::PmModule> g_pm_module_storage;
|
||||
constinit psc::PmModule *g_pm_module;
|
||||
constinit os::WaitableHolderType g_pm_module_holder;
|
||||
|
||||
constexpr const psc::PmModuleId PmModuleDependencies[] = { psc::PmModuleId_TmaHostIo, psc::PmModuleId_Fs };
|
||||
|
||||
/* Service objects. */
|
||||
constinit sf::UnmanagedServiceObject<lm::ILogService, lm::srv::LogServiceImpl> g_log_service_object;
|
||||
constinit sf::UnmanagedServiceObject<lm::ILogGetter, lm::srv::LogGetter> g_log_getter_service_object;
|
||||
|
||||
constinit std::atomic<bool> g_is_sleeping = false;
|
||||
|
||||
}
|
||||
|
||||
bool IsSleeping() {
|
||||
return g_is_sleeping;
|
||||
}
|
||||
|
||||
void InitializeIpcServer() {
|
||||
/* Check that we're not already initialized. */
|
||||
AMS_ABORT_UNLESS(g_server_manager == nullptr);
|
||||
AMS_ABORT_UNLESS(g_pm_module == nullptr);
|
||||
|
||||
/* Create and initialize the psc module. */
|
||||
g_pm_module = util::ConstructAt(g_pm_module_storage);
|
||||
R_ABORT_UNLESS(g_pm_module->Initialize(psc::PmModuleId_Lm, PmModuleDependencies, util::size(PmModuleDependencies), os::EventClearMode_ManualClear));
|
||||
|
||||
/* Create the psc module waitable holder. */
|
||||
os::InitializeWaitableHolder(std::addressof(g_pm_module_holder), g_pm_module->GetEventPointer()->GetBase());
|
||||
os::SetWaitableHolderUserData(std::addressof(g_pm_module_holder), psc::PmModuleId_Lm);
|
||||
|
||||
/* Create the server manager. */
|
||||
g_server_manager = util::ConstructAt(g_server_manager_storage);
|
||||
|
||||
/* Add the pm module holder. */
|
||||
g_server_manager->AddUserWaitableHolder(std::addressof(g_pm_module_holder));
|
||||
|
||||
/* Create services. */
|
||||
R_ABORT_UNLESS(g_server_manager->RegisterObjectForServer(g_log_service_object.GetShared(), LogServiceName, LogSessionCountMax));
|
||||
R_ABORT_UNLESS(g_server_manager->RegisterObjectForServer(g_log_getter_service_object.GetShared(), LogGetterServiceName, LogGetterSessionCountMax));
|
||||
|
||||
/* Start the server manager. */
|
||||
g_server_manager->ResumeProcessing();
|
||||
}
|
||||
|
||||
void LoopIpcServer() {
|
||||
/* Check that we're initialized. */
|
||||
AMS_ABORT_UNLESS(g_server_manager != nullptr);
|
||||
|
||||
/* Loop forever, servicing the server. */
|
||||
auto prev_state = psc::PmState_Unknown;
|
||||
while (true) {
|
||||
/* Get the next signaled holder. */
|
||||
auto *signaled_holder = g_server_manager->WaitSignaled();
|
||||
if (signaled_holder != std::addressof(g_pm_module_holder)) {
|
||||
/* If ipc, process. */
|
||||
R_ABORT_UNLESS(g_server_manager->Process(signaled_holder));
|
||||
} else {
|
||||
/* If pm module, clear the event. */
|
||||
g_pm_module->GetEventPointer()->Clear();
|
||||
g_server_manager->AddUserWaitableHolder(signaled_holder);
|
||||
|
||||
/* Get the power state. */
|
||||
psc::PmState pm_state;
|
||||
psc::PmFlagSet pm_flags;
|
||||
R_ABORT_UNLESS(g_pm_module->GetRequest(std::addressof(pm_state), std::addressof(pm_flags)));
|
||||
|
||||
/* Handle the power state. */
|
||||
if (prev_state == psc::PmState_EssentialServicesAwake && pm_state == psc::PmState_MinimumAwake) {
|
||||
g_is_sleeping = false;
|
||||
} else if (prev_state == psc::PmState_MinimumAwake && pm_state == psc::PmState_SleepReady) {
|
||||
g_is_sleeping = true;
|
||||
} else if (pm_state == psc::PmState_ShutdownReady) {
|
||||
g_is_sleeping = true;
|
||||
}
|
||||
|
||||
/* Set the previous state. */
|
||||
prev_state = pm_state;
|
||||
|
||||
/* Acknowledge the state transition. */
|
||||
R_ABORT_UNLESS(g_pm_module->Acknowledge(pm_state, ResultSuccess()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StopIpcServer() {
|
||||
/* Check that we're initialized. */
|
||||
AMS_ABORT_UNLESS(g_server_manager != nullptr);
|
||||
|
||||
/* Stop the server manager. */
|
||||
g_server_manager->RequestStopProcessing();
|
||||
}
|
||||
|
||||
void FinalizeIpcServer() {
|
||||
/* Check that we're initialized. */
|
||||
AMS_ABORT_UNLESS(g_server_manager != nullptr);
|
||||
|
||||
/* Destroy the server manager. */
|
||||
std::destroy_at(g_server_manager);
|
||||
g_server_manager = nullptr;
|
||||
}
|
||||
|
||||
}
|
207
libraries/libstratosphere/source/lm/srv/lm_log_buffer.cpp
Normal file
207
libraries/libstratosphere/source/lm/srv/lm_log_buffer.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_buffer.hpp"
|
||||
#include "lm_log_server_proxy.hpp"
|
||||
#include "lm_sd_card_logger.hpp"
|
||||
#include "lm_time_util.hpp"
|
||||
#include "lm_log_packet_parser.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
void UpdateUserSystemClock(const u8 *data, size_t size) {
|
||||
/* Get the current time. */
|
||||
const time::PosixTime current_time = GetCurrentTime();
|
||||
|
||||
/* Get the base time. */
|
||||
s64 base_time = current_time.value - os::GetSystemTick().ToTimeSpan().GetSeconds();
|
||||
|
||||
/* Modify the message timestamp. */
|
||||
LogPacketParser::ParsePacket(data, size, [](const impl::LogPacketHeader &header, const void *payload, size_t payload_size, void *arg) -> bool {
|
||||
/* Check that we're a header message. */
|
||||
if (!header.IsHead()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Find the timestamp data chunk. */
|
||||
return LogPacketParser::ParseDataChunk(payload, payload_size, [](impl::LogDataChunkKey key, const void *chunk, size_t chunk_size, void *arg) -> bool {
|
||||
/* Convert the argument. */
|
||||
const s64 *p_base_time = static_cast<const s64 *>(arg);
|
||||
|
||||
/* Modify user system clock. */
|
||||
if (key == impl::LogDataChunkKey_UserSystemClock) {
|
||||
/* Get the time from the chunk. */
|
||||
s64 time;
|
||||
AMS_ASSERT(chunk_size == sizeof(time));
|
||||
std::memcpy(std::addressof(time), chunk, chunk_size);
|
||||
|
||||
/* Add the base time. */
|
||||
time += *p_base_time;
|
||||
|
||||
/* Update the time in the chunk. */
|
||||
std::memcpy(const_cast<void *>(chunk), std::addressof(time), sizeof(time));
|
||||
}
|
||||
|
||||
return true;
|
||||
}, arg);
|
||||
}, std::addressof(base_time));
|
||||
}
|
||||
|
||||
bool DefaultFlushFunction(const u8 *data, size_t size) {
|
||||
/* Update clock. */
|
||||
static constinit bool s_is_user_system_clock_updated = false;
|
||||
if (!s_is_user_system_clock_updated) {
|
||||
UpdateUserSystemClock(data, size);
|
||||
s_is_user_system_clock_updated = true;
|
||||
}
|
||||
|
||||
/* Send the message. */
|
||||
const bool tma_success = LogServerProxy::GetInstance().Send(data, size);
|
||||
const bool sd_success = SdCardLogger::GetInstance().Write(data, size);
|
||||
const bool is_success = tma_success || sd_success;
|
||||
|
||||
/* If we succeeded, wipe the current time. */
|
||||
s_is_user_system_clock_updated &= !is_success;
|
||||
|
||||
return is_success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogBuffer &LogBuffer::GetDefaultInstance() {
|
||||
static constinit u8 s_default_buffers[128_KB * 2];
|
||||
static constinit LogBuffer s_default_log_buffer(s_default_buffers, sizeof(s_default_buffers), DefaultFlushFunction);
|
||||
|
||||
return s_default_log_buffer;
|
||||
}
|
||||
|
||||
void LogBuffer::CancelPush() {
|
||||
/* Acquire exclusive access to the push buffer. */
|
||||
std::scoped_lock lk(m_push_buffer_mutex);
|
||||
|
||||
/* Cancel any pending pushes. */
|
||||
if (m_push_ready_wait_count > 0) {
|
||||
m_push_canceled = true;
|
||||
m_cv_push_ready.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
bool LogBuffer::PushImpl(const void *data, size_t size, bool blocking) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(size <= m_buffer_size);
|
||||
AMS_ASSERT(data != nullptr || size == 0);
|
||||
|
||||
/* Check that we have data to push. */
|
||||
if (size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Wait to be able to push. */
|
||||
u8 *dst;
|
||||
{
|
||||
/* Acquire exclusive access to the push buffer. */
|
||||
std::scoped_lock lk(m_push_buffer_mutex);
|
||||
|
||||
/* Wait for enough space to be available. */
|
||||
while (size > m_buffer_size - m_push_buffer->m_stored_size) {
|
||||
/* Only block if we're allowed to. */
|
||||
if (!blocking) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait for push to be ready. */
|
||||
{
|
||||
++m_push_ready_wait_count;
|
||||
m_cv_push_ready.Wait(m_push_buffer_mutex);
|
||||
--m_push_ready_wait_count;
|
||||
}
|
||||
|
||||
/* Check if push was canceled. */
|
||||
if (m_push_canceled) {
|
||||
if (m_push_ready_wait_count == 0) {
|
||||
m_push_canceled = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the destination. */
|
||||
dst = m_push_buffer->m_head + m_push_buffer->m_stored_size;
|
||||
|
||||
/* Advance the push buffer. */
|
||||
m_push_buffer->m_stored_size += size;
|
||||
++m_push_buffer->m_reference_count;
|
||||
}
|
||||
|
||||
/* Copy the data to the push buffer. */
|
||||
std::memcpy(dst, data, size);
|
||||
|
||||
/* Close our push buffer reference, and signal that we can flush. */
|
||||
{
|
||||
/* Acquire exclusive access to the push buffer. */
|
||||
std::scoped_lock lk(m_push_buffer_mutex);
|
||||
|
||||
/* If there are no pending pushes, signal that we can flush. */
|
||||
if ((--m_push_buffer->m_reference_count) == 0) {
|
||||
m_cv_flush_ready.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LogBuffer::FlushImpl(bool blocking) {
|
||||
/* Acquire exclusive access to the flush buffer. */
|
||||
std::scoped_lock lk(m_flush_buffer_mutex);
|
||||
|
||||
/* If we don't have data to flush, wait for us to have data. */
|
||||
if (m_flush_buffer->m_stored_size == 0) {
|
||||
/* Acquire exclusive access to the push buffer. */
|
||||
std::scoped_lock lk(m_push_buffer_mutex);
|
||||
|
||||
/* Wait for there to be pushed data. */
|
||||
while (m_push_buffer->m_stored_size == 0 || m_push_buffer->m_reference_count != 0) {
|
||||
/* Only block if we're allowed to. */
|
||||
if (!blocking) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait for us to be ready to flush. */
|
||||
m_cv_flush_ready.Wait(m_push_buffer_mutex);
|
||||
}
|
||||
|
||||
/* Swap the push buffer and the flush buffer pointers. */
|
||||
std::swap(m_push_buffer, m_flush_buffer);
|
||||
|
||||
/* Signal that we can push. */
|
||||
m_cv_push_ready.Broadcast();
|
||||
}
|
||||
|
||||
/* Flush any data. */
|
||||
if (!m_flush_function(m_flush_buffer->m_head, m_flush_buffer->m_stored_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reset the flush buffer. */
|
||||
m_flush_buffer->m_stored_size = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
73
libraries/libstratosphere/source/lm/srv/lm_log_buffer.hpp
Normal file
73
libraries/libstratosphere/source/lm/srv/lm_log_buffer.hpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::srv {
|
||||
|
||||
class LogBuffer {
|
||||
NON_COPYABLE(LogBuffer);
|
||||
NON_MOVEABLE(LogBuffer);
|
||||
private:
|
||||
struct BufferInfo {
|
||||
u8 *m_head;
|
||||
size_t m_stored_size;
|
||||
size_t m_reference_count;
|
||||
};
|
||||
public:
|
||||
using FlushFunction = bool (*)(const u8 *data, size_t size);
|
||||
private:
|
||||
BufferInfo m_buffers[2];
|
||||
BufferInfo *m_push_buffer;
|
||||
BufferInfo *m_flush_buffer;
|
||||
size_t m_buffer_size;
|
||||
FlushFunction m_flush_function;
|
||||
os::SdkMutex m_push_buffer_mutex;
|
||||
os::SdkMutex m_flush_buffer_mutex;
|
||||
os::SdkConditionVariable m_cv_push_ready;
|
||||
os::SdkConditionVariable m_cv_flush_ready;
|
||||
bool m_push_canceled;
|
||||
size_t m_push_ready_wait_count;
|
||||
public:
|
||||
constexpr explicit LogBuffer(void *buffer, size_t buffer_size, FlushFunction f)
|
||||
: m_buffers{}, m_push_buffer(m_buffers + 0), m_flush_buffer(m_buffers + 1),
|
||||
m_buffer_size(buffer_size / 2), m_flush_function(f), m_push_buffer_mutex{},
|
||||
m_flush_buffer_mutex{}, m_cv_push_ready{}, m_cv_flush_ready{},
|
||||
m_push_canceled(false), m_push_ready_wait_count(0)
|
||||
{
|
||||
AMS_ASSERT(buffer != nullptr);
|
||||
AMS_ASSERT(buffer_size > 0);
|
||||
AMS_ASSERT(f != nullptr);
|
||||
|
||||
m_buffers[0].m_head = static_cast<u8 *>(buffer);
|
||||
m_buffers[1].m_head = static_cast<u8 *>(buffer) + (buffer_size / 2);
|
||||
}
|
||||
|
||||
static LogBuffer &GetDefaultInstance();
|
||||
|
||||
bool Push(const void *data, size_t size) { return this->PushImpl(data, size, true); }
|
||||
bool TryPush(const void *data, size_t size) { return this->PushImpl(data, size, false); }
|
||||
|
||||
void CancelPush();
|
||||
|
||||
bool Flush() { return this->FlushImpl(true); }
|
||||
bool TryFlush() { return this->FlushImpl(false); }
|
||||
private:
|
||||
bool PushImpl(const void *data, size_t size, bool blocking);
|
||||
bool FlushImpl(bool blocking);
|
||||
};
|
||||
|
||||
}
|
46
libraries/libstratosphere/source/lm/srv/lm_log_getter.cpp
Normal file
46
libraries/libstratosphere/source/lm/srv/lm_log_getter.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_getter.hpp"
|
||||
#include "lm_log_getter_impl.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
extern bool g_is_logging_to_custom_sink;
|
||||
|
||||
Result LogGetter::StartLogging() {
|
||||
g_is_logging_to_custom_sink = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LogGetter::StopLogging() {
|
||||
g_is_logging_to_custom_sink = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LogGetter::GetLog(const sf::OutAutoSelectBuffer &message, sf::Out<s64> out_size, sf::Out<u32> out_drop_count) {
|
||||
/* Try to flush logs. */
|
||||
if (LogGetterImpl::GetBuffer().TryFlush()) {
|
||||
*out_size = LogGetterImpl::GetLog(message.GetPointer(), message.GetSize(), out_drop_count.GetPointer());
|
||||
} else {
|
||||
/* Otherwise, we got no data. */
|
||||
*out_size = 0;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
30
libraries/libstratosphere/source/lm/srv/lm_log_getter.hpp
Normal file
30
libraries/libstratosphere/source/lm/srv/lm_log_getter.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "../sf/lm_i_log_getter.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
class LogGetter {
|
||||
public:
|
||||
Result StartLogging();
|
||||
Result StopLogging();
|
||||
Result GetLog(const sf::OutAutoSelectBuffer &message, sf::Out<s64> out_size, sf::Out<u32> out_drop_count);
|
||||
};
|
||||
static_assert(lm::IsILogGetter<LogGetter>);
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_getter_impl.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
CustomSinkBuffer &LogGetterImpl::GetBuffer() {
|
||||
static constinit u8 s_buffer[32_KB];
|
||||
static constinit CustomSinkBuffer s_custom_sink_buffer(s_buffer, sizeof(s_buffer), FlushFunction);
|
||||
return s_custom_sink_buffer;
|
||||
}
|
||||
|
||||
s64 LogGetterImpl::GetLog(void *buffer, size_t buffer_size, u32 *out_drop_count) {
|
||||
/* Check pre-condition. */
|
||||
AMS_ASSERT(buffer != nullptr);
|
||||
|
||||
/* Determine how much we can get. */
|
||||
size_t min_size = s_buffer_size;
|
||||
if (buffer_size < s_buffer_size) {
|
||||
min_size = buffer_size;
|
||||
IncreaseLogPacketDropCount();
|
||||
}
|
||||
|
||||
/* Get the data. */
|
||||
std::memcpy(buffer, s_message, min_size);
|
||||
|
||||
/* Set output drop count. */
|
||||
*out_drop_count = s_log_packet_drop_count;
|
||||
s_log_packet_drop_count = 0;
|
||||
|
||||
return min_size;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_custom_sink_buffer.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
class LogGetterImpl {
|
||||
NON_COPYABLE(LogGetterImpl);
|
||||
NON_MOVEABLE(LogGetterImpl);
|
||||
private:
|
||||
static constinit inline const u8 *s_message = nullptr;
|
||||
static constinit inline size_t s_buffer_size = 0;
|
||||
static constinit inline size_t s_log_packet_drop_count = 0;
|
||||
private:
|
||||
LogGetterImpl();
|
||||
public:
|
||||
static CustomSinkBuffer &GetBuffer();
|
||||
static s64 GetLog(void *buffer, size_t buffer_size, u32 *out_drop_count);
|
||||
|
||||
static void IncreaseLogPacketDropCount() { ++s_log_packet_drop_count; }
|
||||
private:
|
||||
static bool FlushFunction(const u8 *buffer, size_t buffer_size) {
|
||||
s_message = buffer;
|
||||
s_buffer_size = buffer_size;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
276
libraries/libstratosphere/source/lm/srv/lm_log_packet_parser.cpp
Normal file
276
libraries/libstratosphere/source/lm/srv/lm_log_packet_parser.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_packet_parser.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
const u8 *ParseUleb128(u64 *out, const u8 *cur, const u8 *end) {
|
||||
u64 value = 0;
|
||||
size_t shift = 0;
|
||||
while (cur < end && shift + 7 <= BITSIZEOF(u64)) {
|
||||
value |= static_cast<u64>(*cur & 0x7F) << shift;
|
||||
if ((*cur & 0x80) == 0) {
|
||||
*out = value;
|
||||
return cur;
|
||||
}
|
||||
|
||||
++cur;
|
||||
shift += 7;
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool LogPacketParser::ParsePacket(const void *buffer, size_t buffer_size, ParsePacketCallback callback, void *arg) {
|
||||
const u8 *cur = static_cast<const u8 *>(buffer);
|
||||
const u8 *end = cur + buffer_size;
|
||||
|
||||
while (cur < end) {
|
||||
/* Check that we can parse a header. */
|
||||
size_t remaining_size = end - cur;
|
||||
if (remaining_size < sizeof(impl::LogPacketHeader)) {
|
||||
AMS_ASSERT(remaining_size >= sizeof(impl::LogPacketHeader));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the header. */
|
||||
impl::LogPacketHeader header;
|
||||
std::memcpy(std::addressof(header), cur, sizeof(header));
|
||||
|
||||
/* Advance past the header. */
|
||||
cur += sizeof(header);
|
||||
|
||||
/* Check that we can parse the payload. */
|
||||
const auto payload_size = header.GetPayloadSize();
|
||||
remaining_size = end - cur;
|
||||
if (remaining_size < payload_size) {
|
||||
AMS_ASSERT(remaining_size >= payload_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Invoke the callback. */
|
||||
if (!callback(header, cur, payload_size, arg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
cur += payload_size;
|
||||
}
|
||||
|
||||
/* Check that we parsed all the data. */
|
||||
AMS_ASSERT(cur == end);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LogPacketParser::ParseDataChunk(const void *payload, size_t payload_size, ParseDataChunkCallback callback, void *arg) {
|
||||
const u8 *cur = static_cast<const u8 *>(payload);
|
||||
const u8 *end = cur + payload_size;
|
||||
|
||||
while (cur < end) {
|
||||
/* Get the key. */
|
||||
u64 key;
|
||||
const auto key_last = ParseUleb128(std::addressof(key), cur, end);
|
||||
if (key_last >= end) {
|
||||
return false;
|
||||
}
|
||||
cur = key_last + 1;
|
||||
|
||||
/* Get the size. */
|
||||
u64 size;
|
||||
const auto size_last = ParseUleb128(std::addressof(size), cur, end);
|
||||
if (size_last >= end) {
|
||||
return false;
|
||||
}
|
||||
cur = size_last + 1;
|
||||
|
||||
/* If we're in bounds, invoke the callback. */
|
||||
if (cur + size <= end && !callback(static_cast<impl::LogDataChunkKey>(key), cur, size, arg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cur += size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LogPacketParser::FindDataChunk(const void **out, size_t *out_size, impl::LogDataChunkKey key, const void *buffer, size_t buffer_size) {
|
||||
/* Create context for iteration. */
|
||||
struct FindDataChunkContext {
|
||||
const void *chunk;
|
||||
size_t chunk_size;
|
||||
bool found;
|
||||
impl::LogDataChunkKey key;
|
||||
} context = { nullptr, 0, false, key };
|
||||
|
||||
/* Find the chunk. */
|
||||
LogPacketParser::ParsePacket(buffer, buffer_size, [](const impl::LogPacketHeader &header, const void *payload, size_t payload_size, void *arg) -> bool {
|
||||
/* If the header isn't a header packet, continue. */
|
||||
if (!header.IsHead()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return LogPacketParser::ParseDataChunk(payload, payload_size, [](impl::LogDataChunkKey cur_key, const void *chunk, size_t chunk_size, void *arg) -> bool {
|
||||
/* Get the context. */
|
||||
auto *context = static_cast<FindDataChunkContext *>(arg);
|
||||
|
||||
/* Check if we found the desired key. */
|
||||
if (context->key == cur_key) {
|
||||
context->chunk = chunk;
|
||||
context->chunk_size = chunk_size;
|
||||
context->found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Otherwise, continue. */
|
||||
return true;
|
||||
}, arg);
|
||||
}, std::addressof(context));
|
||||
|
||||
/* Write the chunk we found. */
|
||||
if (context.found) {
|
||||
*out = context.chunk;
|
||||
*out_size = context.chunk_size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t LogPacketParser::ParseModuleName(char *dst, size_t dst_size, const void *buffer, size_t buffer_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(dst_size > 0);
|
||||
AMS_ASSERT(buffer != nullptr);
|
||||
AMS_ASSERT(buffer_size > 0);
|
||||
|
||||
/* Find the relevant data chunk. */
|
||||
const void *chunk;
|
||||
size_t chunk_size;
|
||||
const bool found = LogPacketParser::FindDataChunk(std::addressof(chunk), std::addressof(chunk_size), impl::LogDataChunkKey_ModuleName, buffer, buffer_size);
|
||||
if (!found || chunk_size == 0) {
|
||||
dst[0] = '\x00';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy as much of the module name as we can. */
|
||||
const size_t copy_size = std::min(chunk_size, dst_size - 1);
|
||||
std::memcpy(dst, chunk, copy_size);
|
||||
dst[copy_size] = '\x00';
|
||||
|
||||
return chunk_size;
|
||||
}
|
||||
|
||||
void LogPacketParser::ParseTextLogWithContext(const void *buffer, size_t buffer_size, ParseTextLogCallback callback, void *arg) {
|
||||
/* Declare context for inter-call storage. */
|
||||
struct PreviousPacketContext {
|
||||
u64 process_id;
|
||||
u64 thread_id;
|
||||
size_t carry_size;
|
||||
bool ends_with_text_log;
|
||||
};
|
||||
static constinit PreviousPacketContext s_previous_packet_context = {};
|
||||
|
||||
/* Get the packet header. */
|
||||
auto *header = static_cast<const impl::LogPacketHeader *>(buffer);
|
||||
auto *payload = static_cast<const char *>(buffer) + impl::LogPacketHeaderSize;
|
||||
auto payload_size = buffer_size - impl::LogPacketHeaderSize;
|
||||
|
||||
/* Determine if the packet is a continuation. */
|
||||
const bool is_continuation = !header->IsHead() && header->GetProcessId() == s_previous_packet_context.process_id && header->GetThreadId() == s_previous_packet_context.thread_id;
|
||||
|
||||
/* Require that the packet be a header or a continuation. */
|
||||
if (!header->IsHead() && !is_continuation) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the packet is a continuation, handle the leftover data. */
|
||||
if (is_continuation && s_previous_packet_context.carry_size > 0) {
|
||||
/* Invoke the callback on what we can. */
|
||||
const size_t sendable = std::min(s_previous_packet_context.carry_size, payload_size);
|
||||
if (s_previous_packet_context.ends_with_text_log) {
|
||||
callback(payload, sendable, arg);
|
||||
}
|
||||
|
||||
/* Advance the leftover data. */
|
||||
s_previous_packet_context.carry_size -= sendable;
|
||||
payload += sendable;
|
||||
payload_size -= sendable;
|
||||
}
|
||||
|
||||
/* If we've sent the whole payload, we're done. */
|
||||
if (payload_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse the payload. */
|
||||
size_t carry_size = 0;
|
||||
bool ends_with_text_log = false;
|
||||
{
|
||||
const u8 *cur = reinterpret_cast<const u8 *>(payload);
|
||||
const u8 *end = cur + payload_size;
|
||||
|
||||
while (cur < end) {
|
||||
/* Get the key. */
|
||||
u64 key;
|
||||
const auto key_last = ParseUleb128(std::addressof(key), cur, end);
|
||||
if (key_last >= end) {
|
||||
break;
|
||||
}
|
||||
cur = key_last + 1;
|
||||
|
||||
/* Get the size. */
|
||||
u64 size;
|
||||
const auto size_last = ParseUleb128(std::addressof(size), cur, end);
|
||||
if (size_last >= end) {
|
||||
break;
|
||||
}
|
||||
cur = size_last + 1;
|
||||
|
||||
/* Process the data. */
|
||||
const bool is_text_log = static_cast<impl::LogDataChunkKey>(key) == impl::LogDataChunkKey_TextLog;
|
||||
const size_t remaining = end - cur;
|
||||
|
||||
if (size >= remaining) {
|
||||
carry_size = size - remaining;
|
||||
ends_with_text_log = is_text_log;
|
||||
}
|
||||
|
||||
if (is_text_log) {
|
||||
const size_t sendable_size = std::min(size, remaining);
|
||||
callback(reinterpret_cast<const char *>(cur), sendable_size, arg);
|
||||
}
|
||||
|
||||
cur += size;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the packet isn't a tail packet, update the context. */
|
||||
if (!header->IsTail()) {
|
||||
s_previous_packet_context.process_id = header->GetProcessId();
|
||||
s_previous_packet_context.thread_id = header->GetThreadId();
|
||||
s_previous_packet_context.carry_size = carry_size;
|
||||
s_previous_packet_context.ends_with_text_log = ends_with_text_log;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "../impl/lm_log_data_chunk.hpp"
|
||||
#include "../impl/lm_log_packet_header.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
class LogPacketParser {
|
||||
public:
|
||||
using ParsePacketCallback = bool (*)(const impl::LogPacketHeader &header, const void *payload, size_t payload_size, void *arg);
|
||||
using ParseDataChunkCallback = bool (*)(impl::LogDataChunkKey key, const void *chunk, size_t chunk_size, void *arg);
|
||||
using ParseTextLogCallback = void (*)(const char *txt, size_t size, void *arg);
|
||||
public:
|
||||
static bool ParsePacket(const void *buffer, size_t buffer_size, ParsePacketCallback callback, void *arg);
|
||||
static bool ParseDataChunk(const void *payload, size_t payload_size, ParseDataChunkCallback callback, void *arg);
|
||||
|
||||
static bool FindDataChunk(const void **out, size_t *out_size, impl::LogDataChunkKey key, const void *buffer, size_t buffer_size);
|
||||
|
||||
static size_t ParseModuleName(char *dst, size_t dst_size, const void *buffer, size_t buffer_size);
|
||||
|
||||
static void ParseTextLogWithContext(const void *buffer, size_t buffer_size, ParseTextLogCallback callback, void *arg);
|
||||
};
|
||||
|
||||
}
|
218
libraries/libstratosphere/source/lm/srv/lm_log_server_proxy.cpp
Normal file
218
libraries/libstratosphere/source/lm/srv/lm_log_server_proxy.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_server_proxy.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const char PortName[] = "iywys@$LogManager";
|
||||
constexpr inline const int HtcsSessionCountMax = 2;
|
||||
constexpr inline const TimeSpan PollingInterval = TimeSpan::FromSeconds(1);
|
||||
|
||||
constinit u8 g_htcs_heap_buffer[2_KB];
|
||||
|
||||
constexpr inline const int InvalidHtcsSocket = -1;
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsValidHtcsSocket(int socket) {
|
||||
return socket >= 0;
|
||||
}
|
||||
|
||||
static_assert(!IsValidHtcsSocket(InvalidHtcsSocket));
|
||||
|
||||
bool IsHtcEnabled() {
|
||||
u8 enable_htc = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(&enable_htc, sizeof(enable_htc), "atmosphere", "enable_htc");
|
||||
return enable_htc != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogServerProxy::LogServerProxy() : m_cv_connected(), m_stop_event(os::EventClearMode_ManualClear), m_connection_mutex(), m_observer_mutex(), m_server_socket(InvalidHtcsSocket), m_client_socket(InvalidHtcsSocket), m_connection_observer(nullptr) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void LogServerProxy::Start() {
|
||||
/* Create thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), [](void *_this) { static_cast<LogServerProxy *>(_this)->LoopAuto(); }, this, m_thread_stack, sizeof(m_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, HtcsConnection)));
|
||||
|
||||
/* Set thread name pointer. */
|
||||
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(lm, HtcsConnection));
|
||||
|
||||
/* Clear stop event. */
|
||||
m_stop_event.Clear();
|
||||
|
||||
/* Start thread. */
|
||||
os::StartThread(std::addressof(m_thread));
|
||||
}
|
||||
|
||||
void LogServerProxy::Stop() {
|
||||
/* Signal to connection thread to stop. */
|
||||
m_stop_event.Signal();
|
||||
|
||||
/* Close client socket. */
|
||||
if (const int client_socket = m_client_socket; client_socket >= 0) {
|
||||
htcs::Close(client_socket);
|
||||
}
|
||||
|
||||
/* Close server socket. */
|
||||
if (const int server_socket = m_server_socket; server_socket >= 0) {
|
||||
htcs::Close(server_socket);
|
||||
}
|
||||
|
||||
/* Wait for the connection thread to exit. */
|
||||
os::WaitThread(std::addressof(m_thread));
|
||||
os::DestroyThread(std::addressof(m_thread));
|
||||
}
|
||||
|
||||
bool LogServerProxy::IsConnected() {
|
||||
/* Return whether there's a valid client socket. */
|
||||
return IsValidHtcsSocket(m_client_socket);
|
||||
}
|
||||
|
||||
void LogServerProxy::SetConnectionObserver(ConnectionObserver observer) {
|
||||
/* Acquire exclusive access to observer data. */
|
||||
std::scoped_lock lk(m_observer_mutex);
|
||||
|
||||
/* Set the observer. */
|
||||
m_connection_observer = observer;
|
||||
}
|
||||
|
||||
bool LogServerProxy::Send(const u8 *data, size_t size) {
|
||||
/* Send as much data as we can, until it's all send. */
|
||||
size_t offset = 0;
|
||||
while (this->IsConnected() && offset < size) {
|
||||
/* Try to send the remaining data. */
|
||||
if (const auto result = htcs::Send(m_client_socket, data + offset, size - offset, 0); result >= 0) {
|
||||
/* Advance. */
|
||||
offset += static_cast<size_t>(result);
|
||||
} else {
|
||||
/* We failed to send data, shutdown the socket. */
|
||||
htcs::Shutdown(m_client_socket, htcs::HTCS_SHUT_RDWR);
|
||||
|
||||
/* Wait a second, before returning to the caller. */
|
||||
os::SleepThread(TimeSpan::FromSeconds(1));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether we sent all the data. */
|
||||
AMS_ASSERT(offset <= size);
|
||||
return offset == size;
|
||||
}
|
||||
|
||||
void LogServerProxy::LoopAuto() {
|
||||
/* If we're not using htcs, there's nothing to do. */
|
||||
if (!IsHtcEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check that we have enough working memory. */
|
||||
const auto working_memory_size = htcs::GetWorkingMemorySize(HtcsSessionCountMax);
|
||||
AMS_ABORT_UNLESS(working_memory_size <= sizeof(g_htcs_heap_buffer));
|
||||
|
||||
/* Initialize htcs for the duration that we loop. */
|
||||
htcs::InitializeForDisableDisconnectionEmulation(g_htcs_heap_buffer, working_memory_size);
|
||||
ON_SCOPE_EXIT { htcs::Finalize(); };
|
||||
|
||||
/* Setup socket address. */
|
||||
htcs::SockAddrHtcs server_address;
|
||||
server_address.family = htcs::HTCS_AF_HTCS;
|
||||
server_address.peer_name = htcs::GetPeerNameAny();
|
||||
std::strcpy(server_address.port_name.name, PortName);
|
||||
|
||||
/* Manage htcs connections until a stop is requested. */
|
||||
do {
|
||||
/* Create a server socket. */
|
||||
const auto server_socket = htcs::Socket();
|
||||
if (!IsValidHtcsSocket(server_socket)) {
|
||||
continue;
|
||||
}
|
||||
m_server_socket = server_socket;
|
||||
|
||||
/* Ensure we cleanup the server socket when done. */
|
||||
ON_SCOPE_EXIT {
|
||||
htcs::Close(server_socket);
|
||||
m_server_socket = InvalidHtcsSocket;
|
||||
};
|
||||
|
||||
/* Bind to the server socket. */
|
||||
if (htcs::Bind(server_socket, std::addressof(server_address)) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Listen on the server socket. */
|
||||
if (htcs::Listen(server_socket, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Loop on clients, until we're asked to stop. */
|
||||
while (!m_stop_event.TryWait()) {
|
||||
/* Accept a client socket. */
|
||||
const auto client_socket = htcs::Accept(server_socket, nullptr);
|
||||
if (!IsValidHtcsSocket(client_socket)) {
|
||||
break;
|
||||
}
|
||||
m_client_socket = client_socket;
|
||||
|
||||
/* Note that we're connected. */
|
||||
this->InvokeConnectionObserver(true);
|
||||
this->SignalConnection();
|
||||
|
||||
/* Ensure we cleanup the client socket when done. */
|
||||
ON_SCOPE_EXIT {
|
||||
htcs::Close(client_socket);
|
||||
m_client_socket = InvalidHtcsSocket;
|
||||
|
||||
this->InvokeConnectionObserver(false);
|
||||
};
|
||||
|
||||
/* Receive data (and do nothing with it), so long as we're connected. */
|
||||
u8 v;
|
||||
while (htcs::Recv(client_socket, std::addressof(v), sizeof(v), 0) == sizeof(v)) { /* ... */ }
|
||||
}
|
||||
} while (!m_stop_event.TimedWait(PollingInterval));
|
||||
}
|
||||
|
||||
void LogServerProxy::SignalConnection() {
|
||||
/* Acquire exclusive access to observer data. */
|
||||
std::scoped_lock lk(m_connection_mutex);
|
||||
|
||||
/* Broadcast to our connected cv. */
|
||||
m_cv_connected.Broadcast();
|
||||
}
|
||||
|
||||
void LogServerProxy::InvokeConnectionObserver(bool connected) {
|
||||
/* Acquire exclusive access to observer data. */
|
||||
std::scoped_lock lk(m_observer_mutex);
|
||||
|
||||
/* If we have an observer, observe the connection state. */
|
||||
if (m_connection_observer) {
|
||||
m_connection_observer(connected);
|
||||
}
|
||||
}
|
||||
|
||||
void StartLogServerProxy() {
|
||||
LogServerProxy::GetInstance().Start();
|
||||
}
|
||||
|
||||
void StopLogServerProxy() {
|
||||
LogServerProxy::GetInstance().Stop();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::srv {
|
||||
|
||||
class LogServerProxy {
|
||||
AMS_SINGLETON_TRAITS(LogServerProxy);
|
||||
public:
|
||||
using ConnectionObserver = void (*)(bool connected);
|
||||
private:
|
||||
alignas(os::ThreadStackAlignment) u8 m_thread_stack[4_KB];
|
||||
os::ThreadType m_thread;
|
||||
os::SdkConditionVariable m_cv_connected;
|
||||
os::Event m_stop_event;
|
||||
os::SdkMutex m_connection_mutex;
|
||||
os::SdkMutex m_observer_mutex;
|
||||
std::atomic<int> m_server_socket;
|
||||
std::atomic<int> m_client_socket;
|
||||
ConnectionObserver m_connection_observer;
|
||||
public:
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
bool IsConnected();
|
||||
|
||||
void SetConnectionObserver(ConnectionObserver observer);
|
||||
|
||||
bool Send(const u8 *data, size_t size);
|
||||
private:
|
||||
void LoopAuto();
|
||||
void SignalConnection();
|
||||
void InvokeConnectionObserver(bool connected);
|
||||
};
|
||||
|
||||
void StartLogServerProxy();
|
||||
void StopLogServerProxy();
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_service_impl.hpp"
|
||||
#include "lm_logger_impl.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
struct LoggerImplAllocatorTag;
|
||||
using LoggerAllocator = ams::sf::ExpHeapStaticAllocator<3_KB, LoggerImplAllocatorTag>;
|
||||
using LoggerObjectFactory = ams::sf::ObjectFactory<typename LoggerAllocator::Policy>;
|
||||
|
||||
class StaticAllocatorInitializer {
|
||||
public:
|
||||
StaticAllocatorInitializer() {
|
||||
LoggerAllocator::Initialize(lmem::CreateOption_None);
|
||||
}
|
||||
} g_static_allocator_initializer;
|
||||
|
||||
}
|
||||
|
||||
Result LogServiceImpl::OpenLogger(sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id) {
|
||||
/* Open logger. */
|
||||
out.SetValue(LoggerObjectFactory::CreateSharedEmplaced<::ams::lm::ILogger, LoggerImpl>(this, client_process_id.GetValue()));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "../sf/lm_i_log_service.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
class LogServiceImpl {
|
||||
public:
|
||||
Result OpenLogger(sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id);
|
||||
};
|
||||
static_assert(lm::IsILogService<LogServiceImpl>);
|
||||
|
||||
}
|
147
libraries/libstratosphere/source/lm/srv/lm_logger_impl.cpp
Normal file
147
libraries/libstratosphere/source/lm/srv/lm_logger_impl.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_logger_impl.hpp"
|
||||
#include "lm_event_log_transmitter.hpp"
|
||||
#include "lm_log_buffer.hpp"
|
||||
#include "lm_log_packet_parser.hpp"
|
||||
#include "lm_log_getter_impl.hpp"
|
||||
#include "../impl/lm_log_packet_header.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
bool IsFlushAvailable();
|
||||
|
||||
bool g_is_logging_to_custom_sink = false;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit u32 g_log_destination = lm::LogDestination_TargetManager;
|
||||
|
||||
bool SetProcessId(const sf::InAutoSelectBuffer &message, u64 process_id) {
|
||||
/* Check the message. */
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(message.GetPointer()), alignof(impl::LogPacketHeader)));
|
||||
|
||||
/* Get a modifiable copy of the header. */
|
||||
auto *header = const_cast<impl::LogPacketHeader *>(reinterpret_cast<const impl::LogPacketHeader *>(message.GetPointer()));
|
||||
|
||||
/* Check that the message size is correct. */
|
||||
if (impl::LogPacketHeaderSize + header->GetPayloadSize() != message.GetSize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the header's process id. */
|
||||
header->SetProcessId(process_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PutLogToTargetManager(const sf::InAutoSelectBuffer &message) {
|
||||
/* Try to push the message. */
|
||||
bool success;
|
||||
if (IsFlushAvailable()) {
|
||||
success = LogBuffer::GetDefaultInstance().Push(message.GetPointer(), message.GetSize());
|
||||
} else {
|
||||
success = LogBuffer::GetDefaultInstance().TryPush(message.GetPointer(), message.GetSize());
|
||||
}
|
||||
|
||||
/* If we fail, increment dropped packet count. */
|
||||
if (!success) {
|
||||
EventLogTransmitter::GetDefaultInstance().IncreaseLogPacketDropCount();
|
||||
}
|
||||
}
|
||||
|
||||
void PutLogToUart(const sf::InAutoSelectBuffer &message) {
|
||||
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
|
||||
{
|
||||
/* Get header. */
|
||||
auto *data = message.GetPointer();
|
||||
auto data_size = message.GetSize();
|
||||
const auto *header = reinterpret_cast<const impl::LogPacketHeader *>(data);
|
||||
|
||||
/* Get the module name. */
|
||||
char module_name[0x10] = {};
|
||||
LogPacketParser::ParseModuleName(module_name, sizeof(module_name), data, data_size);
|
||||
|
||||
/* Create log metadata. */
|
||||
const diag::LogMetaData log_meta = {
|
||||
.module_name = module_name,
|
||||
.severity = static_cast<diag::LogSeverity>(header->GetSeverity()),
|
||||
.verbosity = header->GetVerbosity(),
|
||||
};
|
||||
|
||||
LogPacketParser::ParseTextLogWithContext(message.GetPointer(), message.GetSize(), [](const char *txt, size_t size, void *arg) {
|
||||
/* Get metadata. */
|
||||
const auto &meta = *static_cast<const diag::LogMetaData *>(arg);
|
||||
|
||||
/* Put the message to uart. */
|
||||
diag::impl::PutImpl(meta, txt, size);
|
||||
}, const_cast<diag::LogMetaData *>(std::addressof(log_meta)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PutLogToCustomSink(const sf::InAutoSelectBuffer &message) {
|
||||
LogPacketParser::ParseTextLogWithContext(message.GetPointer(), message.GetSize(), [](const char *txt, size_t size, void *) {
|
||||
/* Try to push the message. */
|
||||
if (!LogGetterImpl::GetBuffer().TryPush(txt, size)) {
|
||||
LogGetterImpl::IncreaseLogPacketDropCount();
|
||||
}
|
||||
}, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LoggerImpl::LoggerImpl(LogServiceImpl *parent, os::ProcessId process_id) : m_parent(parent), m_process_id(process_id.value) {
|
||||
/* Log start of session for process. */
|
||||
EventLogTransmitter::GetDefaultInstance().PushLogSessionBegin(m_process_id);
|
||||
}
|
||||
|
||||
LoggerImpl::~LoggerImpl() {
|
||||
/* Log end of session for process. */
|
||||
EventLogTransmitter::GetDefaultInstance().PushLogSessionEnd(m_process_id);
|
||||
}
|
||||
|
||||
Result LoggerImpl::Log(const sf::InAutoSelectBuffer &message) {
|
||||
/* Try to set the log process id. */
|
||||
/* NOTE: Nintendo succeeds here, for whatever purpose, so we will as well. */
|
||||
R_UNLESS(SetProcessId(message, m_process_id), ResultSuccess());
|
||||
|
||||
/* If we should, log to target manager. */
|
||||
if (g_log_destination & lm::LogDestination_TargetManager) {
|
||||
PutLogToTargetManager(message);
|
||||
}
|
||||
|
||||
/* If we should, log to uart. */
|
||||
if ((g_log_destination & lm::LogDestination_Uart) || (IsFlushAvailable() && (g_log_destination & lm::LogDestination_UartIfSleep))) {
|
||||
PutLogToUart(message);
|
||||
}
|
||||
|
||||
/* If we should, log to custom sink. */
|
||||
if (g_is_logging_to_custom_sink) {
|
||||
PutLogToCustomSink(message);
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LoggerImpl::SetDestination(u32 destination) {
|
||||
/* Set the log destination. */
|
||||
g_log_destination = destination;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
35
libraries/libstratosphere/source/lm/srv/lm_logger_impl.hpp
Normal file
35
libraries/libstratosphere/source/lm/srv/lm_logger_impl.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_log_service_impl.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
class LoggerImpl {
|
||||
private:
|
||||
LogServiceImpl *m_parent;
|
||||
u64 m_process_id;
|
||||
public:
|
||||
explicit LoggerImpl(LogServiceImpl *parent, os::ProcessId process_id);
|
||||
~LoggerImpl();
|
||||
public:
|
||||
Result Log(const sf::InAutoSelectBuffer &message);
|
||||
Result SetDestination(u32 destination);
|
||||
};
|
||||
static_assert(lm::IsILogger<LoggerImpl>);
|
||||
|
||||
}
|
361
libraries/libstratosphere/source/lm/srv/lm_sd_card_logger.cpp
Normal file
361
libraries/libstratosphere/source/lm/srv/lm_sd_card_logger.cpp
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_sd_card_logger.hpp"
|
||||
#include "lm_time_util.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char SdCardMountName[] = "sdcard";
|
||||
constexpr const char LogFileExtension[] = "nxbinlog";
|
||||
|
||||
constexpr const char SettingName[] = "lm";
|
||||
constexpr const char SettingKeyLoggingEnabled[] = "enable_sd_card_logging";
|
||||
constexpr const char SettingKeyOutputDirectory[] = "sd_card_log_output_directory";
|
||||
|
||||
constexpr inline size_t LogFileHeaderSize = 8;
|
||||
constexpr inline u32 LogFileHeaderMagic = util::ReverseFourCC<'p','h','p','h'>::Code;
|
||||
constexpr inline u8 LogFileHeaderVersion = 1;
|
||||
|
||||
struct LogFileHeader {
|
||||
u32 magic;
|
||||
u8 version;
|
||||
u8 reserved[3];
|
||||
};
|
||||
static_assert(sizeof(LogFileHeader) == LogFileHeaderSize);
|
||||
|
||||
constinit os::SdkMutex g_sd_card_logging_enabled_mutex;
|
||||
constinit bool g_determined_sd_card_logging_enabled = false;
|
||||
constinit bool g_sd_card_logging_enabled = false;
|
||||
|
||||
constinit os::SdkMutex g_sd_card_detection_event_mutex;
|
||||
constinit bool g_sd_card_inserted_cache = false;
|
||||
constinit bool g_sd_card_detection_event_initialized = false;
|
||||
constinit std::unique_ptr<fs::IEventNotifier> g_sd_card_detection_event_notifier;
|
||||
os::SystemEvent g_sd_card_detection_event;
|
||||
|
||||
bool GetSdCardLoggingEnabledImpl() {
|
||||
bool enabled;
|
||||
const auto size = settings::fwdbg::GetSettingsItemValue(std::addressof(enabled), sizeof(enabled), SettingName, SettingKeyLoggingEnabled);
|
||||
if (size != sizeof(enabled)) {
|
||||
AMS_ASSERT(size == sizeof(enabled));
|
||||
return false;
|
||||
}
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
bool GetSdCardLoggingEnabled() {
|
||||
if (AMS_UNLIKELY(!g_determined_sd_card_logging_enabled)) {
|
||||
std::scoped_lock lk(g_sd_card_logging_enabled_mutex);
|
||||
|
||||
if (AMS_LIKELY(!g_determined_sd_card_logging_enabled)) {
|
||||
g_sd_card_logging_enabled = GetSdCardLoggingEnabledImpl();
|
||||
g_determined_sd_card_logging_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return g_sd_card_logging_enabled;
|
||||
}
|
||||
|
||||
void EnsureSdCardDetectionEventInitialized() {
|
||||
if (AMS_UNLIKELY(!g_sd_card_detection_event_initialized)) {
|
||||
std::scoped_lock lk(g_sd_card_detection_event_mutex);
|
||||
|
||||
if (AMS_LIKELY(!g_sd_card_detection_event_initialized)) {
|
||||
/* Create SD card detection event notifier. */
|
||||
R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(g_sd_card_detection_event_notifier)));
|
||||
R_ABORT_UNLESS(g_sd_card_detection_event_notifier->BindEvent(g_sd_card_detection_event.GetBase(), os::EventClearMode_ManualClear));
|
||||
|
||||
/* Get initial inserted value. */
|
||||
g_sd_card_inserted_cache = fs::IsSdCardInserted();
|
||||
|
||||
g_sd_card_detection_event_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetSdCardStatus(bool *out_inserted, bool *out_status_changed) {
|
||||
/* Ensure that we can detect the sd card. */
|
||||
EnsureSdCardDetectionEventInitialized();
|
||||
|
||||
/* Check if there's a detection event. */
|
||||
const bool status_changed = g_sd_card_detection_event.TryWait();
|
||||
if (status_changed) {
|
||||
g_sd_card_detection_event.Clear();
|
||||
|
||||
/* Update the inserted cache. */
|
||||
g_sd_card_inserted_cache = fs::IsSdCardInserted();
|
||||
}
|
||||
|
||||
*out_inserted = g_sd_card_inserted_cache;
|
||||
*out_status_changed = status_changed;
|
||||
}
|
||||
|
||||
bool GetSdCardLogOutputDirectory(char *dst, size_t size) {
|
||||
/* Get the output directory size. */
|
||||
const auto value_size = settings::fwdbg::GetSettingsItemValueSize(SettingName, SettingKeyOutputDirectory);
|
||||
if (value_size > size) {
|
||||
AMS_ASSERT(value_size <= size);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the output directory. */
|
||||
const auto read_size = settings::fwdbg::GetSettingsItemValue(dst, size, SettingName, SettingKeyOutputDirectory);
|
||||
AMS_ASSERT(read_size == value_size);
|
||||
|
||||
return read_size == value_size;
|
||||
}
|
||||
|
||||
bool EnsureLogDirectory(const char *dir) {
|
||||
/* Generate the log directory path. */
|
||||
char path[0x80];
|
||||
const size_t len = util::SNPrintf(path, sizeof(path), "%s:/%s", SdCardMountName, dir);
|
||||
if (len >= sizeof(path)) {
|
||||
AMS_ASSERT(len < sizeof(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure the directory. */
|
||||
/* NOTE: Nintendo does not perform recusrive directory ensure, only a single CreateDirectory level. */
|
||||
return R_SUCCEEDED(fs::EnsureDirectoryRecursively(path));
|
||||
}
|
||||
|
||||
bool MakeLogFilePathWithoutExtension(char *dst, size_t size, const char *dir) {
|
||||
/* Get the current time. */
|
||||
const auto cur_time = time::ToCalendarTimeInUtc(lm::srv::GetCurrentTime());
|
||||
|
||||
/* Get the device serial number. */
|
||||
settings::system::SerialNumber serial_number;
|
||||
settings::system::GetSerialNumber(std::addressof(serial_number));
|
||||
|
||||
/* Print the path. */
|
||||
const size_t len = util::SNPrintf(dst, size, "%s:/%s/%s_%04d%02d%02d%02d%02d%02d", SdCardMountName, dir, serial_number.str, cur_time.year, cur_time.month, cur_time.day, cur_time.hour, cur_time.minute, cur_time.second);
|
||||
|
||||
AMS_ASSERT(len < size);
|
||||
return len < size;
|
||||
}
|
||||
|
||||
bool GenerateLogFile(char *dst, size_t size, const char *dir) {
|
||||
/* Generate the log file path. */
|
||||
char path_without_ext[0x80];
|
||||
if (!MakeLogFilePathWithoutExtension(path_without_ext, sizeof(path_without_ext), dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to find an available log file path. */
|
||||
constexpr auto MaximumLogIndex = 99;
|
||||
for (auto i = 1; i <= MaximumLogIndex; ++i) {
|
||||
/* Print the current log file path. */
|
||||
const size_t len = (i == 1) ? util::SNPrintf(dst, size, "%s.%s", path_without_ext, LogFileExtension) : util::SNPrintf(dst, size, "%s_%d.%s", path_without_ext, i, LogFileExtension);
|
||||
if (len >= size) {
|
||||
AMS_ASSERT(len < size);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to create the log file. */
|
||||
const auto result = fs::CreateFile(dst, 0);
|
||||
if (R_SUCCEEDED(result)) {
|
||||
return true;
|
||||
} else if (fs::ResultPathAlreadyExists::Includes(result)) {
|
||||
/* The log file already exists, so try the next index. */
|
||||
continue;
|
||||
} else {
|
||||
/* We failed to create a log file. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* We ran out of log file indices. */
|
||||
return false;
|
||||
}
|
||||
|
||||
Result WriteLogFileHeaderImpl(const char *path) {
|
||||
/* Open the log file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Write the log file header. */
|
||||
const LogFileHeader header = {
|
||||
.magic = LogFileHeaderMagic,
|
||||
.version = LogFileHeaderVersion
|
||||
};
|
||||
|
||||
R_TRY(fs::WriteFile(file, 0, std::addressof(header), sizeof(header), fs::WriteOption::Flush));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool WriteLogFileHeader(const char *path) {
|
||||
return R_SUCCEEDED(WriteLogFileHeaderImpl(path));
|
||||
}
|
||||
|
||||
Result WriteLogFileBodyImpl(const char *path, s64 offset, const u8 *data, size_t size) {
|
||||
/* Open the log file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Write the data. */
|
||||
R_TRY(fs::WriteFile(file, offset, data, size, fs::WriteOption::Flush));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool WriteLogFileBody(const char *path, s64 offset, const u8 *data, size_t size) {
|
||||
return R_SUCCEEDED(WriteLogFileBodyImpl(path, offset, data, size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SdCardLogger::SdCardLogger() : m_logging_observer_mutex(), m_is_enabled(false), m_is_sd_card_mounted(false), m_is_sd_card_status_unknown(false), m_log_file_offset(0), m_logging_observer(nullptr) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
bool SdCardLogger::GetEnabled() const {
|
||||
return m_is_enabled;
|
||||
}
|
||||
|
||||
void SdCardLogger::SetEnabled(bool enabled) {
|
||||
/* Only update if we need to. */
|
||||
if (m_is_enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set enabled. */
|
||||
m_is_enabled = enabled;
|
||||
|
||||
/* Invoke our observer. */
|
||||
std::scoped_lock lk(m_logging_observer_mutex);
|
||||
|
||||
if (m_logging_observer) {
|
||||
m_logging_observer(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void SdCardLogger::SetLoggingObserver(LoggingObserver observer) {
|
||||
std::scoped_lock lk(m_logging_observer_mutex);
|
||||
|
||||
m_logging_observer = observer;
|
||||
}
|
||||
|
||||
bool SdCardLogger::Initialize() {
|
||||
/* If we're already enabled, nothing to do. */
|
||||
if (this->GetEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get the sd card status. */
|
||||
bool inserted = false, status_changed = false;
|
||||
GetSdCardStatus(std::addressof(inserted), std::addressof(status_changed));
|
||||
|
||||
/* Update whether status is known. */
|
||||
if (status_changed) {
|
||||
m_is_sd_card_status_unknown = false;
|
||||
}
|
||||
|
||||
/* If the SD isn't inserted, we can't initialize. */
|
||||
if (!inserted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the status is unknown, we can't initialize. */
|
||||
if (m_is_sd_card_status_unknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Mount the SD card. */
|
||||
if (R_FAILED(fs::MountSdCard(SdCardMountName))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note that the SD card is mounted. */
|
||||
m_is_sd_card_mounted = true;
|
||||
|
||||
/* Get the output directory. */
|
||||
char output_dir[0x80];
|
||||
if (!GetSdCardLogOutputDirectory(output_dir, sizeof(output_dir))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure the output directory exists. */
|
||||
if (!EnsureLogDirectory(output_dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure that a log file exists for us to write to. */
|
||||
if (!GenerateLogFile(m_log_file_path, sizeof(m_log_file_path), output_dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Write the log file header. */
|
||||
if (!WriteLogFileHeader(m_log_file_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set our initial offset. */
|
||||
m_log_file_offset = LogFileHeaderSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SdCardLogger::Finalize() {
|
||||
this->SetEnabled(false);
|
||||
if (m_is_sd_card_mounted) {
|
||||
fs::Unmount(SdCardMountName);
|
||||
m_is_sd_card_mounted = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SdCardLogger::Write(const u8 *data, size_t size) {
|
||||
/* Only write if sd card logging is enabled. */
|
||||
if (!GetSdCardLoggingEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure we keep our pre and post-conditions in check. */
|
||||
bool success = false;
|
||||
ON_SCOPE_EXIT {
|
||||
if (!success && m_is_sd_card_mounted) {
|
||||
fs::Unmount(SdCardMountName);
|
||||
m_is_sd_card_mounted = false;
|
||||
m_is_sd_card_status_unknown = true;
|
||||
}
|
||||
this->SetEnabled(success);
|
||||
};
|
||||
|
||||
/* Try to initialize. */
|
||||
if (!this->Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to write the log file. */
|
||||
if (!WriteLogFileBody(m_log_file_path, m_log_file_offset, data, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
m_log_file_offset += size;
|
||||
|
||||
/* We succeeded. */
|
||||
success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::srv {
|
||||
|
||||
class SdCardLogger {
|
||||
AMS_SINGLETON_TRAITS(SdCardLogger);
|
||||
public:
|
||||
using LoggingObserver = void (*)(bool available);
|
||||
private:
|
||||
os::SdkMutex m_logging_observer_mutex;
|
||||
bool m_is_enabled;
|
||||
bool m_is_sd_card_mounted;
|
||||
bool m_is_sd_card_status_unknown;
|
||||
char m_log_file_path[0x80];
|
||||
s64 m_log_file_offset;
|
||||
LoggingObserver m_logging_observer;
|
||||
public:
|
||||
void Finalize();
|
||||
|
||||
void SetLoggingObserver(LoggingObserver observer);
|
||||
|
||||
bool Write(const u8 *data, size_t size);
|
||||
private:
|
||||
bool GetEnabled() const;
|
||||
void SetEnabled(bool enabled);
|
||||
|
||||
bool Initialize();
|
||||
};
|
||||
|
||||
}
|
78
libraries/libstratosphere/source/lm/srv/lm_time_util.cpp
Normal file
78
libraries/libstratosphere/source/lm/srv/lm_time_util.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "lm_time_util.hpp"
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit std::atomic_bool g_is_time_invalid = false;
|
||||
|
||||
constinit time::PosixTime InvalidPosixTime = { .value = 0 };
|
||||
|
||||
constexpr bool IsValidPosixTime(time::PosixTime time) {
|
||||
return time.value > 0;
|
||||
}
|
||||
|
||||
void EnsureTimeInitialized() {
|
||||
static constinit os::SdkMutex g_time_initialized_mutex;
|
||||
static constinit bool g_time_initialized = false;
|
||||
|
||||
if (AMS_UNLIKELY(!g_time_initialized)) {
|
||||
std::scoped_lock lk(g_time_initialized_mutex);
|
||||
|
||||
if (AMS_LIKELY(!g_time_initialized)) {
|
||||
R_ABORT_UNLESS(time::Initialize());
|
||||
g_time_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
time::PosixTime GetCurrentTime() {
|
||||
/* Ensure that we can use time services. */
|
||||
EnsureTimeInitialized();
|
||||
|
||||
/* Repeatedly try to get a valid time. */
|
||||
for (auto wait_seconds = 1; wait_seconds <= 8; wait_seconds *= 2) {
|
||||
/* Get the standard user system clock time. */
|
||||
time::PosixTime current_time{};
|
||||
if (R_FAILED(time::StandardUserSystemClock::GetCurrentTime(std::addressof(current_time)))) {
|
||||
return InvalidPosixTime;
|
||||
}
|
||||
|
||||
/* If the time is valid, return it. */
|
||||
if (IsValidPosixTime(current_time)) {
|
||||
return current_time;
|
||||
}
|
||||
|
||||
/* Check if we've failed to get a time in the past. */
|
||||
if (g_is_time_invalid) {
|
||||
return InvalidPosixTime;
|
||||
}
|
||||
|
||||
/* Wait a bit before trying again. */
|
||||
os::SleepThread(TimeSpan::FromSeconds(wait_seconds));
|
||||
}
|
||||
|
||||
/* We failed to get a valid time. */
|
||||
g_is_time_invalid = true;
|
||||
return InvalidPosixTime;
|
||||
}
|
||||
|
||||
}
|
23
libraries/libstratosphere/source/lm/srv/lm_time_util.hpp
Normal file
23
libraries/libstratosphere/source/lm/srv/lm_time_util.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::lm::srv {
|
||||
|
||||
time::PosixTime GetCurrentTime();
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::ro::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
bool SearchSegmentHead(uintptr_t *out, uintptr_t target, svc::MemoryState state) {
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
bool success = false;
|
||||
|
||||
for (uintptr_t cur = target; cur <= target; cur = mem_info.addr - 1) {
|
||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur));
|
||||
|
||||
if (mem_info.state != state || mem_info.perm != svc::MemoryPermission_ReadExecute) {
|
||||
break;
|
||||
}
|
||||
|
||||
*out = mem_info.addr;
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool SearchSegmentTail(uintptr_t *out, uintptr_t target, svc::MemoryState state) {
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
bool success = false;
|
||||
|
||||
for (uintptr_t cur = target; cur >= target; cur = mem_info.addr + mem_info.size) {
|
||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur));
|
||||
|
||||
if (mem_info.state != state) {
|
||||
break;
|
||||
}
|
||||
|
||||
*out = mem_info.addr + mem_info.size - 1;
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool QueryModule(uintptr_t *out_address, size_t *out_size, uintptr_t pc) {
|
||||
/* Query the program counter. */
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), pc));
|
||||
|
||||
/* Check memory info. */
|
||||
if (mem_info.perm != svc::MemoryPermission_ReadExecute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mem_info.state != svc::MemoryState_Code && mem_info.state != svc::MemoryState_AliasCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find head/tail. */
|
||||
uintptr_t head = 0, tail = 0;
|
||||
AMS_ABORT_UNLESS(SearchSegmentHead(std::addressof(head), pc, mem_info.state));
|
||||
AMS_ABORT_UNLESS(SearchSegmentTail(std::addressof(tail), pc, mem_info.state));
|
||||
AMS_ABORT_UNLESS(SearchSegmentTail(std::addressof(tail), tail + 1, mem_info.state == svc::MemoryState_Code ? svc::MemoryState_CodeData : svc::MemoryState_AliasCodeData));
|
||||
|
||||
/* Set output. */
|
||||
*out_address = head;
|
||||
*out_size = tail + 1 - head;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool GetExceptionInfo(ExceptionInfo *out, uintptr_t pc) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
/* Find the module. */
|
||||
if (!QueryModule(std::addressof(out->module_address), std::addressof(out->module_size), pc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate the module. */
|
||||
rocrt::ModuleHeaderLocation *loc = reinterpret_cast<rocrt::ModuleHeaderLocation *>(out->module_address);
|
||||
rocrt::ModuleHeader *header = rocrt::GetModuleHeader(loc);
|
||||
AMS_ABORT_UNLESS(header->signature == rocrt::ModuleHeaderVersion);
|
||||
|
||||
/* Set the exception info. */
|
||||
out->info_offset = loc->header_offset + header->exception_info_start_offset;
|
||||
out->info_size = header->exception_info_end_offset - header->exception_info_start_offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,140 @@
|
|||
|
||||
namespace ams::time::impl::util {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const int DaysPerMonth[12] = {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
static_assert(std::accumulate(std::begin(DaysPerMonth), std::end(DaysPerMonth), 0) == 365);
|
||||
|
||||
constexpr inline const std::array<int, 12> SumDaysPerMonth = [] {
|
||||
std::array<int, 12> days = {};
|
||||
for (size_t i = 1; i < days.size(); ++i) {
|
||||
days[i] = days[i - 1] + DaysPerMonth[i - 1];
|
||||
}
|
||||
return days;
|
||||
}();
|
||||
static_assert(SumDaysPerMonth[ 0] == 0);
|
||||
static_assert(SumDaysPerMonth[11] + DaysPerMonth[11] == 365);
|
||||
|
||||
constexpr bool IsLeapYearImpl(int year) {
|
||||
if ((year % 400) == 0) {
|
||||
return true;
|
||||
} else if ((year % 100) == 0) {
|
||||
return false;
|
||||
} else if ((year % 4) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int DateToDaysImpl(int year, int month, int day) {
|
||||
/* Lightly validate input. */
|
||||
AMS_ASSERT(year > 0);
|
||||
AMS_ASSERT(month > 0);
|
||||
AMS_ASSERT(day > 0);
|
||||
|
||||
/* Adjust months within range. */
|
||||
year += month / 12;
|
||||
month %= 12;
|
||||
if (month == 0) {
|
||||
month = 12;
|
||||
}
|
||||
AMS_ASSERT(1 <= month && month <= 12);
|
||||
|
||||
/* Calculate days. */
|
||||
int res = (year - 1) * 365;
|
||||
res += (year / 4) - (year / 100) + (year / 400);
|
||||
res += SumDaysPerMonth[month - 1] + day;
|
||||
|
||||
/* Subtract leap day, if it hasn't happened yet. */
|
||||
if (month < 3 && IsLeapYearImpl(year)) {
|
||||
res -= 1;
|
||||
}
|
||||
|
||||
/* Subtract the current day. */
|
||||
res -= 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr void DaysToDateImpl(int *out_year, int *out_month, int *out_day, int days) {
|
||||
/* Lightly validate input. */
|
||||
AMS_ASSERT(days > 0);
|
||||
|
||||
/* Declare unit conversion factors. */
|
||||
constexpr int DaysPerYear = 365;
|
||||
constexpr int DaysPerFourYears = DaysPerYear * 4 + 1;
|
||||
constexpr int DaysPerCentury = DaysPerFourYears * 25 - 1;
|
||||
constexpr int DaysPerFourCenturies = DaysPerCentury * 4 + 1;
|
||||
|
||||
/* Adjust date. */
|
||||
days -= 59;
|
||||
days += 365;
|
||||
|
||||
/* Determine various units. */
|
||||
int four_centuries = days / DaysPerFourCenturies;
|
||||
int four_centuries_rem = days % DaysPerFourCenturies;
|
||||
if (four_centuries_rem < 0) {
|
||||
four_centuries_rem += DaysPerFourCenturies;
|
||||
--four_centuries;
|
||||
}
|
||||
|
||||
int centuries = four_centuries_rem / DaysPerCentury;
|
||||
int centuries_rem = four_centuries_rem % DaysPerCentury;
|
||||
|
||||
int four_years = centuries_rem / DaysPerFourYears;
|
||||
int four_years_rem = centuries_rem % DaysPerFourYears;
|
||||
|
||||
int years = four_years_rem / DaysPerYear;
|
||||
int years_rem = four_years_rem % DaysPerYear;
|
||||
|
||||
/* Adjust into range. */
|
||||
int year = 400 * four_centuries + 100 * centuries + 4 * four_years + years;
|
||||
int month = (5 * years_rem + 2) / 153;
|
||||
int day = years_rem - (153 * month + 2) / 5 + 1;
|
||||
|
||||
/* Adjust in case we fell into a pathological case. */
|
||||
if (years == 4 || centuries == 4) {
|
||||
month = 11;
|
||||
day = 29;
|
||||
year -= 1;
|
||||
}
|
||||
|
||||
/* Adjust month. */
|
||||
if (month <= 9) {
|
||||
month += 3;
|
||||
} else {
|
||||
month -= 9;
|
||||
year += 1;
|
||||
}
|
||||
|
||||
/* Set output. */
|
||||
if (out_year) {
|
||||
*out_year = year;
|
||||
}
|
||||
if (out_month) {
|
||||
*out_month = month;
|
||||
}
|
||||
if (out_day) {
|
||||
*out_day = day;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline int EpochDays = DateToDaysImpl(1970, 1, 1);
|
||||
|
||||
static_assert([]() -> bool {
|
||||
int year{}, month{}, day{};
|
||||
|
||||
DaysToDateImpl(std::addressof(year), std::addressof(month), std::addressof(day), EpochDays);
|
||||
|
||||
return year == 1970 && month == 1 && day == 1;
|
||||
}());
|
||||
|
||||
}
|
||||
|
||||
Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
|
@ -31,4 +165,97 @@ namespace ams::time::impl::util {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool IsValidDate(int year, int month, int day) {
|
||||
return 1 <= year && 1 <= month && month <= 12 && 1 <= day && day <= GetDaysInMonth(year, month);
|
||||
}
|
||||
|
||||
bool IsLeapYear(int year) {
|
||||
AMS_ASSERT(year > 0);
|
||||
|
||||
return IsLeapYearImpl(year);
|
||||
}
|
||||
|
||||
int GetDaysInMonth(int year, int month) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(year > 0);
|
||||
AMS_ASSERT(1 <= month && month <= 12);
|
||||
|
||||
if (month == 2 && IsLeapYear(year)) {
|
||||
return DaysPerMonth[month - 1] + 1;
|
||||
} else {
|
||||
return DaysPerMonth[month - 1];
|
||||
}
|
||||
}
|
||||
|
||||
int DateToDays(int year, int month, int day) {
|
||||
return DateToDaysImpl(year, month, day);
|
||||
}
|
||||
|
||||
void DaysToDate(int *out_year, int *out_month, int *out_day, int days) {
|
||||
DaysToDateImpl(out_year, out_month, out_day, days);
|
||||
}
|
||||
|
||||
CalendarTime ToCalendarTimeInUtc(const PosixTime &posix_time) {
|
||||
constexpr s64 SecondsPerDay = TimeSpan::FromDays(1).GetSeconds();
|
||||
constexpr s64 SecondsPerHour = TimeSpan::FromHours(1).GetSeconds();
|
||||
constexpr s64 SecondsPerMinute = TimeSpan::FromMinutes(1).GetSeconds();
|
||||
|
||||
/* Get year/month/day. */
|
||||
int year, month, day;
|
||||
DaysToDate(std::addressof(year), std::addressof(month), std::addressof(day), static_cast<int>(posix_time.value / SecondsPerDay) + EpochDays);
|
||||
|
||||
/* Handle negative posix times. */
|
||||
s64 posix_abs = posix_time.value >= 0 ? posix_time.value : -1 * posix_time.value;
|
||||
s64 posix_rem = posix_abs % SecondsPerDay;
|
||||
if (posix_time.value < 0) {
|
||||
posix_rem *= -1;
|
||||
}
|
||||
|
||||
/* Adjust remainder if negative. */
|
||||
if (posix_rem < 0) {
|
||||
if ((--day) <= 0) {
|
||||
if ((--month) <= 0) {
|
||||
--year;
|
||||
month = 12;
|
||||
}
|
||||
day = time::impl::util::GetDaysInMonth(year, month);
|
||||
}
|
||||
|
||||
posix_rem += SecondsPerDay;
|
||||
}
|
||||
|
||||
const int hour = posix_rem / SecondsPerHour;
|
||||
posix_rem %= SecondsPerHour;
|
||||
|
||||
const int minute = posix_rem / SecondsPerMinute;
|
||||
posix_rem %= SecondsPerMinute;
|
||||
|
||||
const int second = posix_rem;
|
||||
|
||||
return CalendarTime {
|
||||
.year = static_cast<s16>(year),
|
||||
.month = static_cast<s8>(month),
|
||||
.day = static_cast<s8>(day),
|
||||
.hour = static_cast<s8>(hour),
|
||||
.minute = static_cast<s8>(minute),
|
||||
.second = static_cast<s8>(second),
|
||||
};
|
||||
}
|
||||
|
||||
PosixTime ToPosixTimeFromUtc(const CalendarTime &calendar_time) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(IsValidDate(calendar_time.year, calendar_time.month, calendar_time.day));
|
||||
AMS_ASSERT(0 <= calendar_time.hour && calendar_time.hour <= 23);
|
||||
AMS_ASSERT(0 <= calendar_time.minute && calendar_time.minute <= 59);
|
||||
AMS_ASSERT(0 <= calendar_time.second && calendar_time.second <= 59);
|
||||
|
||||
/* Extract/convert fields. */
|
||||
const s64 days = static_cast<s64>(time::impl::util::DateToDays(calendar_time.year, calendar_time.month, calendar_time.day)) - EpochDays;
|
||||
const s64 hours = calendar_time.hour;
|
||||
const s64 minutes = calendar_time.minute;
|
||||
const s64 seconds = calendar_time.second;
|
||||
|
||||
return PosixTime { .value = ((((days * 24) + hours) * 60) + minutes) * 60 + seconds };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,11 +34,10 @@ namespace ams::time {
|
|||
InitializeMode_SystemUser,
|
||||
};
|
||||
|
||||
u32 g_initialize_count = 0;
|
||||
InitializeMode g_initialize_mode = InitializeMode_None;
|
||||
constinit u32 g_initialize_count = 0;
|
||||
constinit InitializeMode g_initialize_mode = InitializeMode_None;
|
||||
|
||||
/* TODO: os::SdkMutex */
|
||||
os::Mutex g_initialize_mutex(false);
|
||||
constinit os::SdkMutex g_initialize_mutex;
|
||||
|
||||
Result InitializeImpl(InitializeMode mode) {
|
||||
std::scoped_lock lk(g_initialize_mutex);
|
||||
|
@ -76,7 +75,11 @@ namespace ams::time {
|
|||
}
|
||||
|
||||
Result InitializeForSystemUser() {
|
||||
return InitializeImpl(InitializeMode_System);
|
||||
if (hos::GetVersion() >= hos::Version_9_0_0) {
|
||||
return InitializeImpl(InitializeMode_SystemUser);
|
||||
} else {
|
||||
return InitializeImpl(InitializeMode_Normal);
|
||||
}
|
||||
}
|
||||
|
||||
Result Finalize() {
|
||||
|
@ -98,6 +101,10 @@ namespace ams::time {
|
|||
return g_initialize_count > 0;
|
||||
}
|
||||
|
||||
bool IsValidDate(int year, int month, int day) {
|
||||
return impl::util::IsValidDate(year, month, day);
|
||||
}
|
||||
|
||||
Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) {
|
||||
return impl::util::GetSpanBetween(out, from, to);
|
||||
}
|
||||
|
|
46
libraries/libstratosphere/source/time/time_calendar_time.cpp
Normal file
46
libraries/libstratosphere/source/time/time_calendar_time.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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::time {
|
||||
|
||||
bool CalendarTime::IsValid() const {
|
||||
return time::IsValidDate(this->year, this->month, this->day);
|
||||
}
|
||||
|
||||
CalendarTime &CalendarTime::operator+=(const TimeSpan &ts) {
|
||||
*this = ToCalendarTimeInUtc(ToPosixTimeFromUtc(*this) + ts);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CalendarTime &CalendarTime::operator-=(const TimeSpan &ts) {
|
||||
*this = ToCalendarTimeInUtc(ToPosixTimeFromUtc(*this) - ts);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CalendarTime operator+(const CalendarTime &lhs, const TimeSpan &rhs) {
|
||||
return ToCalendarTimeInUtc(ToPosixTimeFromUtc(lhs) + rhs);
|
||||
}
|
||||
|
||||
CalendarTime operator-(const CalendarTime &lhs, const TimeSpan &rhs) {
|
||||
return ToCalendarTimeInUtc(ToPosixTimeFromUtc(lhs) - rhs);
|
||||
}
|
||||
|
||||
TimeSpan operator-(const CalendarTime &lhs, const CalendarTime &rhs) {
|
||||
return ToPosixTimeFromUtc(lhs) - ToPosixTimeFromUtc(rhs);
|
||||
}
|
||||
|
||||
}
|
28
libraries/libstratosphere/source/time/time_timezone_api.cpp
Normal file
28
libraries/libstratosphere/source/time/time_timezone_api.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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::time {
|
||||
|
||||
CalendarTime ToCalendarTimeInUtc(const PosixTime &posix_time) {
|
||||
return impl::util::ToCalendarTimeInUtc(posix_time);
|
||||
}
|
||||
|
||||
PosixTime ToPosixTimeFromUtc(const CalendarTime &calendar_time) {
|
||||
return impl::util::ToPosixTimeFromUtc(calendar_time);
|
||||
}
|
||||
|
||||
}
|
|
@ -48,6 +48,7 @@
|
|||
#include <vapours/util/util_character_encoding.hpp>
|
||||
#include <vapours/util/util_format_string.hpp>
|
||||
#include <vapours/util/util_range.hpp>
|
||||
#include <vapours/util/util_utf8_string_util.hpp>
|
||||
|
||||
#include <vapours/util/util_fixed_map.hpp>
|
||||
#include <vapours/util/util_fixed_set.hpp>
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
|
||||
namespace ams::util {
|
||||
|
||||
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...);
|
||||
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
|
||||
|
||||
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...);
|
||||
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
bool VerifyUtf8String(const char *str, size_t size);
|
||||
|
||||
int GetCodePointCountOfUtf8String(const char *str, size_t size);
|
||||
|
||||
}
|
|
@ -136,6 +136,7 @@ namespace ams::util {
|
|||
|
||||
/* Parse length. */
|
||||
constexpr bool SizeIsLong = sizeof(size_t) == sizeof(long);
|
||||
constexpr bool PointerIsLong = sizeof(uintptr_t) == sizeof(long);
|
||||
constexpr bool IntMaxIsLong = sizeof(intmax_t) == sizeof(long);
|
||||
constexpr bool PtrDiffIsLong = sizeof(ptrdiff_t) == sizeof(long);
|
||||
switch (*format) {
|
||||
|
@ -174,10 +175,10 @@ namespace ams::util {
|
|||
const char specifier = *(format++);
|
||||
switch (specifier) {
|
||||
case 'p':
|
||||
if constexpr (sizeof(uintptr_t) == sizeof(long long)) {
|
||||
SetFlag(FormatSpecifierFlag_LongLong);
|
||||
} else {
|
||||
if constexpr (PointerIsLong) {
|
||||
SetFlag(FormatSpecifierFlag_Long);
|
||||
} else {
|
||||
SetFlag(FormatSpecifierFlag_LongLong);
|
||||
}
|
||||
SetFlag(FormatSpecifierFlag_Hash);
|
||||
[[fallthrough]];
|
||||
|
|
121
libraries/libvapours/source/util/util_utf8_string_util.cpp
Normal file
121
libraries/libvapours/source/util/util_utf8_string_util.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <vapours.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const u8 CodePointByteLengthTable[0x100] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetCodePointByteLength(u8 c) {
|
||||
return CodePointByteLengthTable[c];
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsValidTail(u8 c) {
|
||||
return (c & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
constexpr inline bool VerifyCode(const u8 *code, size_t size) {
|
||||
switch (size) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
if (!IsValidTail(code[1])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (code[0] == 0xE0 && (code[1] & 0x20) == 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (code[0] == 0xED && (code[1] & 0x20) != 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTail(code[1]) || !IsValidTail(code[2])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (code[0] == 0xF0 && (code[1] & 0x30) == 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (code[0] == 0xF4 && (code[1] & 0x30) != 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTail(code[1]) || !IsValidTail(code[2]) || !IsValidTail(code[3])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool VerifyUtf8String(const char *str, size_t size) {
|
||||
return GetCodePointCountOfUtf8String(str, size) != -1;
|
||||
}
|
||||
|
||||
int GetCodePointCountOfUtf8String(const char *str, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(str != nullptr);
|
||||
AMS_ASSERT(size > 0);
|
||||
|
||||
/* Parse codepoints. */
|
||||
int count = 0;
|
||||
|
||||
while (size > 0) {
|
||||
/* Get and check the current codepoint. */
|
||||
const u8 *code = reinterpret_cast<const u8 *>(str);
|
||||
const size_t code_size = GetCodePointByteLength(code[0]);
|
||||
|
||||
if (code_size > size || !VerifyCode(code, code_size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
str += code_size;
|
||||
size -= code_size;
|
||||
|
||||
/* Increment count. */
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
88
stratosphere/LogManager/LogManager.json
Normal file
88
stratosphere/LogManager/LogManager.json
Normal file
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"name": "LogManager",
|
||||
"title_id": "0x0100000000000420",
|
||||
"title_id_range_min": "0x0100000000000420",
|
||||
"title_id_range_max": "0x0100000000000420",
|
||||
"main_thread_stack_size": "0x00003000",
|
||||
"main_thread_priority": 38,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 1,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": ["fsp-srv", "htc", "htcs", "psc:m", "set", "set:sys", "srepo:u", "time:su", "time:u", "tma_log"],
|
||||
"service_host": ["lm", "lm:get"],
|
||||
"kernel_capabilities": [{
|
||||
"type": "kernel_flags",
|
||||
"value": {
|
||||
"highest_thread_priority": 63,
|
||||
"lowest_thread_priority": 24,
|
||||
"lowest_cpu_id": 3,
|
||||
"highest_cpu_id": 3
|
||||
}
|
||||
}, {
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
"svcMapMemory": "0x04",
|
||||
"svcUnmapMemory": "0x05",
|
||||
"svcQueryMemory": "0x06",
|
||||
"svcExitProcess": "0x07",
|
||||
"svcCreateThread": "0x08",
|
||||
"svcStartThread": "0x09",
|
||||
"svcExitThread": "0x0a",
|
||||
"svcSleepThread": "0x0b",
|
||||
"svcGetThreadPriority": "0x0c",
|
||||
"svcSetThreadPriority": "0x0d",
|
||||
"svcGetThreadCoreMask": "0x0e",
|
||||
"svcSetThreadCoreMask": "0x0f",
|
||||
"svcGetCurrentProcessorNumber": "0x10",
|
||||
"svcSignalEvent": "0x11",
|
||||
"svcClearEvent": "0x12",
|
||||
"svcMapSharedMemory": "0x13",
|
||||
"svcUnmapSharedMemory": "0x14",
|
||||
"svcCreateTransferMemory": "0x15",
|
||||
"svcCloseHandle": "0x16",
|
||||
"svcResetSignal": "0x17",
|
||||
"svcWaitSynchronization": "0x18",
|
||||
"svcCancelSynchronization": "0x19",
|
||||
"svcArbitrateLock": "0x1a",
|
||||
"svcArbitrateUnlock": "0x1b",
|
||||
"svcWaitProcessWideKeyAtomic": "0x1c",
|
||||
"svcSignalProcessWideKey": "0x1d",
|
||||
"svcGetSystemTick": "0x1e",
|
||||
"svcConnectToNamedPort": "0x1f",
|
||||
"svcSendSyncRequestLight": "0x20",
|
||||
"svcSendSyncRequest": "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||
"svcGetProcessId": "0x24",
|
||||
"svcGetThreadId": "0x25",
|
||||
"svcBreak": "0x26",
|
||||
"svcOutputDebugString": "0x27",
|
||||
"svcReturnFromException": "0x28",
|
||||
"svcGetInfo": "0x29",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
}, {
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0030"
|
||||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 64
|
||||
}]
|
||||
}
|
113
stratosphere/LogManager/Makefile
Normal file
113
stratosphere/LogManager/Makefile
Normal file
|
@ -0,0 +1,113 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
|
||||
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
|
||||
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
|
||||
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso
|
||||
else
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
endif
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
165
stratosphere/LogManager/source/lm_main.cpp
Normal file
165
stratosphere/LogManager/source/lm_main.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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>
|
||||
|
||||
extern "C" {
|
||||
extern u32 __start__;
|
||||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
u32 __nx_fs_num_sessions = 2;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x0
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
void __libnx_initheap(void);
|
||||
void __appInit(void);
|
||||
void __appExit(void);
|
||||
|
||||
void *__libnx_alloc(size_t size);
|
||||
void *__libnx_aligned_alloc(size_t alignment, size_t size);
|
||||
void __libnx_free(void *mem);
|
||||
}
|
||||
|
||||
namespace ams {
|
||||
|
||||
ncm::ProgramId CurrentProgramId = ncm::AtmosphereProgramId::AtmosphereLogManager;
|
||||
|
||||
}
|
||||
|
||||
using namespace ams;
|
||||
|
||||
#define AMS_LM_USE_FATAL_ERROR 1
|
||||
|
||||
#if AMS_LM_USE_FATAL_ERROR
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* Exception handling. */
|
||||
alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize];
|
||||
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
|
||||
void __libnx_exception_handler(ThreadExceptionDump *ctx);
|
||||
|
||||
}
|
||||
|
||||
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
|
||||
ams::CrashHandler(ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void __libnx_initheap(void) {
|
||||
void* addr = nx_inner_heap;
|
||||
size_t size = nx_inner_heap_size;
|
||||
|
||||
/* Newlib */
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = (char*)addr;
|
||||
fake_heap_end = (char*)addr + size;
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
hos::InitializeForStratosphere();
|
||||
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
|
||||
R_ABORT_UNLESS(::fsInitialize());
|
||||
R_ABORT_UNLESS(::setsysInitialize());
|
||||
R_ABORT_UNLESS(::pscmInitialize());
|
||||
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
|
||||
void __appExit(void) {
|
||||
fsExit();
|
||||
}
|
||||
|
||||
namespace ams {
|
||||
|
||||
void *Malloc(size_t size) {
|
||||
AMS_ABORT("ams::Malloc was called");
|
||||
}
|
||||
|
||||
void Free(void *ptr) {
|
||||
AMS_ABORT("ams::Free was called");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void *operator new(size_t size) {
|
||||
AMS_ABORT("operator new(size_t) was called");
|
||||
}
|
||||
|
||||
void operator delete(void *p) {
|
||||
AMS_ABORT("operator delete(void *) was called");
|
||||
}
|
||||
|
||||
void *__libnx_alloc(size_t size) {
|
||||
AMS_ABORT("__libnx_alloc was called");
|
||||
}
|
||||
|
||||
void *__libnx_aligned_alloc(size_t alignment, size_t size) {
|
||||
AMS_ABORT("__libnx_aligned_alloc was called");
|
||||
}
|
||||
|
||||
void __libnx_free(void *mem) {
|
||||
AMS_ABORT("__libnx_free was called");
|
||||
}
|
||||
|
||||
namespace ams::lm::srv {
|
||||
|
||||
void StartLogServerProxy();
|
||||
void StopLogServerProxy();
|
||||
|
||||
void InitializeFlushThread();
|
||||
void FinalizeFlushThread();
|
||||
|
||||
void InitializeIpcServer();
|
||||
void LoopIpcServer();
|
||||
void FinalizeIpcServer();
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Check thread priority. */
|
||||
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(LogManager, MainThread));
|
||||
|
||||
/* Set thread name. */
|
||||
os::ChangeThreadPriority(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, IpcServer));
|
||||
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(lm, IpcServer));
|
||||
|
||||
/* Start log server proxy. */
|
||||
lm::srv::StartLogServerProxy();
|
||||
|
||||
/* Initialize flush thread. */
|
||||
lm::srv::InitializeFlushThread();
|
||||
|
||||
/* Process IPC server. */
|
||||
lm::srv::InitializeIpcServer();
|
||||
lm::srv::LoopIpcServer();
|
||||
lm::srv::FinalizeIpcServer();
|
||||
|
||||
/* Finalize flush thread. */
|
||||
lm::srv::FinalizeFlushThread();
|
||||
|
||||
/* Stop log server proxy. */
|
||||
lm::srv::StopLogServerProxy();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
MODULES := loader ncm pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2 erpt pgl jpegdec
|
||||
MODULES := loader ncm pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2 erpt pgl jpegdec LogManager
|
||||
|
||||
SUBFOLDERS := $(MODULES)
|
||||
|
||||
|
|
|
@ -317,6 +317,14 @@ namespace ams::settings::fwdbg {
|
|||
/* (note: this is normally not necessary, and ips patches can be used.) */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("ro", "ease_nro_restriction", "u8!0x1"));
|
||||
|
||||
/* Control whether lm should log to the SD card. */
|
||||
/* Note that this setting does nothing when log manager is not enabled. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("lm", "enable_sd_card_logging", "u8!0x1"));
|
||||
|
||||
/* Control the output directory for SD card logs. */
|
||||
/* Note that this setting does nothing when log manager is not enabled/sd card logging is not enabled. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("lm", "sd_card_log_output_directory", "str!atmosphere/binlogs"));
|
||||
|
||||
/* Atmosphere custom settings. */
|
||||
|
||||
/* Reboot from fatal automatically after some number of milliseconds. */
|
||||
|
@ -370,6 +378,11 @@ namespace ams::settings::fwdbg {
|
|||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0"));
|
||||
|
||||
/* Controls whether atmosphere's log manager is enabled. */
|
||||
/* Note that this setting is ignored (and treated as 1) when htc is enabled. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_log_manager", "u8!0x0"));
|
||||
|
||||
/* Hbloader custom settings. */
|
||||
|
||||
/* Controls the size of the homebrew heap when running as applet. */
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* 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 "ldr_capabilities.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_development_manager.hpp"
|
||||
|
|
Loading…
Reference in a new issue