diff --git a/nx/include/switch/runtime/diag.h b/nx/include/switch/runtime/diag.h index 72b5ccb6..0c47852f 100644 --- a/nx/include/switch/runtime/diag.h +++ b/nx/include/switch/runtime/diag.h @@ -6,11 +6,7 @@ */ #pragma once -#include "../services/lm.h" -#include -#include -#include -#include +#include "../types.h" /// DiagLogSeverity typedef enum { @@ -21,356 +17,27 @@ typedef enum { DiagLogSeverity_Fatal = 4 } DiagLogSeverity; -/// DiagLogPacketFlags -typedef enum { - DiagLogPacketFlags_Head = BIT(0), ///< First packet. - DiagLogPacketFlags_Tail = BIT(1) ///< Last packet. -} DiagLogPacketFlags; - -/// DiagLogPacketHeader +/// DiagSourceInfo typedef struct { - u64 process_id; ///< Process ID. - u64 thread_id; ///< Thread ID. - u8 flags; ///< \ref DiagLogPacketFlags - u8 pad; ///< Padding - u8 severity; ///< \ref DiagLogSeverity - u8 verbosity; ///< Verbosity. - u32 payload_size; ///< Total packet size after this header. -} DiagLogPacketHeader; - -/// DiagLogDataChunkKey -typedef enum { - DiagLogDataChunkKey_LogSessionBegin = 0, ///< Log session begin (unknown) - DiagLogDataChunkKey_LogSessionEnd = 1, ///< Log session end (unknown) - DiagLogDataChunkKey_TextLog = 2, ///< Text to be logged. - DiagLogDataChunkKey_LineNumber = 3, ///< Source line number. - DiagLogDataChunkKey_FileName = 4, ///< Source file name. - DiagLogDataChunkKey_FunctionName = 5, ///< Source function name. - DiagLogDataChunkKey_ModuleName = 6, ///< Process module name. - DiagLogDataChunkKey_ThreadName = 7, ///< Process thread name. - DiagLogDataChunkKey_LogPacketDropCount = 8, ///< Log packet drop count (unknown) - DiagLogDataChunkKey_UserSystemClock = 9, ///< User system clock (unknown) - DiagLogDataChunkKey_ProcessName = 10 ///< Process name. -} DiagLogDataChunkKey; - -/// DiagLogDataChunkTypeHeader (see specific types below) -typedef struct { - u8 chunk_key; ///< \ref DiagLogDataChunkKey - u8 chunk_len; ///< Value length. -} DiagLogDataChunkTypeHeader; - -// Specific chunk types. - -NX_CONSTEXPR void diagChunkTypeCreate(void *chunk_type, size_t chunk_type_size, DiagLogDataChunkTypeHeader *header, DiagLogDataChunkKey key) { - __builtin_memset(chunk_type, 0, chunk_type_size); - header->chunk_key = key; - header->chunk_len = 0; -} - -typedef struct { - DiagLogDataChunkTypeHeader header; - u8 value; -} DiagU8ChunkType; - -NX_CONSTEXPR void diagU8ChunkTypeCreate(DiagU8ChunkType *chunk_type, DiagLogDataChunkKey key) { - diagChunkTypeCreate(chunk_type, sizeof(DiagU8ChunkType), &chunk_type->header, key); -} - -NX_CONSTEXPR void diagU8ChunkTypeSetValue(DiagU8ChunkType *chunk_type, DiagLogDataChunkKey key, u8 value) { - diagU8ChunkTypeCreate(chunk_type, key); - chunk_type->header.chunk_len = sizeof(u8); - chunk_type->value = value; -} - -typedef struct { - DiagLogDataChunkTypeHeader header; - u32 value; -} DiagU32ChunkType; - -NX_CONSTEXPR void diagU32ChunkTypeCreate(DiagU32ChunkType *chunk_type, DiagLogDataChunkKey key) { - diagChunkTypeCreate(chunk_type, sizeof(DiagU32ChunkType), &chunk_type->header, key); -} - -NX_CONSTEXPR void diagU32ChunkTypeSetValue(DiagU32ChunkType *chunk_type, DiagLogDataChunkKey key, u32 value) { - diagU32ChunkTypeCreate(chunk_type, key); - chunk_type->header.chunk_len = sizeof(u32); - chunk_type->value = value; -} - -typedef struct { - DiagLogDataChunkTypeHeader header; - u64 value; -} DiagU64ChunkType; - -NX_CONSTEXPR void diagU64ChunkTypeCreate(DiagU64ChunkType *chunk_type, DiagLogDataChunkKey key) { - diagChunkTypeCreate(chunk_type, sizeof(DiagU64ChunkType), &chunk_type->header, key); -} - -NX_CONSTEXPR void diagU64ChunkTypeSetValue(DiagU64ChunkType *chunk_type, DiagLogDataChunkKey key, u64 value) { - diagU64ChunkTypeCreate(chunk_type, key); - chunk_type->header.chunk_len = sizeof(u64); - chunk_type->value = value; -} - -#define DIAG_MAX_STRING_LEN 0xFF - -typedef struct { - DiagLogDataChunkTypeHeader header; - char value[DIAG_MAX_STRING_LEN + 1]; -} DiagStringChunkType; - -NX_CONSTEXPR void diagStringChunkTypeCreate(DiagStringChunkType *chunk_type, DiagLogDataChunkKey key) { - diagChunkTypeCreate(chunk_type, sizeof(DiagStringChunkType), &chunk_type->header, key); -} - -NX_CONSTEXPR void diagStringChunkTypeSetValue(DiagStringChunkType *chunk_type, DiagLogDataChunkKey key, const char *value, const size_t value_len) { - diagStringChunkTypeCreate(chunk_type, key); - const size_t len = (value_len > DIAG_MAX_STRING_LEN) ? DIAG_MAX_STRING_LEN : value_len; - chunk_type->header.chunk_len = (u8)len; - __builtin_memcpy(chunk_type->value, value, len); -} - -typedef struct { - DiagU8ChunkType log_session_begin; - DiagU8ChunkType log_session_end; - DiagStringChunkType text_log; - DiagU32ChunkType line_number; - DiagStringChunkType file_name; - DiagStringChunkType function_name; - DiagStringChunkType module_name; - DiagStringChunkType thread_name; - DiagU64ChunkType log_packet_drop_count; - DiagU64ChunkType user_system_clock; - DiagStringChunkType process_name; -} DiagLogPacketPayload; - -// Struct containing everything that will be logged. - -typedef struct { - DiagLogPacketHeader header; - DiagLogPacketPayload payload; -} DiagLogPacket; - -// Helper structs for logging parameters. - -typedef struct { - u32 line_number; - const char *file_name; - const char *function_name; + u32 line_number; ///< Source line number. + const char *file_name; ///< Source file name. + const char *function_name; ///< Source function name. } DiagSourceInfo; +/// DiagLogMetadata typedef struct { - DiagSourceInfo source_info; - DiagLogSeverity severity; - bool verbosity; - const char *text_log; + DiagSourceInfo source_info; ///< Source info. + DiagLogSeverity severity; ///< Log severity. + bool verbosity; ///< Verbosity. + const char *text_log; ///< Log message. } DiagLogMetadata; -// Log packet allocation/freeing - -/// Allocates the amount of log packets needed depending on the message length. -NX_INLINE DiagLogPacket *diagAllocateLogPackets(const size_t msg_len, size_t *out_packet_count) { - size_t remaining_len = msg_len; - size_t packet_count = 1; - while(remaining_len > DIAG_MAX_STRING_LEN) { - packet_count++; - remaining_len -= DIAG_MAX_STRING_LEN; - } - - // Always send at least 2 packets, a head and a tail. - if(packet_count < 2) { - packet_count = 2; - } - - DiagLogPacket *packets = (DiagLogPacket*)calloc(packet_count, sizeof(DiagLogPacket)); - if(packets == NULL) { - return 0; - } - *out_packet_count = packet_count; - return packets; -} - -NX_INLINE void diagFreeLogPackets(DiagLogPacket *packet_buf) { - free(packet_buf); -} - -// Other log packet / chunk type helpers - -NX_CONSTEXPR bool diagIsChunkTypeEmpty(const DiagLogDataChunkTypeHeader *chunk_header) { - return chunk_header->chunk_len == 0; -} - -NX_CONSTEXPR size_t diagComputeChunkKeySize(const DiagLogDataChunkTypeHeader *chunk_header) { - if(diagIsChunkTypeEmpty(chunk_header)) { - return 0; - } - return sizeof(DiagLogDataChunkTypeHeader) + chunk_header->chunk_len; -} - -NX_CONSTEXPR size_t diagComputeLogPacketPayloadSize(const DiagLogPacket *packet) { - return diagComputeChunkKeySize(&packet->payload.log_session_begin.header) + - diagComputeChunkKeySize(&packet->payload.log_session_end.header) + - diagComputeChunkKeySize(&packet->payload.text_log.header) + - diagComputeChunkKeySize(&packet->payload.line_number.header) + - diagComputeChunkKeySize(&packet->payload.file_name.header) + - diagComputeChunkKeySize(&packet->payload.function_name.header) + - diagComputeChunkKeySize(&packet->payload.module_name.header) + - diagComputeChunkKeySize(&packet->payload.thread_name.header) + - diagComputeChunkKeySize(&packet->payload.log_packet_drop_count.header) + - diagComputeChunkKeySize(&packet->payload.user_system_clock.header) + - diagComputeChunkKeySize(&packet->payload.process_name.header); -} - -// Log payload encoding helpers - -NX_CONSTEXPR u8 *diagLogPayloadEncode(u8 *payload_buf, const void *data, size_t size) { - if(size > 0) { - __builtin_memcpy(payload_buf, data, size); - return payload_buf + size; - } - return payload_buf; -} - -NX_CONSTEXPR u8 *diagLogPayloadEncodeU8ChunkType(u8 *payload_buf, DiagU8ChunkType *chunk_type) { - if(diagIsChunkTypeEmpty(&chunk_type->header)) { - return payload_buf; - } - u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); - // Copy value. - return diagLogPayloadEncode(buf, &chunk_type->value, chunk_type->header.chunk_len); -} - -NX_CONSTEXPR u8 *diagLogPayloadEncodeU32ChunkType(u8 *payload_buf, DiagU32ChunkType *chunk_type) { - if(diagIsChunkTypeEmpty(&chunk_type->header)) { - return payload_buf; - } - u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); - // Copy value. - return diagLogPayloadEncode(buf, &chunk_type->value, chunk_type->header.chunk_len); -} - -NX_CONSTEXPR u8 *diagLogPayloadEncodeU64ChunkType(u8 *payload_buf, DiagU64ChunkType *chunk_type) { - if(diagIsChunkTypeEmpty(&chunk_type->header)) { - return payload_buf; - } - u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); - // Copy value. - return diagLogPayloadEncode(buf, &chunk_type->value, chunk_type->header.chunk_len); -} - -NX_CONSTEXPR u8 *diagLogPayloadEncodeStringChunkType(u8 *payload_buf, DiagStringChunkType *chunk_type) { - if(diagIsChunkTypeEmpty(&chunk_type->header)) { - return payload_buf; - } - u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); - // Copy string by its length. - return diagLogPayloadEncode(buf, chunk_type->value, chunk_type->header.chunk_len); -} - /** * @brief Logs via lm. - * @note Retail lm is stubbed. + * @note Logging is stubbed in retail. * @param[in] metadata Log parameters. */ -NX_INLINE void diagLogImpl(const DiagLogMetadata *metadata) { - Result rc = smInitialize(); - if(R_SUCCEEDED(rc)) { - rc = lmInitialize(); - smExit(); - } - if(R_SUCCEEDED(rc)) { - size_t count = 0; - const size_t text_log_len = __builtin_strlen(metadata->text_log); - DiagLogPacket *packets = diagAllocateLogPackets(text_log_len, &count); - if(count > 0 && packets != NULL) { - DiagLogPacket *head_packet = &packets[0]; - head_packet->header.flags = DiagLogPacketFlags_Head; - DiagLogPacket *tail_packet = &packets[count - 1]; - tail_packet->header.flags = DiagLogPacketFlags_Tail; - - const size_t file_name_len = __builtin_strlen(metadata->source_info.file_name); - diagStringChunkTypeSetValue(&head_packet->payload.file_name, DiagLogDataChunkKey_FileName, metadata->source_info.file_name, file_name_len); - - const size_t function_name_len = __builtin_strlen(metadata->source_info.function_name); - diagStringChunkTypeSetValue(&head_packet->payload.function_name, DiagLogDataChunkKey_FunctionName, metadata->source_info.function_name, function_name_len); - - diagU32ChunkTypeSetValue(&head_packet->payload.line_number, DiagLogDataChunkKey_LineNumber, metadata->source_info.line_number); - - const char *module = "libnx"; - diagStringChunkTypeSetValue(&head_packet->payload.module_name, DiagLogDataChunkKey_ModuleName, module, __builtin_strlen(module)); - - size_t remaining_len = text_log_len; - DiagLogPacket *cur_packet = head_packet; - const char *text_log_buf = metadata->text_log; - while(remaining_len > 0) { - const size_t cur_len = (remaining_len > DIAG_MAX_STRING_LEN) ? DIAG_MAX_STRING_LEN : remaining_len; - diagStringChunkTypeSetValue(&cur_packet->payload.text_log, DiagLogDataChunkKey_TextLog, text_log_buf, cur_len); - cur_packet++; - text_log_buf += cur_len; - remaining_len -= cur_len; - } - - size_t i = 0; - for(; i < count; i++) { - DiagLogPacket *cur_packet = &packets[i]; - cur_packet->header.severity = (u8)metadata->severity; - cur_packet->header.verbosity = (u8)metadata->verbosity; - - cur_packet->header.payload_size = (u32)diagComputeLogPacketPayloadSize(cur_packet); - const size_t log_buf_size = cur_packet->header.payload_size + sizeof(DiagLogPacketHeader); - - void *log_buf = calloc(1, log_buf_size); - if(log_buf != NULL) { - // Write the packet's header. - u8 *payload_buf = diagLogPayloadEncode((u8*)log_buf, &cur_packet->header, sizeof(DiagLogPacketHeader)); - - // Write non-empty packets to the payload. - if(!diagIsChunkTypeEmpty(&cur_packet->payload.log_session_begin.header)) { - payload_buf = diagLogPayloadEncodeU8ChunkType(payload_buf, &cur_packet->payload.log_session_begin); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.log_session_end.header)) { - payload_buf = diagLogPayloadEncodeU8ChunkType(payload_buf, &cur_packet->payload.log_session_end); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.text_log.header)) { - payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.text_log); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.line_number.header)) { - payload_buf = diagLogPayloadEncodeU32ChunkType(payload_buf, &cur_packet->payload.line_number); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.file_name.header)) { - payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.file_name); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.function_name.header)) { - payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.function_name); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.module_name.header)) { - payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.module_name); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.thread_name.header)) { - payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.thread_name); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.log_packet_drop_count.header)) { - payload_buf = diagLogPayloadEncodeU64ChunkType(payload_buf, &cur_packet->payload.log_packet_drop_count); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.user_system_clock.header)) { - payload_buf = diagLogPayloadEncodeU64ChunkType(payload_buf, &cur_packet->payload.user_system_clock); - } - if(!diagIsChunkTypeEmpty(&cur_packet->payload.process_name.header)) { - payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.process_name); - } - - rc = lmLog(log_buf, log_buf_size); - free(log_buf); - if(R_FAILED(rc)) { - break; - } - } - } - diagFreeLogPackets(packets); - } - lmExit(); - } -} +void diagLogImpl(const DiagLogMetadata *metadata); // Helper macros. diff --git a/nx/include/switch/services/lm.h b/nx/include/switch/services/lm.h index 5921a738..73bb3bfa 100644 --- a/nx/include/switch/services/lm.h +++ b/nx/include/switch/services/lm.h @@ -39,6 +39,7 @@ Result lmLog(const void *buf, size_t buf_size); /** * @brief Sets the log destination. + * @note Only available on [3.0.0+] * @param[in] destination Log destination. */ Result lmSetDestination(LmLogDestination destination); diff --git a/nx/source/runtime/diag.c b/nx/source/runtime/diag.c new file mode 100644 index 00000000..99228cbf --- /dev/null +++ b/nx/source/runtime/diag.c @@ -0,0 +1,341 @@ +#include "runtime/diag.h" +#include "services/sm.h" +#include "services/lm.h" +#include "kernel/mutex.h" +#include + +static Mutex g_logMutex; + +/// DiagLogPacketFlags +typedef enum { + DiagLogPacketFlags_Head = BIT(0), ///< First packet. + DiagLogPacketFlags_Tail = BIT(1) ///< Last packet. +} DiagLogPacketFlags; + +/// DiagLogPacketHeader +typedef struct { + u64 process_id; ///< Process ID. + u64 thread_id; ///< Thread ID. + u8 flags; ///< \ref DiagLogPacketFlags + u8 pad; ///< Padding + u8 severity; ///< \ref DiagLogSeverity + u8 verbosity; ///< Verbosity. + u32 payload_size; ///< Total packet size after this header. +} DiagLogPacketHeader; + +/// DiagLogDataChunkKey +typedef enum { + DiagLogDataChunkKey_LogSessionBegin = 0, ///< Log session begin (unknown) + DiagLogDataChunkKey_LogSessionEnd = 1, ///< Log session end (unknown) + DiagLogDataChunkKey_TextLog = 2, ///< Text to be logged. + DiagLogDataChunkKey_LineNumber = 3, ///< Source line number. + DiagLogDataChunkKey_FileName = 4, ///< Source file name. + DiagLogDataChunkKey_FunctionName = 5, ///< Source function name. + DiagLogDataChunkKey_ModuleName = 6, ///< Process module name. + DiagLogDataChunkKey_ThreadName = 7, ///< Process thread name. + DiagLogDataChunkKey_LogPacketDropCount = 8, ///< Log packet drop count (unknown) + DiagLogDataChunkKey_UserSystemClock = 9, ///< User system clock (unknown) + DiagLogDataChunkKey_ProcessName = 10 ///< Process name. +} DiagLogDataChunkKey; + +/// DiagLogDataChunkTypeHeader (see specific types below) +typedef struct { + u8 chunk_key; ///< \ref DiagLogDataChunkKey + u8 chunk_len; ///< Value length. +} DiagLogDataChunkTypeHeader; + +// Specific chunk types. + +NX_CONSTEXPR void diagChunkTypeCreate(void *chunk_type, size_t chunk_type_size, DiagLogDataChunkTypeHeader *header, DiagLogDataChunkKey key) { + __builtin_memset(chunk_type, 0, chunk_type_size); + header->chunk_key = key; + header->chunk_len = 0; +} + +typedef struct { + DiagLogDataChunkTypeHeader header; + u8 value; +} DiagU8ChunkType; + +NX_CONSTEXPR void diagU8ChunkTypeCreate(DiagU8ChunkType *chunk_type, DiagLogDataChunkKey key) { + diagChunkTypeCreate(chunk_type, sizeof(DiagU8ChunkType), &chunk_type->header, key); +} + +NX_CONSTEXPR void diagU8ChunkTypeSetValue(DiagU8ChunkType *chunk_type, DiagLogDataChunkKey key, u8 value) { + diagU8ChunkTypeCreate(chunk_type, key); + chunk_type->header.chunk_len = sizeof(u8); + chunk_type->value = value; +} + +typedef struct { + DiagLogDataChunkTypeHeader header; + u32 value; +} DiagU32ChunkType; + +NX_CONSTEXPR void diagU32ChunkTypeCreate(DiagU32ChunkType *chunk_type, DiagLogDataChunkKey key) { + diagChunkTypeCreate(chunk_type, sizeof(DiagU32ChunkType), &chunk_type->header, key); +} + +NX_CONSTEXPR void diagU32ChunkTypeSetValue(DiagU32ChunkType *chunk_type, DiagLogDataChunkKey key, u32 value) { + diagU32ChunkTypeCreate(chunk_type, key); + chunk_type->header.chunk_len = sizeof(u32); + chunk_type->value = value; +} + +typedef struct { + DiagLogDataChunkTypeHeader header; + u64 value; +} DiagU64ChunkType; + +NX_CONSTEXPR void diagU64ChunkTypeCreate(DiagU64ChunkType *chunk_type, DiagLogDataChunkKey key) { + diagChunkTypeCreate(chunk_type, sizeof(DiagU64ChunkType), &chunk_type->header, key); +} + +NX_CONSTEXPR void diagU64ChunkTypeSetValue(DiagU64ChunkType *chunk_type, DiagLogDataChunkKey key, u64 value) { + diagU64ChunkTypeCreate(chunk_type, key); + chunk_type->header.chunk_len = sizeof(u64); + chunk_type->value = value; +} + +#define DIAG_MAX_STRING_LEN 0xFF + +typedef struct { + DiagLogDataChunkTypeHeader header; + char value[DIAG_MAX_STRING_LEN + 1]; +} DiagStringChunkType; + +NX_CONSTEXPR void diagStringChunkTypeCreate(DiagStringChunkType *chunk_type, DiagLogDataChunkKey key) { + diagChunkTypeCreate(chunk_type, sizeof(DiagStringChunkType), &chunk_type->header, key); +} + +NX_CONSTEXPR void diagStringChunkTypeSetValue(DiagStringChunkType *chunk_type, DiagLogDataChunkKey key, const char *value, const size_t value_len) { + diagStringChunkTypeCreate(chunk_type, key); + const size_t len = (value_len > DIAG_MAX_STRING_LEN) ? DIAG_MAX_STRING_LEN : value_len; + chunk_type->header.chunk_len = (u8)len; + __builtin_memcpy(chunk_type->value, value, len); +} + +typedef struct { + DiagU8ChunkType log_session_begin; + DiagU8ChunkType log_session_end; + DiagStringChunkType text_log; + DiagU32ChunkType line_number; + DiagStringChunkType file_name; + DiagStringChunkType function_name; + DiagStringChunkType module_name; + DiagStringChunkType thread_name; + DiagU64ChunkType log_packet_drop_count; + DiagU64ChunkType user_system_clock; + DiagStringChunkType process_name; +} DiagLogPacketPayload; + +// Struct containing everything that will be logged. + +typedef struct { + DiagLogPacketHeader header; + DiagLogPacketPayload payload; +} DiagLogPacket; + +// Log packet allocation/freeing + +/// Allocates the amount of log packets needed depending on the message length. +NX_INLINE DiagLogPacket *diagAllocateLogPackets(const size_t msg_len, size_t *out_packet_count) { + size_t remaining_len = msg_len; + size_t packet_count = 1; + while(remaining_len > DIAG_MAX_STRING_LEN) { + packet_count++; + remaining_len -= DIAG_MAX_STRING_LEN; + } + + // Always send at least 2 packets, a head and a tail. + if(packet_count < 2) { + packet_count = 2; + } + + DiagLogPacket *packets = (DiagLogPacket*)calloc(packet_count, sizeof(DiagLogPacket)); + if(packets == NULL) { + return 0; + } + *out_packet_count = packet_count; + return packets; +} + +NX_INLINE void diagFreeLogPackets(DiagLogPacket *packet_buf) { + free(packet_buf); +} + +// Other log packet / chunk type helpers + +NX_CONSTEXPR bool diagIsChunkTypeEmpty(const DiagLogDataChunkTypeHeader *chunk_header) { + return chunk_header->chunk_len == 0; +} + +NX_CONSTEXPR size_t diagComputeChunkKeySize(const DiagLogDataChunkTypeHeader *chunk_header) { + if(diagIsChunkTypeEmpty(chunk_header)) { + return 0; + } + return sizeof(DiagLogDataChunkTypeHeader) + chunk_header->chunk_len; +} + +NX_CONSTEXPR size_t diagComputeLogPacketPayloadSize(const DiagLogPacket *packet) { + return diagComputeChunkKeySize(&packet->payload.log_session_begin.header) + + diagComputeChunkKeySize(&packet->payload.log_session_end.header) + + diagComputeChunkKeySize(&packet->payload.text_log.header) + + diagComputeChunkKeySize(&packet->payload.line_number.header) + + diagComputeChunkKeySize(&packet->payload.file_name.header) + + diagComputeChunkKeySize(&packet->payload.function_name.header) + + diagComputeChunkKeySize(&packet->payload.module_name.header) + + diagComputeChunkKeySize(&packet->payload.thread_name.header) + + diagComputeChunkKeySize(&packet->payload.log_packet_drop_count.header) + + diagComputeChunkKeySize(&packet->payload.user_system_clock.header) + + diagComputeChunkKeySize(&packet->payload.process_name.header); +} + +// Log payload encoding helpers + +NX_CONSTEXPR u8 *diagLogPayloadEncode(u8 *payload_buf, const void *data, size_t size) { + if(size > 0) { + __builtin_memcpy(payload_buf, data, size); + return payload_buf + size; + } + return payload_buf; +} + +NX_CONSTEXPR u8 *diagLogPayloadEncodeU8ChunkType(u8 *payload_buf, DiagU8ChunkType *chunk_type) { + if(diagIsChunkTypeEmpty(&chunk_type->header)) { + return payload_buf; + } + u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); + // Copy value. + return diagLogPayloadEncode(buf, &chunk_type->value, chunk_type->header.chunk_len); +} + +NX_CONSTEXPR u8 *diagLogPayloadEncodeU32ChunkType(u8 *payload_buf, DiagU32ChunkType *chunk_type) { + if(diagIsChunkTypeEmpty(&chunk_type->header)) { + return payload_buf; + } + u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); + // Copy value. + return diagLogPayloadEncode(buf, &chunk_type->value, chunk_type->header.chunk_len); +} + +NX_CONSTEXPR u8 *diagLogPayloadEncodeU64ChunkType(u8 *payload_buf, DiagU64ChunkType *chunk_type) { + if(diagIsChunkTypeEmpty(&chunk_type->header)) { + return payload_buf; + } + u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); + // Copy value. + return diagLogPayloadEncode(buf, &chunk_type->value, chunk_type->header.chunk_len); +} + +NX_CONSTEXPR u8 *diagLogPayloadEncodeStringChunkType(u8 *payload_buf, DiagStringChunkType *chunk_type) { + if(diagIsChunkTypeEmpty(&chunk_type->header)) { + return payload_buf; + } + u8 *buf = diagLogPayloadEncode(payload_buf, &chunk_type->header, sizeof(DiagLogDataChunkTypeHeader)); + // Copy string by its length. + return diagLogPayloadEncode(buf, chunk_type->value, chunk_type->header.chunk_len); +} + +void diagLogImpl(const DiagLogMetadata *metadata) { + mutexLock(&g_logMutex); + Result rc = smInitialize(); + if(R_SUCCEEDED(rc)) { + rc = lmInitialize(); + smExit(); + } + if(R_SUCCEEDED(rc)) { + size_t packet_count = 0; + const size_t text_log_len = __builtin_strlen(metadata->text_log); + DiagLogPacket *packets = diagAllocateLogPackets(text_log_len, &packet_count); + if(packet_count > 0 && packets != NULL) { + DiagLogPacket *head_packet = &packets[0]; + head_packet->header.flags = DiagLogPacketFlags_Head; + DiagLogPacket *tail_packet = &packets[packet_count - 1]; + tail_packet->header.flags = DiagLogPacketFlags_Tail; + + const size_t file_name_len = __builtin_strlen(metadata->source_info.file_name); + diagStringChunkTypeSetValue(&head_packet->payload.file_name, DiagLogDataChunkKey_FileName, metadata->source_info.file_name, file_name_len); + + const size_t function_name_len = __builtin_strlen(metadata->source_info.function_name); + diagStringChunkTypeSetValue(&head_packet->payload.function_name, DiagLogDataChunkKey_FunctionName, metadata->source_info.function_name, function_name_len); + + diagU32ChunkTypeSetValue(&head_packet->payload.line_number, DiagLogDataChunkKey_LineNumber, metadata->source_info.line_number); + + // Set libnx as module name. + const char *module = "libnx"; + diagStringChunkTypeSetValue(&head_packet->payload.module_name, DiagLogDataChunkKey_ModuleName, module, __builtin_strlen(module)); + + size_t remaining_len = text_log_len; + DiagLogPacket *cur_packet = head_packet; + const char *text_log_buf = metadata->text_log; + while(remaining_len > 0) { + const size_t cur_len = (remaining_len > DIAG_MAX_STRING_LEN) ? DIAG_MAX_STRING_LEN : remaining_len; + diagStringChunkTypeSetValue(&cur_packet->payload.text_log, DiagLogDataChunkKey_TextLog, text_log_buf, cur_len); + cur_packet++; + text_log_buf += cur_len; + remaining_len -= cur_len; + } + + size_t i = 0; + for(; i < packet_count; i++) { + DiagLogPacket *cur_packet = &packets[i]; + cur_packet->header.severity = (u8)metadata->severity; + cur_packet->header.verbosity = (u8)metadata->verbosity; + + cur_packet->header.payload_size = (u32)diagComputeLogPacketPayloadSize(cur_packet); + const size_t log_buf_size = cur_packet->header.payload_size + sizeof(DiagLogPacketHeader); + + void *log_buf = calloc(1, log_buf_size); + if(log_buf != NULL) { + // Write the packet's header. + u8 *payload_buf = diagLogPayloadEncode((u8*)log_buf, &cur_packet->header, sizeof(DiagLogPacketHeader)); + + // Write non-empty packets to the payload. + if(!diagIsChunkTypeEmpty(&cur_packet->payload.log_session_begin.header)) { + payload_buf = diagLogPayloadEncodeU8ChunkType(payload_buf, &cur_packet->payload.log_session_begin); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.log_session_end.header)) { + payload_buf = diagLogPayloadEncodeU8ChunkType(payload_buf, &cur_packet->payload.log_session_end); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.text_log.header)) { + payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.text_log); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.line_number.header)) { + payload_buf = diagLogPayloadEncodeU32ChunkType(payload_buf, &cur_packet->payload.line_number); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.file_name.header)) { + payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.file_name); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.function_name.header)) { + payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.function_name); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.module_name.header)) { + payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.module_name); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.thread_name.header)) { + payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.thread_name); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.log_packet_drop_count.header)) { + payload_buf = diagLogPayloadEncodeU64ChunkType(payload_buf, &cur_packet->payload.log_packet_drop_count); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.user_system_clock.header)) { + payload_buf = diagLogPayloadEncodeU64ChunkType(payload_buf, &cur_packet->payload.user_system_clock); + } + if(!diagIsChunkTypeEmpty(&cur_packet->payload.process_name.header)) { + payload_buf = diagLogPayloadEncodeStringChunkType(payload_buf, &cur_packet->payload.process_name); + } + + rc = lmLog(log_buf, log_buf_size); + free(log_buf); + if(R_FAILED(rc)) { + break; + } + } + } + diagFreeLogPackets(packets); + } + lmExit(); + mutexUnlock(&g_logMutex); + } +}