summaryrefslogtreecommitdiff
path: root/wiretap/logcat.c
diff options
context:
space:
mode:
authorMichaƂ Orynicz <michal.orynicz@tieto.com>2014-05-14 09:45:23 +0200
committerAnders Broman <a.broman58@gmail.com>2014-08-06 06:46:50 +0000
commit1a02ca0150e434d9ddc7c023267c9097a059b6be (patch)
tree4d7f3812bc71e659ff792355d12028137e06232a /wiretap/logcat.c
parent71d07bcbbf08eac22432203c2c464fdce13b9467 (diff)
downloadwireshark-1a02ca0150e434d9ddc7c023267c9097a059b6be.tar.gz
Add support for android logcat text files
Wireshark already supports reading and writing logcat logs saved in binary files. Binary format, although better, is used less often than saving those logs to text files. This patch extends wireshark's support for android logcat logs to reading and writing logcat logs in text files. Features: * support for tag, brief, process, thread, time, threadtime and long formats * saving in original format * it's generally awesome Change-Id: I013d6ac2da876d9a2b39b740219eb398d03830f6 Reviewed-on: https://code.wireshark.org/review/1802 Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'wiretap/logcat.c')
-rw-r--r--wiretap/logcat.c362
1 files changed, 2 insertions, 360 deletions
diff --git a/wiretap/logcat.c b/wiretap/logcat.c
index 5ac6e383aa..ff80f99a39 100644
--- a/wiretap/logcat.c
+++ b/wiretap/logcat.c
@@ -28,53 +28,6 @@
#include "logcat.h"
-enum dump_type_t {
- DUMP_BINARY,
- DUMP_BRIEF,
- DUMP_PROCESS,
- DUMP_TAG,
- DUMP_TIME,
- DUMP_THREAD,
- DUMP_THREADTIME,
- DUMP_LONG
-};
-
-struct dumper_t {
- enum dump_type_t type;
-};
-
-/* The log format can be found on:
- * https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h
- * Log format is assumed to be little-endian (Android platform).
- */
-/* maximum size of a message payload in a log entry */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4076
-
-struct logger_entry {
- guint16 len; /* length of the payload */
- guint16 __pad; /* no matter what, we get 2 bytes of padding */
- gint32 pid; /* generating process's pid */
- gint32 tid; /* generating process's tid */
- gint32 sec; /* seconds since Epoch */
- gint32 nsec; /* nanoseconds */
- char msg[0]; /* the entry's payload */
-};
-
-struct logger_entry_v2 {
- guint16 len; /* length of the payload */
- guint16 hdr_size; /* sizeof(struct logger_entry_v2) */
- gint32 pid; /* generating process's pid */
- gint32 tid; /* generating process's tid */
- gint32 sec; /* seconds since Epoch */
- gint32 nsec; /* nanoseconds */
- union {
- /* v1: not present */
- guint32 euid; /* v2: effective UID of logger */
- guint32 lid; /* v3: log id of the payload */
- };
- char msg[0]; /* the entry's payload */
-};
-
/* Returns '?' for invalid priorities */
static gchar get_priority(const guint8 priority) {
static gchar priorities[] = "??VDIWEFS";
@@ -85,51 +38,6 @@ static gchar get_priority(const guint8 priority) {
return priorities[priority];
}
-static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
- gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
- const gchar *log)
-{
- gchar time_buffer[15];
- time_t datetime;
-
- datetime = (time_t) seconds;
-
- switch (dumper->type) {
- case DUMP_BRIEF:
- return g_strdup_printf("%c/%-8s(%5i): %s\n",
- priority, tag, pid, log);
- case DUMP_PROCESS:
- /* NOTE: Last parameter should be "process name", not tag;
- Unfortunately, we do not have process name */
- return g_strdup_printf("%c(%5i) %s (%s)\n",
- priority, pid, log, "");
- case DUMP_TAG:
- return g_strdup_printf("%c/%-8s: %s\n",
- priority, tag, log);
- case DUMP_THREAD:
- return g_strdup_printf("%c(%5i:0x%02x) %s\n",
- priority, pid, tid, log);
- case DUMP_TIME:
- strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
- gmtime(&datetime));
- return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
- time_buffer, milliseconds, priority, tag, pid, log);
- case DUMP_THREADTIME:
- strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
- gmtime(&datetime));
- return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
- time_buffer, milliseconds, pid, tid, priority, tag, log);
- case DUMP_LONG:
- strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
- gmtime(&datetime));
- return g_strdup_printf("[ %s.%03i %5i:0x%02x %c/%s ]\n%s\n\n",
- time_buffer, milliseconds, pid, tid, priority, tag, log);
- default:
- return NULL;
- }
-
-}
-
static gint detect_version(wtap *wth, int *err, gchar **err_info)
{
gint bytes_read;
@@ -228,63 +136,7 @@ static gint detect_version(wtap *wth, int *err, gchar **err_info)
return -1;
}
-static gint buffered_detect_version(const guint8 *pd)
-{
- struct logger_entry *log_entry;
- struct logger_entry_v2 *log_entry_v2;
- gint version;
- guint8 *msg_payload = NULL;
- guint8 *msg_part;
- guint8 *msg_end;
- guint16 msg_len;
-
- log_entry_v2 = (struct logger_entry_v2 *) pd;
- log_entry = (struct logger_entry *) pd;
-
- /* must contain at least priority and two nulls as separator */
- if (log_entry->len < 3)
- return -1;
-
- /* payload length may not exceed the maximum payload size */
- if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
- return -1;
-
- /* cannot rely on __pad being 0 for v1, use heuristics to find out what
- * version is in use. First assume the smallest msg. */
- for (version = 1; version <= 2; ++version) {
- if (version == 1) {
- msg_payload = log_entry->msg;
- } else if (version == 2) {
- /* v2 is 4 bytes longer */
- msg_payload = log_entry_v2->msg;
- if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
- continue;
- }
-
- /* A v2 msg has a 32-bit userid instead of v1 priority */
- if (get_priority(msg_payload[0]) == '?')
- continue;
-
- /* Is there a terminating '\0' for the tag? */
- msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
- if (msg_part == NULL)
- continue;
-
- /* if msg is '\0'-terminated, is it equal to the payload len? */
- ++msg_part;
- msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
- msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
- /* is the end of the buffer (-1) equal to the end of msg? */
- if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
- continue;
-
- return version;
- }
-
- return -1;
-}
-
-static gint exported_pdu_length(const guint8 *pd) {
+gint logcat_exported_pdu_length(const guint8 *pd) {
guint16 *tag;
guint16 *tag_length;
gint length = 0;
@@ -457,7 +309,7 @@ static gboolean logcat_binary_dump(wtap_dumper *wdh,
if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
gint skipped_length;
- skipped_length = exported_pdu_length(pd);
+ skipped_length = logcat_exported_pdu_length(pd);
pd += skipped_length;
caplen -= skipped_length;
}
@@ -489,216 +341,6 @@ gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err)
return TRUE;
}
-static gboolean logcat_dump_text(wtap_dumper *wdh,
- const struct wtap_pkthdr *phdr,
- const guint8 *pd, int *err)
-{
- gchar *buf;
- gint length;
- gchar priority;
- const struct logger_entry *log_entry;
- const struct logger_entry_v2 *log_entry_v2;
- gint payload_length;
- const gchar *tag;
- gint32 pid;
- gint32 tid;
- gint32 seconds;
- gint32 milliseconds;
- const gchar *msg_begin;
- gint msg_pre_skip;
- gchar *log;
- gchar *log_part;
- gchar *log_next;
- gint logcat_version;
- const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv;
-
- /* We can only write packet records. */
- if (phdr->rec_type != REC_TYPE_PACKET) {
- *err = WTAP_ERR_REC_TYPE_UNSUPPORTED;
- return FALSE;
- }
-
- /* Skip EXPORTED_PDU*/
- if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
- gint skipped_length;
-
- skipped_length = exported_pdu_length(pd);
- pd += skipped_length;
-
- logcat_version = buffered_detect_version(pd);
- } else {
- const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
-
- logcat_version = pseudo_header->logcat.version;
- }
-
- log_entry = (struct logger_entry *) pd;
- log_entry_v2 = (struct logger_entry_v2 *) pd;
-
- payload_length = GINT32_FROM_LE(log_entry->len);
- pid = GINT32_FROM_LE(log_entry->pid);
- tid = GINT32_FROM_LE(log_entry->tid);
- seconds = GINT32_FROM_LE(log_entry->sec);
- milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
-
- /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
- if (logcat_version == 1) {
- priority = get_priority(log_entry->msg[0]);
- tag = log_entry->msg + 1;
- msg_pre_skip = 1 + (gint) strlen(tag) + 1;
- msg_begin = log_entry->msg + msg_pre_skip;
- } else if (logcat_version == 2) {
- priority = get_priority(log_entry_v2->msg[0]);
- tag = log_entry_v2->msg + 1;
- msg_pre_skip = 1 + (gint) strlen(tag) + 1;
- msg_begin = log_entry_v2->msg + msg_pre_skip;
- } else {
- *err = WTAP_ERR_UNSUPPORTED;
- return FALSE;
- }
-
- /* copy the message part. If a nul byte was missing, it will be added. */
- log = g_strndup(msg_begin, payload_length - msg_pre_skip);
-
- /* long format: display one header followed by the whole message (which may
- * contain new lines). Other formats: include tag, etc. with each line */
- log_next = log;
- do {
- log_part = log_next;
- if (dumper->type == DUMP_LONG) {
- /* read until end, there is no next string */
- log_next = NULL;
- } else {
- /* read until next newline */
- log_next = strchr(log_part, '\n');
- if (log_next != NULL) {
- *log_next = '\0';
- ++log_next;
- /* ignore trailing newline */
- if (*log_next == '\0') {
- log_next = NULL;
- }
- }
- }
-
- buf = logcat_log(dumper, seconds, milliseconds, pid, tid,
- priority, tag, log_part);
- if (!buf) {
- g_free(log);
- return FALSE;
- }
- length = (guint32)strlen(buf);
-
- if (!wtap_dump_file_write(wdh, buf, length, err)) {
- g_free(log);
- return FALSE;
- }
-
- wdh->bytes_dumped += length;
- } while (log_next != NULL);
-
- g_free(log);
- return TRUE;
-}
-
-gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_BRIEF;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_PROCESS;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_TAG;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_TIME;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_THREAD;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_THREADTIME;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
-gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err _U_)
-{
- struct dumper_t *dumper;
-
- dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
- dumper->type = DUMP_LONG;
-
- wdh->priv = dumper;
- wdh->subtype_write = logcat_dump_text;
- wdh->subtype_close = NULL;
-
- return TRUE;
-}
-
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*