summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Chloe Marcec2021-01-20 18:25:15 +1100
committerGravatar Chloe Marcec2021-01-20 18:25:15 +1100
commit83f8c1a25ef9a65eac0d000e41373bc212c5ba1c (patch)
tree8dc5bd39652d0a3ada62e67fcae90f67564fd656 /src
parentMerge pull request #5755 from FearlessTobi/port-5344 (diff)
downloadyuzu-83f8c1a25ef9a65eac0d000e41373bc212c5ba1c.tar.gz
yuzu-83f8c1a25ef9a65eac0d000e41373bc212c5ba1c.tar.xz
yuzu-83f8c1a25ef9a65eac0d000e41373bc212c5ba1c.zip
lm: Recode LM service
Rework the service to spit out to logs instead of a seperate file as well as fix any crashes caused by lm.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp12
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/hle/service/lm/lm.cpp317
-rw-r--r--src/core/hle/service/lm/manager.cpp134
-rw-r--r--src/core/hle/service/lm/manager.h106
-rw-r--r--src/core/reporter.cpp50
-rw-r--r--src/core/reporter.h3
8 files changed, 286 insertions, 345 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 2f6b22747..9c9b3195b 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -400,8 +400,6 @@ add_library(core STATIC
400 hle/service/ldr/ldr.h 400 hle/service/ldr/ldr.h
401 hle/service/lm/lm.cpp 401 hle/service/lm/lm.cpp
402 hle/service/lm/lm.h 402 hle/service/lm/lm.h
403 hle/service/lm/manager.cpp
404 hle/service/lm/manager.h
405 hle/service/mig/mig.cpp 403 hle/service/mig/mig.cpp
406 hle/service/mig/mig.h 404 hle/service/mig/mig.h
407 hle/service/mii/manager.cpp 405 hle/service/mii/manager.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 1a2002dec..86bdc7f6b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -36,7 +36,6 @@
36#include "core/hle/service/apm/controller.h" 36#include "core/hle/service/apm/controller.h"
37#include "core/hle/service/filesystem/filesystem.h" 37#include "core/hle/service/filesystem/filesystem.h"
38#include "core/hle/service/glue/manager.h" 38#include "core/hle/service/glue/manager.h"
39#include "core/hle/service/lm/manager.h"
40#include "core/hle/service/service.h" 39#include "core/hle/service/service.h"
41#include "core/hle/service/sm/sm.h" 40#include "core/hle/service/sm/sm.h"
42#include "core/hle/service/time/time_manager.h" 41#include "core/hle/service/time/time_manager.h"
@@ -293,8 +292,6 @@ struct System::Impl {
293 perf_stats->GetMeanFrametime()); 292 perf_stats->GetMeanFrametime());
294 } 293 }
295 294
296 lm_manager.Flush();
297
298 is_powered_on = false; 295 is_powered_on = false;
299 exit_lock = false; 296 exit_lock = false;
300 297
@@ -398,7 +395,6 @@ struct System::Impl {
398 395
399 /// Service State 396 /// Service State
400 Service::Glue::ARPManager arp_manager; 397 Service::Glue::ARPManager arp_manager;
401 Service::LM::Manager lm_manager{reporter};
402 Service::Time::TimeManager time_manager; 398 Service::Time::TimeManager time_manager;
403 399
404 /// Service manager 400 /// Service manager
@@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const {
720 return impl->apm_controller; 716 return impl->apm_controller;
721} 717}
722 718
723Service::LM::Manager& System::GetLogManager() {
724 return impl->lm_manager;
725}
726
727const Service::LM::Manager& System::GetLogManager() const {
728 return impl->lm_manager;
729}
730
731Service::Time::TimeManager& System::GetTimeManager() { 719Service::Time::TimeManager& System::GetTimeManager() {
732 return impl->time_manager; 720 return impl->time_manager;
733} 721}
diff --git a/src/core/core.h b/src/core/core.h
index 579a774e4..3a8e040c1 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -62,10 +62,6 @@ namespace Glue {
62class ARPManager; 62class ARPManager;
63} 63}
64 64
65namespace LM {
66class Manager;
67} // namespace LM
68
69namespace SM { 65namespace SM {
70class ServiceManager; 66class ServiceManager;
71} // namespace SM 67} // namespace SM
@@ -351,9 +347,6 @@ public:
351 [[nodiscard]] Service::APM::Controller& GetAPMController(); 347 [[nodiscard]] Service::APM::Controller& GetAPMController();
352 [[nodiscard]] const Service::APM::Controller& GetAPMController() const; 348 [[nodiscard]] const Service::APM::Controller& GetAPMController() const;
353 349
354 [[nodiscard]] Service::LM::Manager& GetLogManager();
355 [[nodiscard]] const Service::LM::Manager& GetLogManager() const;
356
357 [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); 350 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
358 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; 351 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
359 352
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 8e49b068c..f4f6533ad 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -5,22 +5,71 @@
5#include <sstream> 5#include <sstream>
6#include <string> 6#include <string>
7 7
8#include <optional>
9#include <unordered_map>
10#include <boost/container_hash/hash.hpp>
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9#include "common/scope_exit.h" 12#include "common/scope_exit.h"
10#include "core/core.h" 13#include "core/core.h"
11#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
12#include "core/hle/service/lm/lm.h" 15#include "core/hle/service/lm/lm.h"
13#include "core/hle/service/lm/manager.h"
14#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
15#include "core/memory.h" 17#include "core/memory.h"
16 18
17namespace Service::LM { 19namespace Service::LM {
20enum class LogSeverity : u8 {
21 Trace = 0,
22 Info = 1,
23 Warning = 2,
24 Error = 3,
25 Fatal = 4,
26};
27
28// To keep flags out of hashing as well as the payload size
29struct LogPacketHeaderEntry {
30 u64_le pid{};
31 u64_le tid{};
32 LogSeverity severity{};
33 u8 verbosity{};
34
35 auto operator<=>(const LogPacketHeaderEntry&) const = default;
36};
37} // namespace Service::LM
38
39namespace std {
40template <>
41struct hash<Service::LM::LogPacketHeaderEntry> {
42 std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const {
43 std::size_t seed{};
44 boost::hash_combine(seed, k.pid);
45 boost::hash_combine(seed, k.tid);
46 boost::hash_combine(seed, k.severity);
47 boost::hash_combine(seed, k.verbosity);
48 return seed;
49 };
50};
51} // namespace std
52
53namespace Service::LM {
54
55enum class LogDestination : u32 {
56 TargetManager = 1 << 0,
57 Uart = 1 << 1,
58 UartSleep = 1 << 2,
59 All = 0xffff,
60};
61DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
62
63enum class LogPacketFlags : u8 {
64 Head = 1 << 0,
65 Tail = 1 << 1,
66 LittleEndian = 1 << 2,
67};
68DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
18 69
19class ILogger final : public ServiceFramework<ILogger> { 70class ILogger final : public ServiceFramework<ILogger> {
20public: 71public:
21 explicit ILogger(Core::System& system_) 72 explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
22 : ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()},
23 memory{system_.Memory()} {
24 static const FunctionInfo functions[] = { 73 static const FunctionInfo functions[] = {
25 {0, &ILogger::Log, "Log"}, 74 {0, &ILogger::Log, "Log"},
26 {1, &ILogger::SetDestination, "SetDestination"}, 75 {1, &ILogger::SetDestination, "SetDestination"},
@@ -30,54 +79,260 @@ public:
30 79
31private: 80private:
32 void Log(Kernel::HLERequestContext& ctx) { 81 void Log(Kernel::HLERequestContext& ctx) {
82 std::size_t offset{};
83 const auto data = ctx.ReadBuffer();
84
33 // This function only succeeds - Get that out of the way 85 // This function only succeeds - Get that out of the way
34 IPC::ResponseBuilder rb{ctx, 2}; 86 IPC::ResponseBuilder rb{ctx, 2};
35 rb.Push(RESULT_SUCCESS); 87 rb.Push(RESULT_SUCCESS);
36 88
37 // Read MessageHeader, despite not doing anything with it right now 89 if (data.size() < sizeof(LogPacketHeader)) {
38 MessageHeader header{}; 90 LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
39 VAddr addr{ctx.BufferDescriptorX()[0].Address()}; 91 return;
40 const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; 92 }
41 memory.ReadBlock(addr, &header, sizeof(MessageHeader));
42 addr += sizeof(MessageHeader);
43
44 FieldMap fields;
45 while (addr < end_addr) {
46 const auto field = static_cast<Field>(memory.Read8(addr++));
47 const auto length = memory.Read8(addr++);
48 93
49 if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) { 94 LogPacketHeader header{};
50 ++addr; 95 std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
51 } 96 offset += sizeof(LogPacketHeader);
52 97
53 SCOPE_EXIT({ addr += length; }); 98 LogPacketHeaderEntry entry{
99 .pid = header.pid,
100 .tid = header.tid,
101 .severity = header.severity,
102 .verbosity = header.verbosity,
103 };
54 104
55 if (field == Field::Skip) { 105 if (True(header.flags & LogPacketFlags::Head)) {
56 continue; 106 std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
107 std::memcpy(tmp.data(), data.data() + offset, tmp.size());
108 entries[entry] = std::move(tmp);
109 } else {
110 // Append to existing entry
111 if (!entries.contains(entry)) {
112 LOG_ERROR(Service_LM, "Log entry does not exist!");
113 return;
57 } 114 }
115 std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
58 116
59 std::vector<u8> data(length); 117 auto& existing_entry = entries[entry];
60 memory.ReadBlock(addr, data.data(), length); 118 const auto base = existing_entry.size();
61 fields.emplace(field, std::move(data)); 119 existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
120 std::memcpy(existing_entry.data() + base, data.data() + offset,
121 (data.size() - sizeof(LogPacketHeader)));
62 } 122 }
63 123
64 manager.Log({header, std::move(fields)}); 124 if (True(header.flags & LogPacketFlags::Tail)) {
125 auto it = entries.find(entry);
126 if (it == entries.end()) {
127 LOG_ERROR(Service_LM, "Log entry does not exist!");
128 return;
129 }
130 ParseLog(it->first, it->second);
131 entries.erase(it);
132 }
65 } 133 }
66 134
67 void SetDestination(Kernel::HLERequestContext& ctx) { 135 void SetDestination(Kernel::HLERequestContext& ctx) {
68 IPC::RequestParser rp{ctx}; 136 IPC::RequestParser rp{ctx};
69 const auto destination = rp.PopEnum<DestinationFlag>(); 137 const auto log_destination = rp.PopEnum<LogDestination>();
70
71 LOG_DEBUG(Service_LM, "called, destination={:08X}", destination);
72 138
73 manager.SetDestination(destination); 139 LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
140 destination = log_destination;
74 141
75 IPC::ResponseBuilder rb{ctx, 2}; 142 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(RESULT_SUCCESS); 143 rb.Push(RESULT_SUCCESS);
77 } 144 }
78 145
79 Manager& manager; 146 u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
80 Core::Memory::Memory& memory; 147 u32 result{};
148 u32 shift{};
149 do {
150 result |= (data[offset] & 0x7f) << shift;
151 shift += 7;
152 offset++;
153 if (offset >= data.size()) {
154 break;
155 }
156 } while ((data[offset] & 0x80) != 0);
157 return result;
158 }
159
160 std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
161 std::size_t length) {
162 if (length == 0) {
163 return std::nullopt;
164 }
165 std::string output(length, '\0');
166 std::memcpy(output.data(), data.data() + offset, length);
167 offset += length;
168 return output;
169 }
170
171 u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
172 ASSERT(length == sizeof(u32));
173 u32_le output{};
174 std::memcpy(&output, data.data() + offset, sizeof(u32));
175 offset += length;
176 return output;
177 }
178
179 u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
180 ASSERT(length == sizeof(u64));
181 u64_le output{};
182 std::memcpy(&output, data.data() + offset, sizeof(u64));
183 offset += length;
184 return output;
185 }
186
187 void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
188 // Possible entries
189 std::optional<std::string> text_log;
190 std::optional<u32> line_number;
191 std::optional<std::string> file_name;
192 std::optional<std::string> function_name;
193 std::optional<std::string> module_name;
194 std::optional<std::string> thread_name;
195 std::optional<u64> log_pack_drop_count;
196 std::optional<s64> user_system_clock;
197 std::optional<std::string> process_name;
198
199 std::size_t offset{};
200 while (offset < log_data.size()) {
201 const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
202 const auto chunk_size = ReadLeb128(log_data, offset);
203
204 switch (key) {
205 case LogDataChunkKey::LogSessionBegin:
206 case LogDataChunkKey::LogSessionEnd:
207 break;
208 case LogDataChunkKey::TextLog:
209 text_log = ReadString(log_data, offset, chunk_size);
210 break;
211 case LogDataChunkKey::LineNumber:
212 line_number = ReadAsU32(log_data, offset, chunk_size);
213 break;
214 case LogDataChunkKey::FileName:
215 file_name = ReadString(log_data, offset, chunk_size);
216 break;
217 case LogDataChunkKey::FunctionName:
218 function_name = ReadString(log_data, offset, chunk_size);
219 break;
220 case LogDataChunkKey::ModuleName:
221 module_name = ReadString(log_data, offset, chunk_size);
222 break;
223 case LogDataChunkKey::ThreadName:
224 thread_name = ReadString(log_data, offset, chunk_size);
225 break;
226 case LogDataChunkKey::LogPacketDropCount:
227 log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
228 break;
229 case LogDataChunkKey::UserSystemClock:
230 user_system_clock = ReadAsU64(log_data, offset, chunk_size);
231 break;
232 case LogDataChunkKey::ProcessName:
233 process_name = ReadString(log_data, offset, chunk_size);
234 break;
235 }
236 }
237
238 std::string output_log{};
239 if (process_name) {
240 output_log += fmt::format("Process: {}\n", *process_name);
241 }
242 if (module_name) {
243 output_log += fmt::format("Module: {}\n", *module_name);
244 }
245 if (file_name) {
246 output_log += fmt::format("File: {}\n", *file_name);
247 }
248 if (function_name) {
249 output_log += fmt::format("Function: {}\n", *function_name);
250 }
251 if (line_number && *line_number != 0) {
252 output_log += fmt::format("Line: {}\n", *line_number);
253 }
254 output_log += fmt::format("ProcessID: {}\n", entry.pid);
255 output_log += fmt::format("ThreadID: {}\n", entry.tid);
256
257 if (text_log) {
258 output_log += fmt::format("Log Text: {}\n", *text_log);
259 }
260
261 switch (entry.severity) {
262 case LogSeverity::Trace:
263 LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination),
264 output_log);
265 break;
266 case LogSeverity::Info:
267 LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
268 output_log);
269 break;
270 case LogSeverity::Warning:
271 LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
272 DestinationToString(destination), output_log);
273 break;
274 case LogSeverity::Error:
275 LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
276 output_log);
277 break;
278 case LogSeverity::Fatal:
279 LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
280 output_log);
281 break;
282 default:
283 LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
284 DestinationToString(destination), output_log);
285 break;
286 }
287 }
288
289 std::string DestinationToString(LogDestination destination) {
290 if (True(destination & LogDestination::All)) {
291 return "TargetManager | Uart | UartSleep";
292 }
293 std::string output{};
294 if (True(destination & LogDestination::TargetManager)) {
295 output += "| TargetManager";
296 }
297 if (True(destination & LogDestination::Uart)) {
298 output += "| Uart";
299 }
300 if (True(destination & LogDestination::UartSleep)) {
301 output += "| UartSleep";
302 }
303 if (output.length() > 0) {
304 return output.substr(2);
305 }
306 return "No Destination";
307 }
308
309 enum class LogDataChunkKey : u32 {
310 LogSessionBegin = 0,
311 LogSessionEnd = 1,
312 TextLog = 2,
313 LineNumber = 3,
314 FileName = 4,
315 FunctionName = 5,
316 ModuleName = 6,
317 ThreadName = 7,
318 LogPacketDropCount = 8,
319 UserSystemClock = 9,
320 ProcessName = 10,
321 };
322
323 struct LogPacketHeader {
324 u64_le pid{};
325 u64_le tid{};
326 LogPacketFlags flags{};
327 INSERT_PADDING_BYTES(1);
328 LogSeverity severity{};
329 u8 verbosity{};
330 u32_le payload_size{};
331 };
332 static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
333
334 std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
335 LogDestination destination{LogDestination::All};
81}; 336};
82 337
83class LM final : public ServiceFramework<LM> { 338class LM final : public ServiceFramework<LM> {
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp
deleted file mode 100644
index 3ee2374e7..000000000
--- a/src/core/hle/service/lm/manager.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "common/string_util.h"
8#include "core/hle/service/lm/manager.h"
9#include "core/reporter.h"
10
11namespace Service::LM {
12
13std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
14 std::vector<std::string> array;
15 const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
16 if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
17 array.emplace_back(std::move(name));
18 }
19 };
20
21 check_single_flag(DestinationFlag::Default, "Default");
22 check_single_flag(DestinationFlag::UART, "UART");
23 check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
24
25 os << "[";
26 for (const auto& entry : array) {
27 os << entry << ", ";
28 }
29 return os << "]";
30}
31
32std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
33 switch (severity) {
34 case MessageHeader::Severity::Trace:
35 return os << "Trace";
36 case MessageHeader::Severity::Info:
37 return os << "Info";
38 case MessageHeader::Severity::Warning:
39 return os << "Warning";
40 case MessageHeader::Severity::Error:
41 return os << "Error";
42 case MessageHeader::Severity::Critical:
43 return os << "Critical";
44 default:
45 return os << fmt::format("{:08X}", static_cast<u32>(severity));
46 }
47}
48
49std::ostream& operator<<(std::ostream& os, Field field) {
50 switch (field) {
51 case Field::Skip:
52 return os << "Skip";
53 case Field::Message:
54 return os << "Message";
55 case Field::Line:
56 return os << "Line";
57 case Field::Filename:
58 return os << "Filename";
59 case Field::Function:
60 return os << "Function";
61 case Field::Module:
62 return os << "Module";
63 case Field::Thread:
64 return os << "Thread";
65 default:
66 return os << fmt::format("{:08X}", static_cast<u32>(field));
67 }
68}
69
70std::string FormatField(Field type, const std::vector<u8>& data) {
71 switch (type) {
72 case Field::Skip:
73 return "";
74 case Field::Line:
75 if (data.size() >= sizeof(u32)) {
76 u32 line;
77 std::memcpy(&line, data.data(), sizeof(u32));
78 return fmt::format("{}", line);
79 }
80 return "[ERROR DECODING LINE NUMBER]";
81 case Field::Message:
82 case Field::Filename:
83 case Field::Function:
84 case Field::Module:
85 case Field::Thread:
86 return Common::StringFromFixedZeroTerminatedBuffer(
87 reinterpret_cast<const char*>(data.data()), data.size());
88 default:
89 UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
90 return "";
91 }
92}
93
94Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
95
96Manager::~Manager() = default;
97
98void Manager::SetEnabled(bool enabled) {
99 this->enabled = enabled;
100}
101
102void Manager::SetDestination(DestinationFlag destination) {
103 this->destination = destination;
104}
105
106void Manager::Log(LogMessage message) {
107 if (message.header.IsHeadLog()) {
108 InitializeLog();
109 }
110
111 current_log.emplace_back(std::move(message));
112
113 if (current_log.back().header.IsTailLog()) {
114 FinalizeLog();
115 }
116}
117
118void Manager::Flush() {
119 FinalizeLog();
120}
121
122void Manager::InitializeLog() {
123 current_log.clear();
124
125 LOG_INFO(Service_LM, "Initialized new log session");
126}
127
128void Manager::FinalizeLog() {
129 reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
130
131 LOG_INFO(Service_LM, "Finalized current log session");
132}
133
134} // namespace Service::LM
diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h
deleted file mode 100644
index 544e636ba..000000000
--- a/src/core/hle/service/lm/manager.h
+++ /dev/null
@@ -1,106 +0,0 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <ostream>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12#include "common/swap.h"
13
14namespace Core {
15class Reporter;
16}
17
18namespace Service::LM {
19
20enum class DestinationFlag : u32 {
21 Default = 1,
22 UART = 2,
23 UARTSleeping = 4,
24
25 All = 0xFFFF,
26};
27
28struct MessageHeader {
29 enum Flags : u32_le {
30 IsHead = 1,
31 IsTail = 2,
32 };
33 enum Severity : u32_le {
34 Trace,
35 Info,
36 Warning,
37 Error,
38 Critical,
39 };
40
41 u64_le pid;
42 u64_le thread_context;
43 union {
44 BitField<0, 16, Flags> flags;
45 BitField<16, 8, Severity> severity;
46 BitField<24, 8, u32> verbosity;
47 };
48 u32_le payload_size;
49
50 bool IsHeadLog() const {
51 return flags & IsHead;
52 }
53 bool IsTailLog() const {
54 return flags & IsTail;
55 }
56};
57static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
58
59enum class Field : u8 {
60 Skip = 1,
61 Message = 2,
62 Line = 3,
63 Filename = 4,
64 Function = 5,
65 Module = 6,
66 Thread = 7,
67};
68
69std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
70std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
71std::ostream& operator<<(std::ostream& os, Field field);
72
73using FieldMap = std::map<Field, std::vector<u8>>;
74
75struct LogMessage {
76 MessageHeader header;
77 FieldMap fields;
78};
79
80std::string FormatField(Field type, const std::vector<u8>& data);
81
82class Manager {
83public:
84 explicit Manager(Core::Reporter& reporter);
85 ~Manager();
86
87 void SetEnabled(bool enabled);
88 void SetDestination(DestinationFlag destination);
89
90 void Log(LogMessage message);
91
92 void Flush();
93
94private:
95 void InitializeLog();
96 void FinalizeLog();
97
98 bool enabled = true;
99 DestinationFlag destination = DestinationFlag::All;
100
101 std::vector<LogMessage> current_log;
102
103 Core::Reporter& reporter;
104};
105
106} // namespace Service::LM
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 0becdf642..f199c3362 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -20,7 +20,6 @@
20#include "core/hle/kernel/memory/page_table.h" 20#include "core/hle/kernel/memory/page_table.h"
21#include "core/hle/kernel/process.h" 21#include "core/hle/kernel/process.h"
22#include "core/hle/result.h" 22#include "core/hle/result.h"
23#include "core/hle/service/lm/manager.h"
24#include "core/memory.h" 23#include "core/memory.h"
25#include "core/reporter.h" 24#include "core/reporter.h"
26#include "core/settings.h" 25#include "core/settings.h"
@@ -360,55 +359,6 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
360 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); 359 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
361} 360}
362 361
363void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const {
364 if (!IsReportingEnabled()) {
365 return;
366 }
367
368 const auto timestamp = GetTimestamp();
369 json out;
370
371 out["yuzu_version"] = GetYuzuVersionData();
372 out["report_common"] =
373 GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp);
374
375 out["log_destination"] =
376 fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination));
377
378 auto json_messages = json::array();
379 std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages),
380 [](const Service::LM::LogMessage& message) {
381 json out;
382 out["is_head"] = fmt::format("{}", message.header.IsHeadLog());
383 out["is_tail"] = fmt::format("{}", message.header.IsTailLog());
384 out["pid"] = fmt::format("{:016X}", message.header.pid);
385 out["thread_context"] =
386 fmt::format("{:016X}", message.header.thread_context);
387 out["payload_size"] = fmt::format("{:016X}", message.header.payload_size);
388 out["flags"] = fmt::format("{:04X}", message.header.flags.Value());
389 out["severity"] = fmt::format("{}", message.header.severity.Value());
390 out["verbosity"] = fmt::format("{:02X}", message.header.verbosity);
391
392 auto fields = json::array();
393 std::transform(message.fields.begin(), message.fields.end(),
394 std::back_inserter(fields), [](const auto& kv) {
395 json out;
396 out["type"] = fmt::format("{}", kv.first);
397 out["data"] =
398 Service::LM::FormatField(kv.first, kv.second);
399 return out;
400 });
401
402 out["fields"] = std::move(fields);
403 return out;
404 });
405
406 out["log_messages"] = std::move(json_messages);
407
408 SaveToFile(std::move(out),
409 GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp));
410}
411
412void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, 362void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
413 std::string log_message) const { 363 std::string log_message) const {
414 if (!IsReportingEnabled()) 364 if (!IsReportingEnabled())
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 86d760cf0..b2c2d9a2e 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -72,9 +72,6 @@ public:
72 void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, 72 void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode,
73 std::string log_message) const; 73 std::string log_message) const;
74 74
75 // Used by lm services
76 void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const;
77
78 // Can be used anywhere to generate a backtrace and general info report at any point during 75 // Can be used anywhere to generate a backtrace and general info report at any point during
79 // execution. Not intended to be used for anything other than debugging or testing. 76 // execution. Not intended to be used for anything other than debugging or testing.
80 void SaveUserReport() const; 77 void SaveUserReport() const;