logcat: Add --regex option

You can now filter log output by a regex on the messages.

Test: New unit test passes
Bug: 27706081
Change-Id: Idfa637f62a25fb1b92e9b49b89572dff0fec6e08
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c148d89..729e704 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -37,6 +37,8 @@
 #include <log/logprint.h>
 #include <system/thread_defs.h>
 
+#include <pcrecpp.h>
+
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
 static AndroidLogFormat * g_logformat;
@@ -76,6 +78,7 @@
 static size_t g_outByteCount = 0;
 static int g_printBinary = 0;
 static int g_devCount = 0;                              // >1 means multiple
+static pcrecpp::RE* g_regex;
 
 __noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
 
@@ -143,6 +146,21 @@
     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
 }
 
+static bool regexOk(const AndroidLogEntry& entry, log_id_t id)
+{
+    if (! g_regex) {
+        return true;
+    }
+
+    if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+        return false;
+    }
+
+    std::string messageString(entry.message, entry.messageLen);
+
+    return g_regex->PartialMatch(messageString);
+}
+
 static void processBuffer(log_device_t* dev, struct log_msg *buf)
 {
     int bytesWritten = 0;
@@ -171,7 +189,8 @@
         goto error;
     }
 
-    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
+    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority) &&
+        regexOk(entry, buf->id())) {
         bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
 
         if (bytesWritten < 0) {
@@ -271,6 +290,8 @@
                     "  -c              clear (flush) the entire log and exit\n"
                     "  --clear\n"
                     "  -d              dump the log and then exit (don't block)\n"
+                    "  -e <expr>       only print lines where the log message matches <expr>\n"
+                    "  --regex <expr>  where <expr> is a regular expression\n"
                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
                     "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
@@ -548,6 +569,7 @@
           { "last",          no_argument,       NULL,   'L' },
           { pid_str,         required_argument, NULL,   0 },
           { "prune",         optional_argument, NULL,   'p' },
+          { "regex",         required_argument, NULL,   'e' },
           { "rotate_count",  required_argument, NULL,   'n' },
           { "rotate_kbytes", required_argument, NULL,   'r' },
           { "statistics",    no_argument,       NULL,   'S' },
@@ -556,7 +578,7 @@
           { NULL,            0,                 NULL,   0 }
         };
 
-        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:",
+        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:e:",
                           long_options, &option_index);
 
         if (ret < 0) {
@@ -644,6 +666,10 @@
                 printDividers = true;
             break;
 
+            case 'e':
+                g_regex = new pcrecpp::RE(optarg);
+            break;
+
             case 'g':
                 if (!optarg) {
                     getLogSize = 1;