diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 19736525..aa9d9642 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -108,6 +108,8 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/pl/IPlatformServiceManager.cpp ${source_DIR}/skyline/services/aocsrv/IAddOnContentManager.cpp ${source_DIR}/skyline/services/pctl/IParentalControlServiceFactory.cpp + ${source_DIR}/skyline/services/lm/ILogService.cpp + ${source_DIR}/skyline/services/lm/ILogger.cpp ${source_DIR}/skyline/vfs/partition_filesystem.cpp ${source_DIR}/skyline/vfs/rom_filesystem.cpp ${source_DIR}/skyline/vfs/os_backing.cpp diff --git a/app/src/main/cpp/skyline/services/base_service.h b/app/src/main/cpp/skyline/services/base_service.h index 1a36aa34..9f80c730 100644 --- a/app/src/main/cpp/skyline/services/base_service.h +++ b/app/src/main/cpp/skyline/services/base_service.h @@ -65,6 +65,8 @@ namespace skyline::service { pl_IPlatformServiceManager, aocsrv_IAddOnContentManager, pctl_IParentalControlServiceFactory, + lm_ILogService, + lm_ILogger }; /** @@ -94,7 +96,8 @@ namespace skyline::service { {"pctl", Service::pctl_IParentalControlServiceFactory}, {"pctl:a", Service::pctl_IParentalControlServiceFactory}, {"pctl:s", Service::pctl_IParentalControlServiceFactory}, - {"pctl:r", Service::pctl_IParentalControlServiceFactory} + {"pctl:r", Service::pctl_IParentalControlServiceFactory}, + {"lm", Service::lm_ILogService} }; class ServiceManager; diff --git a/app/src/main/cpp/skyline/services/lm/ILogService.cpp b/app/src/main/cpp/skyline/services/lm/ILogService.cpp new file mode 100644 index 00000000..ce7c2611 --- /dev/null +++ b/app/src/main/cpp/skyline/services/lm/ILogService.cpp @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "ILogger.h" +#include "ILogService.h" + +namespace skyline::service::lm { + ILogService::ILogService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::lm_ILogService, "lm:ILogService", { + {0x0, SFUNC(ILogService::OpenLogger)} + }) {} + + void ILogService::OpenLogger(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(std::make_shared(state, manager), session, response); + } +} diff --git a/app/src/main/cpp/skyline/services/lm/ILogService.h b/app/src/main/cpp/skyline/services/lm/ILogService.h new file mode 100644 index 00000000..7ed5fc2f --- /dev/null +++ b/app/src/main/cpp/skyline/services/lm/ILogService.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include + +namespace skyline::service::lm { + /** + * @brief ILogService is used by applications to open an ILogger for printing log messages (https://switchbrew.org/wiki/Log_services#lm) + */ + class ILogService : public BaseService { + public: + ILogService(const DeviceState &state, ServiceManager &manager); + + /** + * @brief This opens an ILogger that can be used by applications to print log messages (https://switchbrew.org/wiki/Log_services#OpenLogger) + */ + void OpenLogger(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/lm/ILogger.cpp b/app/src/main/cpp/skyline/services/lm/ILogger.cpp new file mode 100644 index 00000000..057e5bcd --- /dev/null +++ b/app/src/main/cpp/skyline/services/lm/ILogger.cpp @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "ILogger.h" + +namespace skyline::service::lm { + ILogger::ILogger(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::lm_ILogger, "lm:ILogger", { + {0x0, SFUNC(ILogger::Log)}, + {0x1, SFUNC(ILogger::SetDestination)} + }) {} + + std::string ILogger::GetFieldName(LogFieldType type) { + switch (type) { + case LogFieldType::Message: + return "Message"; + case LogFieldType::Line: + return "Line"; + case LogFieldType::Filename: + return "Filename"; + case LogFieldType::Function: + return "Function"; + case LogFieldType::Module: + return "Module"; + case LogFieldType::Thread: + return "Thread"; + case LogFieldType::DropCount: + return "DropCount"; + case LogFieldType::Time: + return "Time"; + case LogFieldType::ProgramName: + return "ProgramName"; + default: + return ""; + } + } + + void ILogger::Log(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + struct Data { + u64 pid; + u64 threadContext; + u16 flags; + LogLevel level; + u8 verbosity; + u32 payloadLength; + } data = state.process->GetReference(request.inputBuf.at(0).address); + + std::ostringstream logMessage; + logMessage << "Guest log: "; + + u64 offset = sizeof(Data); + while (offset < request.inputBuf.at(0).size) { + auto fieldType = state.process->GetObject(request.inputBuf.at(0).address + offset++); + auto length = state.process->GetObject(request.inputBuf.at(0).address + offset++); + auto address = request.inputBuf.at(0).address + offset; + + switch (fieldType) { + case LogFieldType::Start: + offset += length; + continue; + case LogFieldType::Line: + logMessage << GetFieldName(fieldType) << ": " << state.process->GetObject(address); + offset += sizeof(u32); + continue; + case LogFieldType::DropCount: + logMessage << GetFieldName(fieldType) << ": " << state.process->GetObject(address); + offset += sizeof(u64); + continue; + case LogFieldType::Time: + logMessage << GetFieldName(fieldType) << ": " << state.process->GetObject(address) << "s"; + offset += sizeof(u64); + continue; + case LogFieldType::Stop: + break; + default: + logMessage << GetFieldName(fieldType) << ": " << state.process->GetString(address, length); + offset += length; + continue; + } + break; + } + + switch (data.level) { + case LogLevel::Trace: + state.logger->Debug("{}", logMessage.str()); + break; + case LogLevel::Info: + state.logger->Info("{}", logMessage.str()); + break; + case LogLevel::Warning: + state.logger->Warn("{}", logMessage.str()); + break; + case LogLevel::Error: + case LogLevel::Critical: + state.logger->Error("{}", logMessage.str()); + break; + } + + } + + void ILogger::SetDestination(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} +} diff --git a/app/src/main/cpp/skyline/services/lm/ILogger.h b/app/src/main/cpp/skyline/services/lm/ILogger.h new file mode 100644 index 00000000..3222c5c7 --- /dev/null +++ b/app/src/main/cpp/skyline/services/lm/ILogger.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include + +namespace skyline::service::lm { + /** + * @brief ILogger is used by applications to print messages to the system log (https://switchbrew.org/wiki/Log_services#ILogger) + */ + class ILogger : public BaseService { + private: + /** + * @brief This enumerates the field types in a log message + */ + enum class LogFieldType : u8 { + Start = 0, //!< This is the first log message in the stream + Stop = 1, //!< This is the final log message in the stream + Message = 2, //!< This log field contains a general message + Line = 3, //!< This log field contains a line number + Filename = 4, //!< This log field contains a filename + Function = 5, //!< This log field contains a function name + Module = 6, //!< This log field contains a module name + Thread = 7, //!< This log field contains a thread name + DropCount = 8, //!< This log field contains the number of dropped messages + Time = 9, //!< This log field contains a timestamp + ProgramName = 10, //!< This log field contains the program's name + }; + + /** + * @brief This enumerates the log levels for log messages + */ + enum class LogLevel : u8 { + Trace, //!< This is a trace log + Info, //!< This is an info log + Warning, //!< This is a warning log + Error, //!< This is an error log + Critical //!< This is a critical log + }; + + /** + * @brief Obtains a string containing the name of the given field type + * @param type The field type to return the name of + * @return The name of the given field type + */ + std::string GetFieldName(LogFieldType type); + + public: + ILogger(const DeviceState &state, ServiceManager &manager); + + /** + * @brief This prints a message to the log (https://switchbrew.org/wiki/Log_services#Log) + */ + void Log(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This sets the log destination (https://switchbrew.org/wiki/Log_services#SetDestination) + */ + void SetDestination(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index adfe7561..dacb3ed3 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -19,6 +19,7 @@ #include "pl/IPlatformServiceManager.h" #include "aocsrv/IAddOnContentManager.h" #include "pctl/IParentalControlServiceFactory.h" +#include "lm/ILogService.h" #include "serviceman.h" namespace skyline::service { @@ -82,6 +83,9 @@ namespace skyline::service { case Service::pctl_IParentalControlServiceFactory: serviceObj = std::make_shared(state, *this); break; + case Service::lm_ILogService: + serviceObj = std::make_shared(state, *this); + break; default: throw exception("CreateService called on missing object, type: {}", serviceType); }