2018-01-13 13:22:39 -08:00
|
|
|
// Copyright 2018 yuzu emulator team
|
2017-10-14 19:18:42 -07:00
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2018-02-12 22:44:53 -08:00
|
|
|
#include <sstream>
|
2017-10-18 18:41:24 -07:00
|
|
|
#include <string>
|
2018-07-25 19:32:42 -07:00
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
#include <optional>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <boost/container_hash/hash.hpp>
|
2017-10-14 19:18:42 -07:00
|
|
|
#include "common/logging/log.h"
|
2019-06-29 14:17:35 -07:00
|
|
|
#include "common/scope_exit.h"
|
2020-11-08 12:49:45 -08:00
|
|
|
#include "core/core.h"
|
2017-10-14 19:18:42 -07:00
|
|
|
#include "core/hle/ipc_helpers.h"
|
|
|
|
#include "core/hle/service/lm/lm.h"
|
2018-07-25 19:32:42 -07:00
|
|
|
#include "core/hle/service/service.h"
|
|
|
|
#include "core/memory.h"
|
2017-10-14 19:18:42 -07:00
|
|
|
|
2018-04-19 18:41:44 -07:00
|
|
|
namespace Service::LM {
|
2021-01-19 23:25:15 -08:00
|
|
|
enum class LogSeverity : u8 {
|
|
|
|
Trace = 0,
|
|
|
|
Info = 1,
|
|
|
|
Warning = 2,
|
|
|
|
Error = 3,
|
|
|
|
Fatal = 4,
|
|
|
|
};
|
|
|
|
|
|
|
|
// To keep flags out of hashing as well as the payload size
|
|
|
|
struct LogPacketHeaderEntry {
|
|
|
|
u64_le pid{};
|
|
|
|
u64_le tid{};
|
|
|
|
LogSeverity severity{};
|
|
|
|
u8 verbosity{};
|
|
|
|
|
|
|
|
auto operator<=>(const LogPacketHeaderEntry&) const = default;
|
|
|
|
};
|
|
|
|
} // namespace Service::LM
|
|
|
|
|
|
|
|
namespace std {
|
|
|
|
template <>
|
|
|
|
struct hash<Service::LM::LogPacketHeaderEntry> {
|
2021-01-19 23:35:58 -08:00
|
|
|
std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
|
2021-01-19 23:25:15 -08:00
|
|
|
std::size_t seed{};
|
|
|
|
boost::hash_combine(seed, k.pid);
|
|
|
|
boost::hash_combine(seed, k.tid);
|
|
|
|
boost::hash_combine(seed, k.severity);
|
|
|
|
boost::hash_combine(seed, k.verbosity);
|
|
|
|
return seed;
|
2021-04-23 06:20:52 -07:00
|
|
|
}
|
2021-01-19 23:25:15 -08:00
|
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
|
|
|
|
namespace Service::LM {
|
|
|
|
|
|
|
|
enum class LogDestination : u32 {
|
|
|
|
TargetManager = 1 << 0,
|
|
|
|
Uart = 1 << 1,
|
|
|
|
UartSleep = 1 << 2,
|
|
|
|
All = 0xffff,
|
|
|
|
};
|
|
|
|
DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
|
|
|
|
|
|
|
|
enum class LogPacketFlags : u8 {
|
|
|
|
Head = 1 << 0,
|
|
|
|
Tail = 1 << 1,
|
|
|
|
LittleEndian = 1 << 2,
|
|
|
|
};
|
|
|
|
DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
|
2017-10-14 19:18:42 -07:00
|
|
|
|
2018-08-01 14:08:41 -07:00
|
|
|
class ILogger final : public ServiceFramework<ILogger> {
|
2017-10-18 18:41:24 -07:00
|
|
|
public:
|
2021-01-19 23:25:15 -08:00
|
|
|
explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
|
2017-10-18 18:41:24 -07:00
|
|
|
static const FunctionInfo functions[] = {
|
2019-06-29 14:18:33 -07:00
|
|
|
{0, &ILogger::Log, "Log"},
|
|
|
|
{1, &ILogger::SetDestination, "SetDestination"},
|
2017-10-18 18:41:24 -07:00
|
|
|
};
|
|
|
|
RegisterHandlers(functions);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2019-06-29 14:18:33 -07:00
|
|
|
void Log(Kernel::HLERequestContext& ctx) {
|
2021-01-19 23:25:15 -08:00
|
|
|
std::size_t offset{};
|
|
|
|
const auto data = ctx.ReadBuffer();
|
|
|
|
|
2018-01-04 21:45:13 -08:00
|
|
|
// This function only succeeds - Get that out of the way
|
2018-01-23 16:52:18 -08:00
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
2018-01-04 21:45:13 -08:00
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
if (data.size() < sizeof(LogPacketHeader)) {
|
|
|
|
LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
|
|
|
|
return;
|
|
|
|
}
|
2018-01-17 21:08:38 -08:00
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
LogPacketHeader header{};
|
|
|
|
std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
|
|
|
|
offset += sizeof(LogPacketHeader);
|
2018-01-17 21:08:38 -08:00
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
LogPacketHeaderEntry entry{
|
|
|
|
.pid = header.pid,
|
|
|
|
.tid = header.tid,
|
|
|
|
.severity = header.severity,
|
|
|
|
.verbosity = header.verbosity,
|
|
|
|
};
|
2018-01-17 21:08:38 -08:00
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
if (True(header.flags & LogPacketFlags::Head)) {
|
|
|
|
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
|
|
|
|
std::memcpy(tmp.data(), data.data() + offset, tmp.size());
|
|
|
|
entries[entry] = std::move(tmp);
|
|
|
|
} else {
|
|
|
|
// Append to existing entry
|
|
|
|
if (!entries.contains(entry)) {
|
|
|
|
LOG_ERROR(Service_LM, "Log entry does not exist!");
|
|
|
|
return;
|
2019-06-29 14:18:33 -07:00
|
|
|
}
|
2021-01-19 23:25:15 -08:00
|
|
|
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
|
2017-10-18 18:41:24 -07:00
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
auto& existing_entry = entries[entry];
|
|
|
|
const auto base = existing_entry.size();
|
|
|
|
existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
|
|
|
|
std::memcpy(existing_entry.data() + base, data.data() + offset,
|
|
|
|
(data.size() - sizeof(LogPacketHeader)));
|
2018-01-04 21:45:13 -08:00
|
|
|
}
|
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
if (True(header.flags & LogPacketFlags::Tail)) {
|
|
|
|
auto it = entries.find(entry);
|
|
|
|
if (it == entries.end()) {
|
|
|
|
LOG_ERROR(Service_LM, "Log entry does not exist!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ParseLog(it->first, it->second);
|
|
|
|
entries.erase(it);
|
|
|
|
}
|
2017-10-18 18:41:24 -07:00
|
|
|
}
|
2018-02-12 22:44:53 -08:00
|
|
|
|
2018-11-19 15:00:11 -08:00
|
|
|
void SetDestination(Kernel::HLERequestContext& ctx) {
|
2019-06-29 14:18:33 -07:00
|
|
|
IPC::RequestParser rp{ctx};
|
2021-01-19 23:25:15 -08:00
|
|
|
const auto log_destination = rp.PopEnum<LogDestination>();
|
2019-06-29 14:18:33 -07:00
|
|
|
|
2021-01-19 23:25:15 -08:00
|
|
|
LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
|
|
|
|
destination = log_destination;
|
2018-11-19 15:00:11 -08:00
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2021-02-07 04:52:56 -08:00
|
|
|
u64 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
|
|
|
|
u64 result{};
|
2021-01-19 23:25:15 -08:00
|
|
|
u32 shift{};
|
2021-02-07 04:52:56 -08:00
|
|
|
|
|
|
|
for (std::size_t i = 0; i < sizeof(u64); i++) {
|
|
|
|
const auto v = data[offset];
|
|
|
|
result |= (static_cast<u64>(v & 0x7f) << shift);
|
2021-01-19 23:25:15 -08:00
|
|
|
shift += 7;
|
|
|
|
offset++;
|
2021-02-07 04:52:56 -08:00
|
|
|
if (offset >= data.size() || ((v & 0x80) == 0)) {
|
2021-01-19 23:25:15 -08:00
|
|
|
break;
|
|
|
|
}
|
2021-02-07 04:52:56 -08:00
|
|
|
}
|
2021-01-19 23:25:15 -08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
|
|
|
|
std::size_t length) {
|
|
|
|
if (length == 0) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-01-22 23:24:57 -08:00
|
|
|
const auto length_to_read = std::min(length, data.size() - offset);
|
|
|
|
|
|
|
|
std::string output(length_to_read, '\0');
|
|
|
|
std::memcpy(output.data(), data.data() + offset, length_to_read);
|
|
|
|
offset += length_to_read;
|
2021-01-19 23:25:15 -08:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
|
|
|
|
ASSERT(length == sizeof(u32));
|
|
|
|
u32_le output{};
|
|
|
|
std::memcpy(&output, data.data() + offset, sizeof(u32));
|
|
|
|
offset += length;
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
|
|
|
|
ASSERT(length == sizeof(u64));
|
|
|
|
u64_le output{};
|
|
|
|
std::memcpy(&output, data.data() + offset, sizeof(u64));
|
|
|
|
offset += length;
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
|
|
|
|
// Possible entries
|
|
|
|
std::optional<std::string> text_log;
|
|
|
|
std::optional<u32> line_number;
|
|
|
|
std::optional<std::string> file_name;
|
|
|
|
std::optional<std::string> function_name;
|
|
|
|
std::optional<std::string> module_name;
|
|
|
|
std::optional<std::string> thread_name;
|
|
|
|
std::optional<u64> log_pack_drop_count;
|
|
|
|
std::optional<s64> user_system_clock;
|
|
|
|
std::optional<std::string> process_name;
|
|
|
|
|
|
|
|
std::size_t offset{};
|
|
|
|
while (offset < log_data.size()) {
|
|
|
|
const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
|
|
|
|
const auto chunk_size = ReadLeb128(log_data, offset);
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case LogDataChunkKey::LogSessionBegin:
|
|
|
|
case LogDataChunkKey::LogSessionEnd:
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::TextLog:
|
|
|
|
text_log = ReadString(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::LineNumber:
|
|
|
|
line_number = ReadAsU32(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::FileName:
|
|
|
|
file_name = ReadString(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::FunctionName:
|
|
|
|
function_name = ReadString(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::ModuleName:
|
|
|
|
module_name = ReadString(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::ThreadName:
|
|
|
|
thread_name = ReadString(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::LogPacketDropCount:
|
|
|
|
log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::UserSystemClock:
|
|
|
|
user_system_clock = ReadAsU64(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
case LogDataChunkKey::ProcessName:
|
|
|
|
process_name = ReadString(log_data, offset, chunk_size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string output_log{};
|
|
|
|
if (process_name) {
|
|
|
|
output_log += fmt::format("Process: {}\n", *process_name);
|
|
|
|
}
|
|
|
|
if (module_name) {
|
|
|
|
output_log += fmt::format("Module: {}\n", *module_name);
|
|
|
|
}
|
|
|
|
if (file_name) {
|
|
|
|
output_log += fmt::format("File: {}\n", *file_name);
|
|
|
|
}
|
|
|
|
if (function_name) {
|
|
|
|
output_log += fmt::format("Function: {}\n", *function_name);
|
|
|
|
}
|
|
|
|
if (line_number && *line_number != 0) {
|
|
|
|
output_log += fmt::format("Line: {}\n", *line_number);
|
|
|
|
}
|
2021-01-24 07:47:40 -08:00
|
|
|
output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
|
|
|
|
output_log += fmt::format("ThreadID: {:X}\n", entry.tid);
|
2021-01-19 23:25:15 -08:00
|
|
|
|
|
|
|
if (text_log) {
|
|
|
|
output_log += fmt::format("Log Text: {}\n", *text_log);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (entry.severity) {
|
|
|
|
case LogSeverity::Trace:
|
2021-02-07 04:52:56 -08:00
|
|
|
LOG_DEBUG(Service_LM, "LogManager TRACE ({}):\n{}", DestinationToString(destination),
|
2021-01-19 23:25:15 -08:00
|
|
|
output_log);
|
|
|
|
break;
|
|
|
|
case LogSeverity::Info:
|
|
|
|
LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
|
|
|
|
output_log);
|
|
|
|
break;
|
|
|
|
case LogSeverity::Warning:
|
|
|
|
LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
|
|
|
|
DestinationToString(destination), output_log);
|
|
|
|
break;
|
|
|
|
case LogSeverity::Error:
|
|
|
|
LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
|
|
|
|
output_log);
|
|
|
|
break;
|
|
|
|
case LogSeverity::Fatal:
|
|
|
|
LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
|
|
|
|
output_log);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
|
|
|
|
DestinationToString(destination), output_log);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-19 23:42:39 -08:00
|
|
|
static std::string DestinationToString(LogDestination destination) {
|
2021-01-19 23:25:15 -08:00
|
|
|
if (True(destination & LogDestination::All)) {
|
|
|
|
return "TargetManager | Uart | UartSleep";
|
|
|
|
}
|
|
|
|
std::string output{};
|
|
|
|
if (True(destination & LogDestination::TargetManager)) {
|
|
|
|
output += "| TargetManager";
|
|
|
|
}
|
|
|
|
if (True(destination & LogDestination::Uart)) {
|
|
|
|
output += "| Uart";
|
|
|
|
}
|
|
|
|
if (True(destination & LogDestination::UartSleep)) {
|
|
|
|
output += "| UartSleep";
|
|
|
|
}
|
|
|
|
if (output.length() > 0) {
|
|
|
|
return output.substr(2);
|
|
|
|
}
|
|
|
|
return "No Destination";
|
|
|
|
}
|
|
|
|
|
|
|
|
enum class LogDataChunkKey : u32 {
|
|
|
|
LogSessionBegin = 0,
|
|
|
|
LogSessionEnd = 1,
|
|
|
|
TextLog = 2,
|
|
|
|
LineNumber = 3,
|
|
|
|
FileName = 4,
|
|
|
|
FunctionName = 5,
|
|
|
|
ModuleName = 6,
|
|
|
|
ThreadName = 7,
|
|
|
|
LogPacketDropCount = 8,
|
|
|
|
UserSystemClock = 9,
|
|
|
|
ProcessName = 10,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct LogPacketHeader {
|
|
|
|
u64_le pid{};
|
|
|
|
u64_le tid{};
|
|
|
|
LogPacketFlags flags{};
|
|
|
|
INSERT_PADDING_BYTES(1);
|
|
|
|
LogSeverity severity{};
|
|
|
|
u8 verbosity{};
|
|
|
|
u32_le payload_size{};
|
|
|
|
};
|
|
|
|
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
|
|
|
|
|
|
|
|
std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
|
|
|
LogDestination destination{LogDestination::All};
|
2017-10-18 18:41:24 -07:00
|
|
|
};
|
|
|
|
|
2018-07-25 19:32:42 -07:00
|
|
|
class LM final : public ServiceFramework<LM> {
|
|
|
|
public:
|
2020-11-26 12:19:08 -08:00
|
|
|
explicit LM(Core::System& system_) : ServiceFramework{system_, "lm"} {
|
2019-06-29 14:17:35 -07:00
|
|
|
// clang-format off
|
2018-07-25 19:32:42 -07:00
|
|
|
static const FunctionInfo functions[] = {
|
2019-06-29 14:17:35 -07:00
|
|
|
{0, &LM::OpenLogger, "OpenLogger"},
|
2018-07-25 19:32:42 -07:00
|
|
|
};
|
2019-06-29 14:17:35 -07:00
|
|
|
// clang-format on
|
|
|
|
|
2018-07-25 19:32:42 -07:00
|
|
|
RegisterHandlers(functions);
|
|
|
|
}
|
2017-10-14 19:18:42 -07:00
|
|
|
|
2019-06-29 14:17:35 -07:00
|
|
|
private:
|
2018-07-25 19:32:42 -07:00
|
|
|
void OpenLogger(Kernel::HLERequestContext& ctx) {
|
2018-11-25 22:06:13 -08:00
|
|
|
LOG_DEBUG(Service_LM, "called");
|
|
|
|
|
2018-07-25 19:32:42 -07:00
|
|
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
2020-11-26 12:19:08 -08:00
|
|
|
rb.PushIpcInterface<ILogger>(system);
|
2018-07-25 19:32:42 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-06-29 14:17:35 -07:00
|
|
|
void InstallInterfaces(Core::System& system) {
|
2020-11-26 12:19:08 -08:00
|
|
|
std::make_shared<LM>(system)->InstallAsService(system.ServiceManager());
|
2017-10-14 19:18:42 -07:00
|
|
|
}
|
|
|
|
|
2018-04-19 18:41:44 -07:00
|
|
|
} // namespace Service::LM
|