lm.cpp 12 KB

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