/// <summary> /// Reads the next <see cref="LogEntry"/> from the stream. /// </summary> /// <returns> /// A new <see cref="LogEntry"/> object. /// </returns> public async Task <LogEntry> ReadEntry(CancellationToken cancellationToken) { LogEntry value = new LogEntry(); // Read the log data in binary format. This format is defined at // https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h var payloadLengthValue = await this.ReadUInt16Async(cancellationToken).ConfigureAwait(false); var headerSizeValue = payloadLengthValue == null ? null : await this.ReadUInt16Async(cancellationToken).ConfigureAwait(false); var pidValue = headerSizeValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); var tidValue = pidValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); var secValue = tidValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); var nsecValue = secValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); if (nsecValue == null) { return(null); } var payloadLength = payloadLengthValue.Value; var headerSize = headerSizeValue.Value; var pid = pidValue.Value; var tid = tidValue.Value; var sec = secValue.Value; var nsec = nsecValue.Value; // If the headerSize is not 0, we have either a logger_entry_v3 or logger_entry_v2 object. // For both objects, the size should be 0x18 uint id = 0; if (headerSize != 0) { if (headerSize == 0x18) { var idValue = await this.ReadUInt32Async(cancellationToken).ConfigureAwait(false); if (idValue == null) { return(null); } id = idValue.Value; } else { throw new Exception(); } } byte[] data = await this.ReadBytesSafeAsync(payloadLength, cancellationToken).ConfigureAwait(false); if (data == null) { return(null); } DateTime timestamp = DateTimeHelper.Epoch.AddSeconds(sec); timestamp = timestamp.AddMilliseconds(nsec / 1000000d); switch ((LogId)id) { case LogId.Crash: case LogId.Kernel: case LogId.Main: case LogId.Radio: case LogId.System: { // format: <priority:1><tag:N>\0<message:N>\0 var priority = data[0]; // Find the first \0 byte in the array. This is the seperator // between the tag and the actual message int tagEnd = 1; while (data[tagEnd] != '\0' && tagEnd < data.Length) { tagEnd++; } // Message should be null termintated, so remove the last entry, too (-2 instead of -1) string tag = Encoding.ASCII.GetString(data, 1, tagEnd - 1); string message = Encoding.ASCII.GetString(data, tagEnd + 1, data.Length - tagEnd - 2); return(new AndroidLogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id, Priority = (Priority)priority, Message = message, Tag = tag }); } case LogId.Events: { // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 var entry = new EventLogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id }; // Use a stream on the data buffer. This will make sure that, // if anything goes wrong parsing the data, we never go past // the message boundary itself. using (MemoryStream dataStream = new MemoryStream(data)) using (BinaryReader reader = new BinaryReader(dataStream)) { var priority = reader.ReadInt32(); while (dataStream.Position < dataStream.Length) { this.ReadLogEntry(reader, entry.Values); } } return(entry); } default: return(new LogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id }); } }
/// <summary> /// Reads the next <see cref="LogEntry"/> from the stream. /// </summary> /// <returns> /// A new <see cref="LogEntry"/> object. /// </returns> public async Task <LogEntry> ReadEntry(CancellationToken cancellationToken) { LogEntry value = new LogEntry(); // Read the log data in binary format. This format is defined at // https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h // https://android.googlesource.com/platform/system/core/+/67d7eaf/include/log/logger.h var payloadLengthValue = await this.ReadUInt16Async(cancellationToken).ConfigureAwait(false); var headerSizeValue = payloadLengthValue == null ? null : await this.ReadUInt16Async(cancellationToken).ConfigureAwait(false); var pidValue = headerSizeValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); var tidValue = pidValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); var secValue = tidValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); var nsecValue = secValue == null ? null : await this.ReadInt32Async(cancellationToken).ConfigureAwait(false); if (nsecValue == null) { return(null); } var payloadLength = payloadLengthValue.Value; var headerSize = headerSizeValue.Value; var pid = pidValue.Value; var tid = tidValue.Value; var sec = secValue.Value; var nsec = nsecValue.Value; // If the headerSize is not 0, we have on of the logger_entry_v* objects. // In all cases, it appears that they always start with a two uint16's giving the // header size and payload length. // For both objects, the size should be 0x18 uint id = 0; uint uid = 0; if (headerSize != 0) { if (headerSize >= 0x18) { var idValue = await this.ReadUInt32Async(cancellationToken).ConfigureAwait(false); if (idValue == null) { return(null); } id = idValue.Value; } if (headerSize >= 0x1c) { var uidValue = await this.ReadUInt32Async(cancellationToken).ConfigureAwait(false); if (uidValue == null) { return(null); } uid = uidValue.Value; } if (headerSize >= 0x20) { // Not sure what this is. await this.ReadUInt32Async(cancellationToken).ConfigureAwait(false); } if (headerSize > 0x20) { throw new AdbException($"An error occurred while reading data from the ADB stream. Although the header size was expected to be 0x18, a header size of 0x{headerSize:X} was sent by the device"); } } byte[] data = await this.ReadBytesSafeAsync(payloadLength, cancellationToken).ConfigureAwait(false); if (data == null) { return(null); } var timestamp = DateTimeOffset.FromUnixTimeSeconds(sec); switch ((LogId)id) { case LogId.Crash: case LogId.Kernel: case LogId.Main: case LogId.Radio: case LogId.System: { // format: <priority:1><tag:N>\0<message:N>\0 var priority = data[0]; // Find the first \0 byte in the array. This is the seperator // between the tag and the actual message int tagEnd = 1; while (data[tagEnd] != '\0' && tagEnd < data.Length) { tagEnd++; } // Message should be null termintated, so remove the last entry, too (-2 instead of -1) string tag = Encoding.ASCII.GetString(data, 1, tagEnd - 1); string message = Encoding.ASCII.GetString(data, tagEnd + 1, data.Length - tagEnd - 2); return(new AndroidLogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id, Priority = (Priority)priority, Message = message, Tag = tag }); } case LogId.Events: { // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 var entry = new EventLogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id }; // Use a stream on the data buffer. This will make sure that, // if anything goes wrong parsing the data, we never go past // the message boundary itself. using (MemoryStream dataStream = new MemoryStream(data)) using (BinaryReader reader = new BinaryReader(dataStream)) { var priority = reader.ReadInt32(); while (dataStream.Position < dataStream.Length) { this.ReadLogEntry(reader, entry.Values); } } return(entry); } default: return(new LogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id }); } }
/// <summary> /// Reads the next <see cref="LogEntry"/> from the stream. /// </summary> /// <returns> /// A new <see cref="LogEntry"/> object. /// </returns> public LogEntry ReadEntry() { LogEntry value = new LogEntry(); // Read the log data in binary format. This format is defined at // https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h var payloadLength = this.ReadUInt16(); var headerSize = this.ReadUInt16(); var pid = this.ReadInt32(); var tid = this.ReadInt32(); var sec = this.ReadInt32(); var nsec = this.ReadInt32(); // If the headerSize is not 0, we have either a logger_entry_v3 or logger_entry_v2 object. // For both objects, the size should be 0x18 uint id = 0; if (headerSize != 0) { if (headerSize == 0x18) { id = this.ReadUInt32(); } else { throw new Exception(); } } byte[] data = this.ReadBytes(payloadLength); DateTime timestamp = DateTimeHelper.Epoch.AddSeconds(sec); timestamp = timestamp.AddMilliseconds(nsec / 1000000d); switch ((LogId)id) { case LogId.Crash: case LogId.Kernel: case LogId.Main: case LogId.Radio: case LogId.System: { // format: <priority:1><tag:N>\0<message:N>\0 var priority = data[0]; // Find the first \0 byte in the array. This is the seperator // between the tag and the actual message int tagEnd = 1; while (data[tagEnd] != '\0' && tagEnd < data.Length) { tagEnd++; } // Message should be null termintated, so remove the last entry, too (-2 instead of -1) string tag = Encoding.ASCII.GetString(data, 1, tagEnd - 1); string message = Encoding.ASCII.GetString(data, tagEnd + 1, data.Length - tagEnd - 2); return new AndroidLogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id, Priority = priority, Message = message, Tag = tag }; } case LogId.Events: { // https://android.googlesource.com/platform/system/core.git/+/master/liblog/logprint.c#547 var entry = new EventLogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id }; // Use a stream on the data buffer. This will make sure that, // if anything goes wrong parsing the data, we never go past // the message boundary itself. using (MemoryStream stream = new MemoryStream(data)) using (BinaryReader reader = new BinaryReader(stream)) { var priority = reader.ReadInt32(); while (stream.Position < stream.Length) { this.ReadLogEntry(reader, entry.Values); } } return entry; } default: return new LogEntry() { Data = data, ProcessId = pid, ThreadId = tid, TimeStamp = timestamp, Id = id }; } }