Line 1: |
Line 1: |
| = lm = | | = lm = |
| This is "nn::lm::ILogService". | | This is "nn::lm::ILogService". |
| + | |
| + | This service is stubbed on retail, with all the commands/interfaces being present but returning success and doing anything. |
| | | |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
Line 13: |
Line 15: |
| Takes an input u64 value representing a process ID and returns an [[#ILogger]] instance. | | Takes an input u64 value representing a process ID and returns an [[#ILogger]] instance. |
| | | |
− | == ILogger == | + | = lm:get = |
| + | This is "nn::lm::ILogGetter". This service doesn't exist on retail. |
| + | |
| + | This service can be used to retrieve log messages (only the TextLog field, see [[#Logging]]) |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Cmd || Name |
| + | |- |
| + | | 0 || [[#StartLogging]] |
| + | |- |
| + | | 1 || [[#StopLogging]] |
| + | |- |
| + | | 2 || [[#GetLog]] |
| + | |} |
| + | |
| + | == StartLogging == |
| + | Takes no input and no output, and just sets a global flag to true. |
| + | |
| + | == StopLogging == |
| + | Same as above, but the flag is set to false. |
| + | |
| + | == GetLog == |
| + | Takes an output type-0x22 buffer, returns two output u64s (read size and packet drop count) |
| + | The flag mentioned above is not checked here, as it is used in a different place (see [[#Logging]]) |
| + | |
| + | = ILogger = |
| This is "nn::lm::ILogger". | | This is "nn::lm::ILogger". |
| | | |
Line 27: |
Line 55: |
| == Log == | | == Log == |
| | | |
− | Takes an input buffer containing the message to log. | + | Takes a type-0x21 input buffer containing the message to log. |
| Will always return successfully. | | Will always return successfully. |
| | | |
Line 38: |
Line 66: |
| ! Value || Description | | ! Value || Description |
| |- | | |- |
− | | 1 || TMA | + | | 1 || Log to SD card / HTCS |
| |- | | |- |
− | | 2 || UART | + | | 2 || Disable something (?) |
| + | |} |
| + | |
| + | = LogPacketHeader = |
| + | This is "nn::lm::detail::LogPacketHeader" |
| + | |
| + | {| class="wikitable" border="1" |
| |- | | |- |
− | | 4 || UART when sleeping | + | ! Offset || Size || Description |
| |- | | |- |
− | | 0xFFFF || Log to all destinations | + | | 0x0 || 0x8 || Process ID |
| + | |- |
| + | | 0x8 || 0x8 || Thread ID |
| + | |- |
| + | | 0x10 || 0x1 || [[#LogPacketFlags]] |
| + | |- |
| + | | 0x11 || 0x1 || Padding/reserved |
| + | |- |
| + | | 0x12 || 0x1 || [[#LogSeverity]] |
| + | |- |
| + | | 0x13 || 0x1 || Verbosity |
| + | |- |
| + | | 0x14 || 0x4 || Payload size |
| |} | | |} |
| | | |
− | = lm:get = | + | = LogPacketFlags = |
− | This is "nn::lm::ILogGetter". | + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Value || Name |
| + | |- |
| + | | 1 || Head |
| + | |- |
| + | | 2 || Tail |
| + | |- |
| + | | 4 || LittleEndian |
| + | |} |
| + | |
| + | = Binary header = |
| + | Binary log files saved in SD card have this extra header (see [[#Logging]]) |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Offset || Size || Description |
| + | |- |
| + | | 0x0 || 0x4 || Magic ("hphp") |
| + | |- |
| + | | 0x4 || 0x1 || Version (1) |
| + | |- |
| + | | 0x5 || 0x3 || Padding/reserved |
| + | |} |
| + | |
| + | = LogSeverity = |
| + | This is "nn::diag::LogSeverity" |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Value || Name |
| + | |- |
| + | | 0 || Trace |
| + | |- |
| + | | 1 || Info |
| + | |- |
| + | | 2 || Warn |
| + | |- |
| + | | 3 || Error |
| + | |- |
| + | | 4 || Fatal |
| + | |} |
| + | |
| + | = LogDataChunkKey = |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Value || Name || Description |
| + | |- |
| + | | 0 || LogSessionBegin || Special field sent when a process starts logging (see [[#Logging]]) |
| + | |- |
| + | | 1 || LogSessionEnd || Special field sent when a process finishes logging (see [[#Logging]]) |
| + | |- |
| + | | 2 || TextLog || Actual log text. |
| + | |- |
| + | | 3 || LineNumber || Line number. |
| + | |- |
| + | | 4 || FileName || File name. |
| + | |- |
| + | | 5 || FunctionName || Function name. |
| + | |- |
| + | | 6 || ModuleName || Module name. |
| + | |- |
| + | | 7 || ThreadName || Thread name. |
| + | |- |
| + | | 8 || LogPacketDropCount || Special field containing the amount of packets missed (failed to log/flush, see [[#Logging]]) |
| + | |- |
| + | | 9 || UserSystemClock || Time value. |
| + | |- |
| + | | 10 || ProcessName || Process name. |
| + | |} |
| + | |
| + | = Data chunks = |
| + | Data chunks (log packet data fields) follow this format: |
| | | |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
− | ! Cmd || Name | + | ! Offset || Size || Description |
| + | |- |
| + | | 0x0 || Variable (normally 0x1) || [[#LogDataChunkKey]] in LEB128 format |
| + | |- |
| + | | Variable || Variable (normally 0x1) || Chunk size in LEB128 format |
| + | |- |
| + | | Variable || Chunk size || Chunk data |
| + | |} |
| + | |
| + | = Log packet = |
| + | |
| + | {| class="wikitable" border="1" |
| |- | | |- |
− | | 0 || StartLogging | + | ! Offset || Size || Description |
| |- | | |- |
− | | 1 || StopLogging | + | | 0x0 || 0x18 || [[#LogPacketHeader]] |
| |- | | |- |
− | | 2 || GetLog | + | | 0x18 || Variable || Data chunks |
| |} | | |} |
| | | |
− | This service doesn't normally exist on retail.
| + | = Logging = |
| + | |
| + | LogManager uses two separate threads: a flushing thread and a htcs thread (main thread is handling both IPC and PM module requests): |
| + | |
| + | * The flushing calls Flush() on the "LogBuffer" object, which is the object responsible for flushing logged data though the other objects, and sends a LogPacketDropCount packet via the "EventLogTransmitter" object (see below) if there any dropped packets and the flushing succeeds - this is done in a loop, waiting until the process starts finalization. |
| + | |
| + | * The htcs thread continously reads from the server connection, and will close and reopen the client socket if the connection happened to be lost - this is done in a loop, waiting until the process starts finalization. |
| + | |
| + | Different global objects are used for different purposes: |
| + | |
| + | * SdCardLogging object - saves logs on the SD card, if both <code>lm!sd_card_log_output_directory</code> and <code>lm!enable_sd_card_logging</code> are present and the last one is true: |
| + | ** The file name format used is <code>sd:/<log_output_directory>/<serial_no>_<year><month><day><hour><min><sec>[_<extra_index>].nxbinlog</code>, where the extra index is added if the file is already present (several logs being saved at the same time), with a maximum value of 100 (otherwise the saving fails). |
| + | ** These binary files contain multiple log packets, always preceded by a binary header (see above). |
| + | ** A single file is used for all the log packets, if the SD card gets ejected or something similar happens the code will re-mount it when it's ready and create a new file. |
| + | |
| + | * LogServerProxy object - sends log data via HTCS (see [[TMA_services]]) |
| + | |
| + | * LogGetterLogger object - retains log data which can be later retrieved by [[#lm:get]]: |
| + | ** When processes log via [[#lm]] service, some code analyzes the log packet, locates the TextLog ([[#LogDataChunkKey]]) chunk (if present) and saves it's contents in a global buffer (therefore, only the actual log message gets retained). |
| + | ** Log packets are only analyzed/retained after enabling it (by calling [[#StartLogging]]), and the [[#GetLog]] command just reads data from the global buffer. |
| + | ** The analyzing code also keeps track of which packets are "missed" (skipped/not properly logged), and that missed packet count can be also retrieved on the [[#GetLog]] command. |
| + | ** When the global buffer is full, nothing will get logged until a process reads the logged data, cleaning the buffer. |
| + | |
| + | * EventLogTransmitter object - sends special log packets (event logs): |
| + | ** When an [[#ILogger]] gets created, this object will send a packet containing the logger's process ID and a LogSessionBegin chunk, telling the remote connection that a new process has started logging. Analogously, when the logger gets destroyed, a simple packet with the process ID and a LogSessionEnd chunk gets sent, telling the remote connection that the process has stopped logging. |
| + | ** As mentioned above, the flush thread will tell this object to send a simple packet with a LogPacketDropCount chunk after flushing succeeds (and if there any dropped packets). |
| + | ** These event log packets are logged via the LogBuffer object, like process logs, but they are not analized by the LogGetterLogger object since they have no text to be saved. |
| + | |
| + | * LogBuffer object - responsible for flushing log packets: |
| + | ** It internally calls the SdCardLogging and LogServerProxy objects when flushing, so that log packets get flushed to both SD card (if enabled) and HTCS. |
| + | ** It uses a secondary object to flush, alternating between itself and that secondary object when flushing. |
| | | |
| [[Category:Services]] | | [[Category:Services]] |