Log services: Difference between revisions
Rename ILogger command 0 from Initialize to Log. I doubt the original service name is Initialize, and this is more in line with what the actual function does; given this is used heavily by some games to fire out debug info. |
|||
(11 intermediate revisions by 4 users not shown) | |||
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 nothing. | |||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
Line 6: | Line 8: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| 0 || OpenLogger | | 0 || [[#OpenLogger]] | ||
|} | |||
== OpenLogger == | |||
Takes an input u64 value representing a process ID and returns an [[#ILogger]] instance. | |||
= 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]] | |||
|} | |} | ||
== ILogger | == 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 16: | Line 48: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| 0 || Log | | 0 || [[#Log]] | ||
|- | |||
| 1 || [3.0.0+] [[#SetDestination]] | |||
|- | |||
| 2 || [20.0.0+] TransmitHashedLog | |||
|- | |||
| 3 || [20.0.0+] DevNotify | |||
|} | |||
== Log == | |||
Takes a type-0x21 input buffer containing the message to log. | |||
Will always return successfully. | |||
== SetDestination == | |||
Takes an input u32 mask value indicating the logging destinations to send incoming log messages to. This is stored as a global value. The mask entries are as follows: | |||
{| class="wikitable" border="1" | |||
|- | |||
! Value || Description | |||
|- | |||
| 1 || TMA | |||
|- | |||
| 2 || UART | |||
|- | |||
| 4 || UART when sleeping | |||
|- | |||
| 0xFFFF || Log to all destinations | |||
|} | |||
= LogPacketHeader = | |||
This is "nn::lm::detail::LogPacketHeader" | |||
{| class="wikitable" border="1" | |||
|- | |||
! Offset || Size || Description | |||
|- | |||
| 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 | |||
|} | |} | ||
= | = LogPacketFlags = | ||
This is "nn:: | |||
{| 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" | ||
|- | |- | ||
! | ! 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" | |||
|- | |- | ||
| | ! Offset || Size || Description | ||
|- | |- | ||
| | | 0x0 || 0x18 || [[#LogPacketHeader]] | ||
|- | |- | ||
| | | 0x18 || Variable || Data chunks | ||
|} | |} | ||
= 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]]) | |||
* CustomSinkBuffer 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 CustomSinkBuffer 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]] |