mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-11-04 04:51:16 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			167 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) Atmosphère-NX
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms and conditions of the GNU General Public License,
 | 
						|
 * version 2, as published by the Free Software Foundation.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | 
						|
 * more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
#include <stratosphere.hpp>
 | 
						|
#include "diag_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);
 | 
						|
    }
 | 
						|
 | 
						|
}
 |