lm.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <string>
  4. #include <optional>
  5. #include <unordered_map>
  6. #include <boost/container_hash/hash.hpp>
  7. #include "common/logging/log.h"
  8. #include "core/core.h"
  9. #include "core/hle/ipc_helpers.h"
  10. #include "core/hle/service/lm/lm.h"
  11. #include "core/hle/service/server_manager.h"
  12. #include "core/hle/service/service.h"
  13. namespace Service::LM {
  14. enum class LogSeverity : u8 {
  15. Trace = 0,
  16. Info = 1,
  17. Warning = 2,
  18. Error = 3,
  19. Fatal = 4,
  20. };
  21. // To keep flags out of hashing as well as the payload size
  22. struct LogPacketHeaderEntry {
  23. u64_le pid{};
  24. u64_le tid{};
  25. LogSeverity severity{};
  26. u8 verbosity{};
  27. auto operator<=>(const LogPacketHeaderEntry&) const = default;
  28. };
  29. } // namespace Service::LM
  30. namespace std {
  31. template <>
  32. struct hash<Service::LM::LogPacketHeaderEntry> {
  33. std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
  34. std::size_t seed{};
  35. boost::hash_combine(seed, k.pid);
  36. boost::hash_combine(seed, k.tid);
  37. boost::hash_combine(seed, k.severity);
  38. boost::hash_combine(seed, k.verbosity);
  39. return seed;
  40. }
  41. };
  42. } // namespace std
  43. namespace Service::LM {
  44. namespace {
  45. std::string_view NameOf(LogSeverity severity) {
  46. switch (severity) {
  47. case LogSeverity::Trace:
  48. return "TRACE";
  49. case LogSeverity::Info:
  50. return "INFO";
  51. case LogSeverity::Warning:
  52. return "WARNING";
  53. case LogSeverity::Error:
  54. return "ERROR";
  55. case LogSeverity::Fatal:
  56. return "FATAL";
  57. default:
  58. return "UNKNOWN";
  59. }
  60. }
  61. } // Anonymous namespace
  62. enum class LogDestination : u32 {
  63. TargetManager = 1 << 0,
  64. Uart = 1 << 1,
  65. UartSleep = 1 << 2,
  66. All = 0xffff,
  67. };
  68. DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
  69. enum class LogPacketFlags : u8 {
  70. Head = 1 << 0,
  71. Tail = 1 << 1,
  72. LittleEndian = 1 << 2,
  73. };
  74. DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
  75. class ILogger final : public ServiceFramework<ILogger> {
  76. public:
  77. explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
  78. static const FunctionInfo functions[] = {
  79. {0, &ILogger::Log, "Log"},
  80. {1, &ILogger::SetDestination, "SetDestination"},
  81. };
  82. RegisterHandlers(functions);
  83. }
  84. private:
  85. void Log(Kernel::HLERequestContext& ctx) {
  86. std::size_t offset{};
  87. const auto data = ctx.ReadBuffer();
  88. // This function only succeeds - Get that out of the way
  89. IPC::ResponseBuilder rb{ctx, 2};
  90. rb.Push(ResultSuccess);
  91. if (data.size() < sizeof(LogPacketHeader)) {
  92. LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
  93. return;
  94. }
  95. LogPacketHeader header{};
  96. std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
  97. offset += sizeof(LogPacketHeader);
  98. const LogPacketHeaderEntry entry{
  99. .pid = header.pid,
  100. .tid = header.tid,
  101. .severity = header.severity,
  102. .verbosity = header.verbosity,
  103. };
  104. if (True(header.flags & LogPacketFlags::Head)) {
  105. std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
  106. std::memcpy(tmp.data(), data.data() + offset, tmp.size());
  107. entries.insert_or_assign(entry, std::move(tmp));
  108. } else {
  109. const auto entry_iter = entries.find(entry);
  110. // Append to existing entry
  111. if (entry_iter == entries.cend()) {
  112. LOG_ERROR(Service_LM, "Log entry does not exist!");
  113. return;
  114. }
  115. auto& existing_entry = entry_iter->second;
  116. const auto base = existing_entry.size();
  117. existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
  118. std::memcpy(existing_entry.data() + base, data.data() + offset,
  119. (data.size() - sizeof(LogPacketHeader)));
  120. }
  121. if (True(header.flags & LogPacketFlags::Tail)) {
  122. auto it = entries.find(entry);
  123. if (it == entries.end()) {
  124. LOG_ERROR(Service_LM, "Log entry does not exist!");
  125. return;
  126. }
  127. ParseLog(it->first, it->second);
  128. entries.erase(it);
  129. }
  130. }
  131. void SetDestination(Kernel::HLERequestContext& ctx) {
  132. IPC::RequestParser rp{ctx};
  133. const auto log_destination = rp.PopEnum<LogDestination>();
  134. LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
  135. destination = log_destination;
  136. IPC::ResponseBuilder rb{ctx, 2};
  137. rb.Push(ResultSuccess);
  138. }
  139. u64 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
  140. u64 result{};
  141. u32 shift{};
  142. for (std::size_t i = 0; i < sizeof(u64); i++) {
  143. const auto v = data[offset];
  144. result |= (static_cast<u64>(v & 0x7f) << shift);
  145. shift += 7;
  146. offset++;
  147. if (offset >= data.size() || ((v & 0x80) == 0)) {
  148. break;
  149. }
  150. }
  151. return result;
  152. }
  153. std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
  154. std::size_t length) {
  155. if (length == 0) {
  156. return std::nullopt;
  157. }
  158. const auto length_to_read = std::min(length, data.size() - offset);
  159. std::string output(length_to_read, '\0');
  160. std::memcpy(output.data(), data.data() + offset, length_to_read);
  161. offset += length_to_read;
  162. return output;
  163. }
  164. u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
  165. ASSERT(length == sizeof(u32));
  166. u32_le output{};
  167. std::memcpy(&output, data.data() + offset, sizeof(u32));
  168. offset += length;
  169. return output;
  170. }
  171. u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
  172. ASSERT(length == sizeof(u64));
  173. u64_le output{};
  174. std::memcpy(&output, data.data() + offset, sizeof(u64));
  175. offset += length;
  176. return output;
  177. }
  178. void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
  179. // Possible entries
  180. std::optional<std::string> text_log;
  181. std::optional<u32> line_number;
  182. std::optional<std::string> file_name;
  183. std::optional<std::string> function_name;
  184. std::optional<std::string> module_name;
  185. std::optional<std::string> thread_name;
  186. std::optional<u64> log_pack_drop_count;
  187. std::optional<s64> user_system_clock;
  188. std::optional<std::string> process_name;
  189. std::size_t offset{};
  190. while (offset < log_data.size()) {
  191. const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
  192. const auto chunk_size = ReadLeb128(log_data, offset);
  193. switch (key) {
  194. case LogDataChunkKey::LogSessionBegin:
  195. case LogDataChunkKey::LogSessionEnd:
  196. break;
  197. case LogDataChunkKey::TextLog:
  198. text_log = ReadString(log_data, offset, chunk_size);
  199. break;
  200. case LogDataChunkKey::LineNumber:
  201. line_number = ReadAsU32(log_data, offset, chunk_size);
  202. break;
  203. case LogDataChunkKey::FileName:
  204. file_name = ReadString(log_data, offset, chunk_size);
  205. break;
  206. case LogDataChunkKey::FunctionName:
  207. function_name = ReadString(log_data, offset, chunk_size);
  208. break;
  209. case LogDataChunkKey::ModuleName:
  210. module_name = ReadString(log_data, offset, chunk_size);
  211. break;
  212. case LogDataChunkKey::ThreadName:
  213. thread_name = ReadString(log_data, offset, chunk_size);
  214. break;
  215. case LogDataChunkKey::LogPacketDropCount:
  216. log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
  217. break;
  218. case LogDataChunkKey::UserSystemClock:
  219. user_system_clock = ReadAsU64(log_data, offset, chunk_size);
  220. break;
  221. case LogDataChunkKey::ProcessName:
  222. process_name = ReadString(log_data, offset, chunk_size);
  223. break;
  224. }
  225. }
  226. std::string output_log{};
  227. if (process_name) {
  228. output_log += fmt::format("Process: {}\n", *process_name);
  229. }
  230. if (module_name) {
  231. output_log += fmt::format("Module: {}\n", *module_name);
  232. }
  233. if (file_name) {
  234. output_log += fmt::format("File: {}\n", *file_name);
  235. }
  236. if (function_name) {
  237. output_log += fmt::format("Function: {}\n", *function_name);
  238. }
  239. if (line_number && *line_number != 0) {
  240. output_log += fmt::format("Line: {}\n", *line_number);
  241. }
  242. output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
  243. output_log += fmt::format("ThreadID: {:X}\n", entry.tid);
  244. if (text_log) {
  245. output_log += fmt::format("Log Text: {}\n", *text_log);
  246. }
  247. LOG_DEBUG(Service_LM, "LogManager {} ({}):\n{}", NameOf(entry.severity),
  248. DestinationToString(destination), output_log);
  249. }
  250. static std::string DestinationToString(LogDestination destination) {
  251. if (True(destination & LogDestination::All)) {
  252. return "TargetManager | Uart | UartSleep";
  253. }
  254. std::string output{};
  255. if (True(destination & LogDestination::TargetManager)) {
  256. output += "| TargetManager";
  257. }
  258. if (True(destination & LogDestination::Uart)) {
  259. output += "| Uart";
  260. }
  261. if (True(destination & LogDestination::UartSleep)) {
  262. output += "| UartSleep";
  263. }
  264. if (output.length() > 0) {
  265. return output.substr(2);
  266. }
  267. return "No Destination";
  268. }
  269. enum class LogDataChunkKey : u32 {
  270. LogSessionBegin = 0,
  271. LogSessionEnd = 1,
  272. TextLog = 2,
  273. LineNumber = 3,
  274. FileName = 4,
  275. FunctionName = 5,
  276. ModuleName = 6,
  277. ThreadName = 7,
  278. LogPacketDropCount = 8,
  279. UserSystemClock = 9,
  280. ProcessName = 10,
  281. };
  282. struct LogPacketHeader {
  283. u64_le pid{};
  284. u64_le tid{};
  285. LogPacketFlags flags{};
  286. INSERT_PADDING_BYTES(1);
  287. LogSeverity severity{};
  288. u8 verbosity{};
  289. u32_le payload_size{};
  290. };
  291. static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
  292. std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
  293. LogDestination destination{LogDestination::All};
  294. };
  295. class LM final : public ServiceFramework<LM> {
  296. public:
  297. explicit LM(Core::System& system_) : ServiceFramework{system_, "lm"} {
  298. // clang-format off
  299. static const FunctionInfo functions[] = {
  300. {0, &LM::OpenLogger, "OpenLogger"},
  301. };
  302. // clang-format on
  303. RegisterHandlers(functions);
  304. }
  305. private:
  306. void OpenLogger(Kernel::HLERequestContext& ctx) {
  307. LOG_DEBUG(Service_LM, "called");
  308. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  309. rb.Push(ResultSuccess);
  310. rb.PushIpcInterface<ILogger>(system);
  311. }
  312. };
  313. void LoopProcess(Core::System& system) {
  314. auto server_manager = std::make_unique<ServerManager>(system);
  315. server_manager->RegisterNamedService("lm", std::make_shared<LM>(system));
  316. ServerManager::RunServer(std::move(server_manager));
  317. }
  318. } // namespace Service::LM