2021-09-12 02:32:14 +00:00
|
|
|
/*
|
2021-10-04 19:59:10 +00:00
|
|
|
* Copyright (c) Atmosphère-NX
|
2021-09-12 02:32:14 +00:00
|
|
|
*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|