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>
This commit is contained in:
Michał Orynicz 2014-05-14 09:45:23 +02:00 committed by Anders Broman
parent 71d07bcbbf
commit 1a02ca0150
11 changed files with 1098 additions and 377 deletions

View File

@ -907,6 +907,7 @@ set(DISSECTOR_SRC
dissectors/packet-lmi.c
dissectors/packet-lmp.c
dissectors/packet-logcat.c
dissectors/packet-logcat-text.c
dissectors/packet-lon.c
dissectors/packet-loop.c
dissectors/packet-lpd.c

View File

@ -833,6 +833,7 @@ DISSECTOR_SRC = \
packet-lmi.c \
packet-lmp.c \
packet-logcat.c \
packet-logcat-text.c \
packet-lon.c \
packet-loop.c \
packet-lpd.c \

View File

@ -0,0 +1,394 @@
/* packet-logcat-text.c
* Routines for Android Logcat text formats
*
* Copyright 2014, Michal Orynicz for Tieto Corporation
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "epan/packet.h"
#include "epan/expert.h"
#include "wiretap/wtap.h"
#include <stdio.h>
/* Basically the same regexes are present in wiretap/logcat_text.c */
#define SPECIAL_STRING "[-]+ (beginning of \\/.+)"
#define BRIEF_STRING "([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)"
#define TAG_STRING "([IVDWEF])/(.*?): (.*)"
#define TIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) ([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)"
#define THREAD_STRING "([IVDWEF])\\( *(\\d+): *(\\d+)\\) (.*)"
#define PROCESS_STRING "([IVDWEF])\\( *(\\d+)\\) (.*)"
#define THREADTIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+) +(\\d+) ([IVDWEF]) (.*?): (.*)"
#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): +(\\d+) ([IVDWEF])/(.+) ]\\R(.*)"
extern const value_string priority_vals[];
static int proto_logcat_text = -1;
static int hf_logcat_text_pid = -1;
static int hf_logcat_text_tid = -1;
static int hf_logcat_text_timestamp = -1;
static int hf_logcat_text_priority = -1;
static int hf_logcat_text_tag = -1;
static int hf_logcat_text_log = -1;
static gint ett_logcat = -1;
static expert_field ei_malformed_time = EI_INIT;
static expert_field ei_malformed_token = EI_INIT;
static dissector_handle_t logcat_text_brief_handle;
static dissector_handle_t logcat_text_tag_handle;
static dissector_handle_t logcat_text_process_handle;
static dissector_handle_t logcat_text_time_handle;
static dissector_handle_t logcat_text_thread_handle;
static dissector_handle_t logcat_text_threadtime_handle;
static dissector_handle_t logcat_text_long_handle;
static GRegex *special_regex = NULL;
static GRegex *brief_regex = NULL;
static GRegex *tag_regex = NULL;
static GRegex *time_regex = NULL;
static GRegex *process_regex = NULL;
static GRegex *thread_regex = NULL;
static GRegex *threadtime_regex = NULL;
static GRegex *long_regex = NULL;
static const gchar dissector_name[] = "Logcat Text";
typedef int (*tGETTER) (const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo);
typedef struct {
GRegex *regex;
const tGETTER *getters;
guint no_of_getters;
} dissect_info_t;
void proto_register_logcat_text(void);
void proto_reg_handoff_logcat_text(void);
static int get_priority(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) {
int prio;
gchar *p = g_strstr_len(frame + start_offset, -1, token);
int offset = p - frame;
switch (*p) {
case 'I':
prio = 4;
break;
case 'V':
prio = 2;
break;
case 'D':
prio = 3;
break;
case 'W':
prio = 5;
break;
case 'E':
prio = 6;
break;
case 'F':
prio = 7;
break;
default:
prio = 0;
}
proto_tree_add_uint(maintree, hf_logcat_text_priority, tvb, offset, 1, prio);
return offset + 1;
}
static int get_tag(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo) {
gchar *p = g_strstr_len(frame + start_offset, -1, token);
int offset = p - frame;
guint8 *src_addr = wmem_strdup(wmem_packet_scope(), token);
gint tok_len = strlen(token);
proto_tree_add_string(maintree, hf_logcat_text_tag, tvb, offset, tok_len,
token);
SET_ADDRESS(&pinfo->src, AT_STRINGZ, tok_len + 1, src_addr);
SET_ADDRESS(&pinfo->dst, AT_STRINGZ, sizeof(dissector_name), dissector_name);
return offset + tok_len;
}
static int get_ptid(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint header_field, gint start_offset) {
gchar *p = g_strstr_len(frame + start_offset, -1, token);
int offset = p - frame;
proto_tree_add_uint(maintree, header_field, tvb, offset, strlen(token),
g_ascii_strtoull(token, NULL, 10));
return offset + strlen(token);
}
static int get_pid(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) {
return get_ptid(frame, token, tvb, maintree, hf_logcat_text_pid, start_offset);
}
static int get_tid(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo _U_) {
return get_ptid(frame, token, tvb, maintree, hf_logcat_text_tid, start_offset);
}
static int get_log(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo) {
gchar *p = g_strstr_len(frame + start_offset, -1, token);
int offset = p - frame;
proto_tree_add_string(maintree, hf_logcat_text_log, tvb, offset,
strlen(token), token);
col_add_str(pinfo->cinfo, COL_INFO, token);
return offset + strlen(token);
}
static int get_time(const gchar *frame, const gchar *token, tvbuff_t *tvb,
proto_tree *maintree, gint start_offset, packet_info *pinfo) {
gint offset;
gchar *p;
gint day, month, hrs, min, sec, ms;
GDateTime *date;
gint64 seconds;
nstime_t ts;
p = g_strstr_len(frame + start_offset, -1, token);
offset = p - frame;
if (6 == sscanf(token, "%d-%d %d:%d:%d.%d", &month, &day, &hrs, &min, &sec, &ms)) {
date = g_date_time_new_local(1970, month, day, hrs, min,
(gdouble) sec + ((gdouble) ms) * 0.001);
seconds = g_date_time_to_unix(date);
ts.secs = (time_t) seconds;
ts.nsecs = (int) ms * 1e6;
proto_tree_add_time(maintree, hf_logcat_text_timestamp, tvb, offset,
strlen(token), &ts);
g_date_time_unref(date);
} else {
proto_tree_add_expert(maintree, pinfo, &ei_malformed_time, tvb, offset, -1);
}
return offset + strlen(token);
}
static int dissect_logcat_text(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo,
const dissect_info_t *dinfo) {
gchar **tokens;
guint i;
gchar *frame = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, tvb_captured_length(tvb),
ENC_ASCII);
proto_item *mainitem = proto_tree_add_item(tree, proto_logcat_text, tvb, 0, -1, ENC_NA);
proto_tree *maintree = proto_item_add_subtree(mainitem, ett_logcat);
gint offset = 0;
col_set_str(pinfo->cinfo, COL_PROTOCOL, dissector_name);
if (!g_regex_match(special_regex, frame, G_REGEX_MATCH_NOTEMPTY, NULL)) {
tokens = g_regex_split(dinfo->regex, frame, G_REGEX_MATCH_NOTEMPTY);
if (NULL == tokens) return 0;
if (g_strv_length(tokens) != dinfo->no_of_getters + 2) {
proto_tree_add_expert(maintree, pinfo, &ei_malformed_token, tvb, offset, -1);
g_strfreev(tokens);
return 0;
}
for (i = 0; i < dinfo->no_of_getters; ++i) {
offset = ((*dinfo->getters[i])(frame, tokens[i + 1], tvb, maintree, offset, pinfo));
}
} else {
tokens = g_regex_split(special_regex, frame, G_REGEX_MATCH_NOTEMPTY);
if (NULL == tokens) return 0;
get_log(frame, tokens[1], tvb, maintree, 0, pinfo);
}
g_strfreev(tokens);
return offset;
}
static int dissect_logcat_text_brief(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_priority, get_tag, get_pid, get_log };
dissect_info_t dinfo = { brief_regex, getters, array_length(getters) };
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
static int dissect_logcat_text_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_priority, get_tag, get_log };
dissect_info_t dinfo = { tag_regex, getters, array_length(getters) };
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
static int dissect_logcat_text_process(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_priority, get_pid, get_log };
dissect_info_t dinfo = { process_regex, getters, array_length(getters) };
SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 0, "");
SET_ADDRESS(&pinfo->src, AT_STRINGZ, 0, "");
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
static int dissect_logcat_text_time(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_time, get_priority, get_tag, get_pid, get_log };
dissect_info_t dinfo = { time_regex, getters, array_length(getters) };
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
static int dissect_logcat_text_thread(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_priority, get_pid, get_tid, get_log };
dissect_info_t dinfo = { thread_regex, getters, array_length(getters) };
SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 0, "");
SET_ADDRESS(&pinfo->src, AT_STRINGZ, 0, "");
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
static int dissect_logcat_text_threadtime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log };
dissect_info_t dinfo = { threadtime_regex, getters, array_length(getters) };
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
static int dissect_logcat_text_long(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_) {
static const tGETTER getters[] = { get_time, get_pid, get_tid, get_priority, get_tag, get_log };
dissect_info_t dinfo = { long_regex, getters, array_length(getters) };
return dissect_logcat_text(tvb, tree, pinfo, &dinfo);
}
void proto_register_logcat_text(void) {
expert_module_t *expert_module;
static hf_register_info hf[] = {
{ &hf_logcat_text_timestamp,
{ "Timestamp", "logcat_text.timestamp",
FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x00, NULL, HFILL
}
},
{ &hf_logcat_text_tag,
{ "Tag", "logcat_text.tag",
FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL
}
},
{ &hf_logcat_text_log,
{ "Log", "logcat_text.log",
FT_STRING, STR_ASCII, NULL, 0x00, NULL, HFILL
}
},
{ &hf_logcat_text_priority,
{ "Priority", "logcat_text.priority",
FT_UINT8, BASE_DEC, VALS(priority_vals), 0x00, NULL, HFILL
}
},
{ &hf_logcat_text_pid,
{ "PID", "logcat_text.pid",
FT_UINT32, BASE_DEC, NULL, 0x00, "Process ID", HFILL
}
},
{ &hf_logcat_text_tid,
{ "TID", "logcat_text.tid",
FT_UINT32, BASE_DEC, NULL, 0x00, "Thread ID", HFILL
}
}
};
static ei_register_info ei[] = {
{ &ei_malformed_time, { "logcat_text.malformed_time", PI_PROTOCOL, PI_ERROR, "Malformed time data", EXPFILL }},
{ &ei_malformed_token, { "logcat_text.malformed_token", PI_PROTOCOL, PI_ERROR, "Failed to decode one or more tokens", EXPFILL }},
};
static gint *ett[] = { &ett_logcat};
proto_logcat_text = proto_register_protocol("Android Logcat Text", dissector_name,
"logcat_text");
proto_register_field_array(proto_logcat_text, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
logcat_text_brief_handle = new_register_dissector("logcat_text_brief",
dissect_logcat_text_brief, proto_logcat_text);
logcat_text_tag_handle = new_register_dissector("logcat_text_tag",
dissect_logcat_text_tag, proto_logcat_text);
logcat_text_time_handle = new_register_dissector("logcat_text_time",
dissect_logcat_text_time, proto_logcat_text);
logcat_text_process_handle = new_register_dissector("logcat_text_process",
dissect_logcat_text_process, proto_logcat_text);
logcat_text_thread_handle = new_register_dissector("logcat_text_thread",
dissect_logcat_text_thread, proto_logcat_text);
logcat_text_threadtime_handle = new_register_dissector("logcat_text_threadtime",
dissect_logcat_text_threadtime, proto_logcat_text);
logcat_text_long_handle = new_register_dissector("logcat_text_long",
dissect_logcat_text_long, proto_logcat_text);
special_regex = g_regex_new(SPECIAL_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
brief_regex = g_regex_new(BRIEF_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
tag_regex = g_regex_new(TAG_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
time_regex = g_regex_new(TIME_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
thread_regex = g_regex_new(THREAD_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
threadtime_regex = g_regex_new(THREADTIME_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
process_regex = g_regex_new(PROCESS_STRING, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY, NULL);
long_regex = g_regex_new(LONG_STRING, G_REGEX_MULTILINE, G_REGEX_MATCH_NOTEMPTY, NULL);
expert_module = expert_register_protocol(proto_logcat_text);
expert_register_field_array(expert_module, ei, array_length(ei));
}
void proto_reg_handoff_logcat_text(void) {
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_BRIEF,
logcat_text_brief_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TAG,
logcat_text_tag_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_TIME,
logcat_text_time_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREAD,
logcat_text_thread_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_THREADTIME,
logcat_text_threadtime_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_PROCESS,
logcat_text_process_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOGCAT_LONG,
logcat_text_long_handle);
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -57,7 +57,7 @@ static gint exported_pdu_tap = -1;
static expert_field ei_invalid_payload_length = EI_INIT;
static const value_string priority_vals[] = {
const value_string priority_vals[] = {
{ 0x00, "Unknown" },
{ 0x01, "Default" },
{ 0x02, "Verbose" },

View File

@ -49,6 +49,7 @@ set(CLEAN_FILES
lanalyzer.c
libpcap.c
logcat.c
logcat_text.c
merge.c
mpeg.c
mime_file.c

View File

@ -53,6 +53,7 @@ NONGENERATED_C_FILES = \
mime_file.c \
k12.c \
lanalyzer.c \
logcat_text.c \
logcat.c \
libpcap.c \
merge.c \
@ -112,6 +113,7 @@ NONGENERATED_HEADER_FILES = \
lanalyzer.h \
libpcap.h \
logcat.h \
logcat_text.h \
merge.h \
mpeg.h \
mp2t.h \

View File

@ -65,6 +65,7 @@
#include "erf.h"
#include "hcidump.h"
#include "logcat.h"
#include "logcat_text.h"
#include "network_instruments.h"
#include "k12.h"
#include "ber.h"
@ -362,6 +363,7 @@ static struct open_info open_info_base[] = {
{ "Commview", OPEN_INFO_HEURISTIC, commview_open, "ncf", NULL, NULL },
{ "Nstrace", OPEN_INFO_HEURISTIC, nstrace_open, "txt", NULL, NULL },
{ "Logcat ", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL },
{ "Logcat Text", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL },
/* ASCII trace files from Telnet sessions. */
{ "Ascend", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL },
{ "Toshiba", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL },
@ -1441,25 +1443,25 @@ static const struct file_type_subtype_info dump_open_table_base[] = {
logcat_dump_can_write_encap, logcat_binary_dump_open, NULL },
{ "Android Logcat Brief text format", "logcat-brief", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_brief_dump_open, NULL },
logcat_text_brief_dump_can_write_encap, logcat_text_brief_dump_open, NULL },
{ "Android Logcat Process text format", "logcat-process", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_process_dump_open, NULL },
logcat_text_process_dump_can_write_encap, logcat_text_process_dump_open, NULL },
{ "Android Logcat Tag text format", "logcat-tag", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_tag_dump_open, NULL },
logcat_text_tag_dump_can_write_encap, logcat_text_tag_dump_open, NULL },
{ "Android Logcat Thread text format", "logcat-thread", NULL, NULL,
FALSE, FALSE, 0,
logcat_text_thread_dump_can_write_encap, logcat_text_thread_dump_open, NULL },
{ "Android Logcat Time text format", "logcat-time", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_time_dump_open, NULL },
{ "Android Logcat Thread text format", "logcat-thread", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_thread_dump_open, NULL },
logcat_text_time_dump_can_write_encap, logcat_text_time_dump_open, NULL },
{ "Android Logcat Threadtime text format", "logcat-threadtime", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL },
logcat_text_threadtime_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL },
{ "Android Logcat Long text format", "logcat-long", NULL, NULL,
FALSE, FALSE, 0,
logcat_dump_can_write_encap, logcat_text_long_dump_open, NULL }
logcat_text_long_dump_can_write_encap, logcat_text_long_dump_open, NULL }
};

View File

@ -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
*

View File

@ -25,19 +25,45 @@
#include "wtap.h"
/* 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 */
};
int logcat_open(wtap *wth, int *err, gchar **err_info);
gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err);
int logcat_dump_can_write_encap(int encap);
gint logcat_exported_pdu_length(const guint8 *pd);
#endif
/*

593
wiretap/logcat_text.c Normal file
View File

@ -0,0 +1,593 @@
/* logcat_text.c
*
* Copyright 2014, Michal Orynicz for Tieto Corporation
* Copyright 2014, Michal Labedzki for Tieto Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <string.h>
#include <time.h>
#include "wtap-int.h"
#include "file_wrappers.h"
#include "wsutil/buffer.h"
#include "logcat_text.h"
#include "logcat.h"
/* Basically the same regexes are present in epan/packet-logcat-text.c */
#define SPECIAL_STRING "[-]+ beginning of \\/"
#define BRIEF_STRING "[IVDWEF]/.*\\( *\\d*\\): .*"
#define TAG_STRING "[IVDWEF]/.*: .*"
#define TIME_STRING "\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} [IVDWE]/.*\\( *\\d*\\): .*"
#define THREAD_STRING "[IVDWEF]\\( *\\d+: *\\d+\\) .*"
#define PROCESS_STRING "[IVDWEF]\\( *\\d+\\) .*"
#define THREADTIME_STRING "\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} *\\d+ *\\d+ [IVDWEF] .+: +"
#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): +(\\d+) ([IVDWEF])/(.+) ]"
struct dumper_t {
int type;
};
/* Returns '?' for invalid priorities */
static gchar get_priority(const guint8 priority) {
static gchar priorities[] = "??VDIWEFS";
if (priority >= (guint8) sizeof(priorities))
return '?';
return priorities[priority];
}
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 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 WTAP_ENCAP_LOGCAT_BRIEF:
return g_strdup_printf("%c/%-8s(%5i): %s\n",
priority, tag, pid, log);
case WTAP_ENCAP_LOGCAT_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 WTAP_ENCAP_LOGCAT_TAG:
return g_strdup_printf("%c/%-8s: %s\n",
priority, tag, log);
case WTAP_ENCAP_LOGCAT_THREAD:
return g_strdup_printf("%c(%5i:%5i) %s\n",
priority, pid, tid, log);
case WTAP_ENCAP_LOGCAT_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 WTAP_ENCAP_LOGCAT_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 WTAP_ENCAP_LOGCAT_LONG:
strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
gmtime(&datetime));
return g_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n",
time_buffer, milliseconds, pid, tid, priority, tag, log);
default:
return NULL;
}
}
static void get_time(gchar *string, struct wtap_pkthdr *phdr) {
gint day, month, hrs, min, sec, ms;
GDateTime *date = NULL;
gint64 seconds;
if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &month, &day, &hrs, &min, &sec, &ms)) {
date = g_date_time_new_local(1970, month, day, hrs, min,
(gdouble) sec + ((gdouble) ms) * 0.001);
seconds = g_date_time_to_unix(date);
phdr->ts.secs = (time_t) seconds;
phdr->ts.nsecs = (int) ms * 1e6;
phdr->presence_flags = WTAP_HAS_TS;
g_date_time_unref(date);
} else {
phdr->presence_flags = 0;
phdr->ts.secs = (time_t) 0;
phdr->ts.nsecs = (int) 0;
}
}
static gboolean logcat_text_read_packet(FILE_T fh, struct wtap_pkthdr *phdr,
Buffer *buf, gint file_type) {
gint8 *pd;
gchar cbuff[WTAP_MAX_PACKET_SIZE];
gchar *ret = NULL;
do {
ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE, fh);
} while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh));
if (NULL == ret || 3 > strlen(cbuff)) {
return FALSE;
}
if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type &&
!g_regex_match_simple(SPECIAL_STRING, cbuff, G_REGEX_ANCHORED, G_REGEX_MATCH_NOTEMPTY)) {
gint64 file_off = 0;
gchar lbuff[WTAP_MAX_PACKET_SIZE];
int err;
gchar *ret2 = NULL;
file_off = file_tell(fh);
ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE, fh);
while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) {
g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE);
file_off = file_tell(fh);
ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE, fh);
}
if(NULL == ret2 || 2 < strlen(lbuff)) {
return FALSE;
}
file_seek(fh,file_off,SEEK_SET,&err);
}
phdr->rec_type = REC_TYPE_PACKET;
phdr->caplen = strlen(cbuff);
phdr->len = phdr->caplen;
ws_buffer_assure_space(buf, phdr->caplen + 1);
pd = ws_buffer_start_ptr(buf);
if ((WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME == file_type
|| WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME == file_type
|| WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type)
&& '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */
if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) {
get_time(cbuff+2, phdr);
} else {
get_time(cbuff, phdr);
}
} else {
phdr->presence_flags = 0;
phdr->ts.secs = (time_t) 0;
phdr->ts.nsecs = (int) 0;
}
memcpy(pd, cbuff, phdr->caplen + 1);
return TRUE;
}
static gboolean logcat_text_read(wtap *wth, int *err _U_ , gchar **err_info _U_,
gint64 *data_offset _U_) {
*data_offset = file_tell(wth->fh);
return logcat_text_read_packet(wth->fh, &wth->phdr, wth->frame_buffer,
wth->file_type_subtype);
}
static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off,
struct wtap_pkthdr *phdr, Buffer *buf, int *err _U_, gchar **err_info _U_) {
if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
return FALSE;
if (!logcat_text_read_packet(wth->random_fh, phdr, buf,
wth->file_type_subtype)) {
if (*err == 0)
*err = WTAP_ERR_SHORT_READ;
return FALSE;
}
return TRUE;
}
int logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) {
gchar cbuff[WTAP_MAX_PACKET_SIZE];
gchar *ret = NULL;
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
return -1;
do {
ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE, wth->fh);
} while (NULL != ret && !file_eof(wth->fh)
&& ((3 > strlen(cbuff))
|| g_regex_match_simple(SPECIAL_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)));
if (g_regex_match_simple(BRIEF_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_BRIEF;
wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF;
} else if (g_regex_match_simple(TAG_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TAG;
wth->file_encap = WTAP_ENCAP_LOGCAT_TAG;
} else if (g_regex_match_simple(PROCESS_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_PROCESS;
wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS;
} else if (g_regex_match_simple(TIME_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME;
wth->file_encap = WTAP_ENCAP_LOGCAT_TIME;
} else if (g_regex_match_simple(THREAD_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREAD;
wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD;
} else if (g_regex_match_simple(THREADTIME_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME;
wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME;
} else if (g_regex_match_simple(LONG_STRING, cbuff, G_REGEX_ANCHORED,
G_REGEX_MATCH_NOTEMPTY)) {
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG;
wth->file_encap = WTAP_ENCAP_LOGCAT_LONG;
} else {
return -1;
}
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
return -1;
wth->snapshot_length = 0;
wth->subtype_read = logcat_text_read;
wth->subtype_seek_read = logcat_text_seek_read;
wth->tsprecision = WTAP_FILE_TSPREC_USEC;
return 1;
}
int logcat_text_brief_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_BRIEF:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
int logcat_text_process_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_PROCESS:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
int logcat_text_tag_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_TAG:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
int logcat_text_time_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_TIME:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
int logcat_text_thread_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_THREAD:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
int logcat_text_threadtime_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_THREADTIME:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
int logcat_text_long_dump_can_write_encap(int encap) {
if (encap == WTAP_ENCAP_PER_PACKET)
return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
switch (encap) {
case WTAP_ENCAP_LOGCAT:
case WTAP_ENCAP_LOGCAT_LONG:
return 0;
default:
return WTAP_ERR_UNSUPPORTED_ENCAP;
}
}
static gboolean logcat_text_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;
}
switch (wdh->encap) {
case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
case WTAP_ENCAP_LOGCAT:
/* Skip EXPORTED_PDU*/
if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
gint skipped_length;
skipped_length = logcat_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 == WTAP_ENCAP_LOGCAT_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);
break;
case WTAP_ENCAP_LOGCAT_BRIEF:
case WTAP_ENCAP_LOGCAT_TAG:
case WTAP_ENCAP_LOGCAT_PROCESS:
case WTAP_ENCAP_LOGCAT_TIME:
case WTAP_ENCAP_LOGCAT_THREAD:
case WTAP_ENCAP_LOGCAT_THREADTIME:
case WTAP_ENCAP_LOGCAT_LONG:
if (dumper->type == wdh->encap) {
if (!wtap_dump_file_write(wdh, (gchar*) pd, phdr->caplen, err)) {
return FALSE;
}
} else {
*err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
return FALSE;
}
}
return TRUE;
}
static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type, int *err _U_) {
struct dumper_t *dumper;
dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
dumper->type = dump_type;
wdh->priv = dumper;
wdh->subtype_write = logcat_text_dump_text;
wdh->subtype_close = NULL;
return TRUE;
}
gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF, err);
}
gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS, err);
}
gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG, err);
}
gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME, err);
}
gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD, err);
}
gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME, err);
}
gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err) {
return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG, err);
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

59
wiretap/logcat_text.h Normal file
View File

@ -0,0 +1,59 @@
/* logcat_text.h
*
* Copyright 2014, Michal Orynicz for Tieto Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __LOGCAT_TEXT_H__
#define __LOGCAT_TEXT_H__
#include <glib.h>
#include "wtap.h"
int logcat_text_open(wtap *wth, int *err, gchar **err_info);
gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err);
gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err);
int logcat_text_brief_dump_can_write_encap(int encap);
int logcat_text_tag_dump_can_write_encap(int encap);
int logcat_text_process_dump_can_write_encap(int encap);
int logcat_text_thread_dump_can_write_encap(int encap);
int logcat_text_time_dump_can_write_encap(int encap);
int logcat_text_threadtime_dump_can_write_encap(int encap);
int logcat_text_long_dump_can_write_encap(int encap);
#endif
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/