Implement log services (lm)

lm is used by applications to print messages to the system log. Log
messages are made up of a header and then several fields containing
metadata or string messages.
This commit is contained in:
Billy Laws 2020-07-04 19:56:33 +01:00 committed by ◱ PixelyIon
parent 8985fe705f
commit 3a343d3a48
7 changed files with 212 additions and 1 deletions

View File

@ -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

View File

@ -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;

View File

@ -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<ILogger>(state, manager), session, response);
}
}

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
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);
};
}

View File

@ -0,0 +1,102 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h>
#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<Data>(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<LogFieldType>(request.inputBuf.at(0).address + offset++);
auto length = state.process->GetObject<u8>(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<u32>(address);
offset += sizeof(u32);
continue;
case LogFieldType::DropCount:
logMessage << GetFieldName(fieldType) << ": " << state.process->GetObject<u64>(address);
offset += sizeof(u64);
continue;
case LogFieldType::Time:
logMessage << GetFieldName(fieldType) << ": " << state.process->GetObject<u64>(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) {}
}

View File

@ -0,0 +1,63 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
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);
};
}

View File

@ -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<pctl::IParentalControlServiceFactory>(state, *this);
break;
case Service::lm_ILogService:
serviceObj = std::make_shared<lm::ILogService>(state, *this);
break;
default:
throw exception("CreateService called on missing object, type: {}", serviceType);
}