From 39e63f1358aa6baa72a5feba1909a5086d061c02 Mon Sep 17 00:00:00 2001
From: Sergei Trofimov <sergei.trofimov@arm.com>
Date: Thu, 23 Nov 2017 17:39:54 +0000
Subject: [PATCH] utils/android: add a parser for Logcat

Add a simple streaming parser for Logcat logs. At the moment, only the
recent logcat format is supported.
---
 wa/utils/android.py | 66 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 wa/utils/android.py

diff --git a/wa/utils/android.py b/wa/utils/android.py
new file mode 100644
index 00000000..454772c3
--- /dev/null
+++ b/wa/utils/android.py
@@ -0,0 +1,66 @@
+import logging
+from datetime import datetime
+
+from wa.utils.types import enum
+
+
+LogcatLogLevel = enum(['verbose', 'debug', 'info', 'warn', 'error', 'assert'], start=2)
+
+log_level_map = ''.join(n[0].upper() for n in LogcatLogLevel.names)
+
+logger = logging.getLogger('logcat')
+
+
+class LogcatEvent(object):
+
+    __slots__ = ['timestamp', 'pid', 'tid', 'level', 'tag', 'message']
+
+    def __init__(self, timestamp, pid, tid, level, tag, message):
+        self.timestamp = timestamp
+        self.pid = pid
+        self.tid = pid
+        self.level = level
+        self.tag = tag
+        self.message = message
+
+    def __repr__(self):
+        return '{} {} {} {} {}: {}'.format(
+            self.timestamp, self.pid, self.tid,
+            self.level.name.upper(), self.tag,
+            self.message,
+        )
+
+    __str__ = __repr__
+
+
+class LogcatParser(object):
+
+    def parse(self, filepath):
+        with open(filepath) as fh:
+            for line in fh:
+                event = self.parse_line(line)
+                if event:
+                    yield event
+
+    def parse_line(self, line):
+        line = line.strip()
+        if not line or line.startswith('-') or ':' not in line:
+            return None
+
+        metadata, message = line.split(': ', 1)
+
+        parts = metadata.split(None, 5)
+        try:
+            ts = ' '.join([parts.pop(0), parts.pop(0)])
+            timestamp = datetime.strptime(ts, '%m-%d %H:%M:%S.%f').replace(year=datetime.now().year)
+            pid = int(parts.pop(0))
+            tid = int(parts.pop(0))
+            level = LogcatLogLevel.levels[log_level_map.index(parts.pop(0))]
+            tag = (parts.pop(0) if parts else '').strip()
+        except Exception as e:
+            message = 'Invalid metadata for line:\n\t{}\n\tgot: "{}"'
+            logger.warning(message.format(line, e))
+            return None
+
+        return LogcatEvent(timestamp, pid, tid, level, tag, message)
+