Implement Cpu Info Section

Support carriage return in Read class, and add a new way to parse lines
which is not able to split purly by delimiters

Bug: 65642861
Test: unit test and on device test
Change-Id: Ib82dd4e458bb7d2fa33462b23fbe11b828325916
diff --git a/Android.bp b/Android.bp
index 5c1ccb7..2c4963c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
             // runtime, as well as the only protos that are actually
             // needed by the device.
             srcs: [
+                "core/proto/android/os/cpuinfo.proto",
                 "core/proto/android/os/kernelwake.proto",
                 "core/proto/android/os/pagetypeinfo.proto",
                 "core/proto/android/os/procrank.proto",
@@ -81,6 +82,7 @@
     ],
 
     srcs: [
+        "core/proto/android/os/cpuinfo.proto",
         "core/proto/android/os/kernelwake.proto",
         "core/proto/android/os/pagetypeinfo.proto",
         "core/proto/android/os/procrank.proto",
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index c7d1ca2..0b51e66 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -22,19 +22,28 @@
 #include <sstream>
 #include <unistd.h>
 
-const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
+bool isValidChar(char c) {
+    uint8_t v = (uint8_t)c;
+    return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
+        || (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
+        || (v >= (uint8_t)'0' && v <= (uint8_t)'9')
+        || (v == (uint8_t)'_');
+}
 
-
-static std::string trim(const std::string& s) {
-    const auto head = s.find_first_not_of(DEFAULT_WHITESPACE);
+static std::string trim(const std::string& s, const std::string& chars) {
+    const auto head = s.find_first_not_of(chars);
     if (head == std::string::npos) return "";
 
-    const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
+    const auto tail = s.find_last_not_of(chars);
     return s.substr(head, tail - head + 1);
 }
 
+static std::string trimDefault(const std::string& s) {
+    return trim(s, DEFAULT_WHITESPACE);
+}
+
 static std::string trimHeader(const std::string& s) {
-    std::string res = trim(s);
+    std::string res = trimDefault(s);
     std::transform(res.begin(), res.end(), res.begin(), ::tolower);
     return res;
 }
@@ -68,22 +77,68 @@
 
 record_t parseRecord(const std::string& line, const std::string& delimiters) {
     record_t record;
-    trans_func f = &trim;
+    trans_func f = &trimDefault;
     split(line, record, f, delimiters);
     return record;
 }
 
-bool hasPrefix(std::string* line, const char* key) {
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
+    record_t record;
+    int lastIndex = 0;
+    int lineSize = (int)line.size();
+    for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
+        int idx = *it;
+        if (lastIndex > idx || idx > lineSize) {
+            record.clear(); // The indices is wrong, return empty;
+            return record;
+        }
+        while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
+        record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
+        lastIndex = idx;
+    }
+    record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex)));
+    return record;
+}
+
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
     const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
     if (head == std::string::npos) return false;
-    auto i = 0;
-    auto j = head;
+    int len = (int)line->length();
+    int i = 0;
+    int j = head;
     while (key[i] != '\0') {
-        if (j >= line->size() || key[i++] != line->at(j++)) {
+        if (j >= len || key[i++] != line->at(j++)) {
             return false;
         }
     }
-    line->assign(trim(line->substr(j)));
+
+    if (endAtDelimiter) {
+        // this means if the line only have prefix or no delimiter, we still return false.
+        if (j == len || isValidChar(line->at(j))) return false;
+    }
+
+    line->assign(trimDefault(line->substr(j)));
+    return true;
+}
+
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
+    const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
+    if (tail == std::string::npos) return false;
+    int i = 0;
+    while (key[++i] != '\0'); // compute the size of the key
+    int j = tail;
+    while (i > 0) {
+        if (j < 0 || key[--i] != line->at(j--)) {
+            return false;
+        }
+    }
+
+    if (endAtDelimiter) {
+        // this means if the line only have suffix or no delimiter, we still return false.
+        if (j < 0 || isValidChar(line->at(j))) return false;
+    }
+
+    line->assign(trimDefault(line->substr(0, j+1)));
     return true;
 }
 
@@ -95,65 +150,36 @@
     return atoll(s.c_str());
 }
 
-// ==============================================================================
-Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
+double toDouble(const std::string& s) {
+    return atof(s.c_str());
+}
 
-Reader::Reader(const int fd, const size_t capacity)
-        : mFd(fd), mMaxSize(capacity), mBufSize(0), mRead(0), mFlushed(0)
+// ==============================================================================
+Reader::Reader(const int fd)
 {
-    mBuf = capacity > 0 ? (char*)malloc(capacity * sizeof(char)) : NULL;
-    mStatus = mFd < 0 ? "Negative fd" : (capacity == 0 ? "Zero buffer capacity" : "");
+    mFile = fdopen(fd, "r");
+    mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
 }
 
 Reader::~Reader()
 {
-    free(mBuf);
+    if (mFile != NULL) fclose(mFile);
 }
 
-bool Reader::readLine(std::string* line, const char newline) {
-    if (!ok(line)) return false; // bad status
-    line->clear();
-    std::stringstream ss;
-    while (!EOR()) {
-        // read if available
-        if (mFd != -1 && mBufSize != mMaxSize) {
-            ssize_t amt = 0;
-            if (mRead >= mFlushed) {
-                amt = ::read(mFd, mBuf + mRead, mMaxSize - mRead);
-            } else {
-                amt = ::read(mFd, mBuf + mRead, mFlushed - mRead);
-            }
-            if (amt < 0) {
-                mStatus = "Fail to read from fd";
-                return false;
-            } else if (amt == 0) {
-                close(mFd);
-                mFd = -1;
-            }
-            mRead += amt;
-            mBufSize += amt;
-        }
+bool Reader::readLine(std::string* line) {
+    if (mFile == NULL) return false;
 
-        bool meetsNewLine = false;
-        if (mBufSize > 0) {
-            int start = mFlushed;
-            int end = mFlushed < mRead ? mRead : mMaxSize;
-            while (mFlushed < end && mBuf[mFlushed++] != newline && mBufSize > 0) mBufSize--;
-            meetsNewLine = (mBuf[mFlushed-1] == newline);
-            if (meetsNewLine) mBufSize--; // deduct the new line character
-            size_t len = meetsNewLine ? mFlushed - start - 1 : mFlushed - start;
-            ss.write(mBuf + start, len);
-        }
-
-        if (mRead >= (int) mMaxSize) mRead = 0;
-        if (mFlushed >= (int) mMaxSize) mFlushed = 0;
-
-        if (EOR() || meetsNewLine) {
-            line->assign(ss.str());
-            return true;
-        }
+    char* buf = NULL;
+    size_t len = 0;
+    ssize_t read = getline(&buf, &len, mFile);
+    if (read != -1) {
+        std::string s(buf);
+        line->assign(trim(s, DEFAULT_NEWLINE));
+    } else if (errno == EINVAL) {
+        mStatus = "Bad Argument";
     }
-    return false;
+    free(buf);
+    return read != -1;
 }
 
 bool Reader::ok(std::string* error) {
@@ -162,10 +188,41 @@
 }
 
 // ==============================================================================
+static int
+lookupName(const char** names, const int size, const char* name)
+{
+    for (int i=0; i<size; i++) {
+        if (strcmp(name, names[i]) == 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount)
+        :mEnumNames(enumNames),
+         mEnumValues(enumValues),
+         mEnumCount(enumCount)
+{
+}
+
+EnumTypeMap::~EnumTypeMap()
+{
+}
+
+int
+EnumTypeMap::parseValue(const std::string& value)
+{
+    int index = lookupName(mEnumNames, mEnumCount, value.c_str());
+    if (index < 0) return mEnumValues[0]; // Assume value 0 is default
+    return mEnumValues[index];
+}
+
 Table::Table(const char* names[], const uint64_t ids[], const int count)
         :mFieldNames(names),
          mFieldIds(ids),
-         mFieldCount(count)
+         mFieldCount(count),
+         mEnums()
 {
 }
 
@@ -173,41 +230,54 @@
 {
 }
 
-bool
-Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+void
+Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize)
 {
-    uint64_t found = 0;
-    for (int i=0; i<mFieldCount; i++) {
-        if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
-            found = mFieldIds[i];
-            break;
-        }
-    }
+    int index = lookupName(mFieldNames, mFieldCount, field);
+    if (index < 0) return;
 
+    EnumTypeMap enu(enumNames, enumValues, enumSize);
+    mEnums[index] = enu;
+}
+
+bool
+Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+    int index = lookupName(mFieldNames, mFieldCount, name.c_str());
+    if (index < 0) return false;
+
+    uint64_t found = mFieldIds[index];
     switch (found & FIELD_TYPE_MASK) {
         case FIELD_TYPE_DOUBLE:
         case FIELD_TYPE_FLOAT:
-            // TODO: support parse string to float/double
-            return false;
+            proto->write(found, toDouble(value));
+            break;
         case FIELD_TYPE_STRING:
         case FIELD_TYPE_BYTES:
-            proto.write(found, value);
+            proto->write(found, value);
             break;
         case FIELD_TYPE_INT64:
         case FIELD_TYPE_SINT64:
         case FIELD_TYPE_UINT64:
         case FIELD_TYPE_FIXED64:
         case FIELD_TYPE_SFIXED64:
-            proto.write(found, toLongLong(value));
+            proto->write(found, toLongLong(value));
             break;
         case FIELD_TYPE_BOOL:
+            return false;
         case FIELD_TYPE_ENUM:
+            if (mEnums.find(index) == mEnums.end()) {
+                // forget to add enum type mapping
+                return false;
+            }
+            proto->write(found, mEnums[index].parseValue(value));
+            break;
         case FIELD_TYPE_INT32:
         case FIELD_TYPE_SINT32:
         case FIELD_TYPE_UINT32:
         case FIELD_TYPE_FIXED32:
         case FIELD_TYPE_SFIXED32:
-            proto.write(found, toInt(value));
+            proto->write(found, toInt(value));
             break;
         default:
             return false;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 86761e9..e8366fa 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -17,9 +17,9 @@
 #ifndef INCIDENT_HELPER_UTIL_H
 #define INCIDENT_HELPER_UTIL_H
 
+#include <map>
 #include <string>
 #include <vector>
-#include <sstream>
 
 #include <android/util/ProtoOutputStream.h>
 
@@ -29,8 +29,13 @@
 typedef std::vector<std::string> record_t;
 typedef std::string (*trans_func) (const std::string&);
 
-const char DEFAULT_NEWLINE = '\n';
 const std::string DEFAULT_WHITESPACE = " \t";
+const std::string DEFAULT_NEWLINE = "\r\n";
+const std::string TAB_DELIMITER = "\t";
+const std::string COMMA_DELIMITER = ",";
+
+// returns true if c is a-zA-Z0-9 or underscore _
+bool isValidChar(char c);
 
 /**
  * When a text has a table format like this
@@ -47,19 +52,33 @@
 record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
 
 /**
- * When the line starts with the given key, the function returns true
- * as well as the line argument is changed to the rest part of the original.
+ * When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters.
+ * This function allows to parse record by its header's column position' indices, must in ascending order.
+ * At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters.
+ */
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE);
+
+/**
+ * When the line starts/ends with the given key, the function returns true
+ * as well as the line argument is changed to the rest trimmed part of the original.
  * e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes
  * "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:",
  * otherwise the line is not changed.
+ *
+ * In order to prevent two values have same prefix which cause entering to incorrect conditions,
+ * stripPrefix and stripSuffix can turn on a flag that requires the ending char in the line must not be a valid
+ * character or digits, this feature is off by default.
+ * i.e. ABC%some value, ABCD%other value
  */
-bool hasPrefix(std::string* line, const char* key);
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
 
 /**
  * Converts string to the desired type
  */
 int toInt(const std::string& s);
 long long toLongLong(const std::string& s);
+double toDouble(const std::string& s);
 
 /**
  * Reader class reads data from given fd in streaming fashion.
@@ -69,23 +88,29 @@
 {
 public:
     Reader(const int fd);
-    Reader(const int fd, const size_t capacity);
     ~Reader();
 
-    bool readLine(std::string* line, const char newline = DEFAULT_NEWLINE);
+    bool readLine(std::string* line);
     bool ok(std::string* error);
 
 private:
-    int mFd; // set mFd to -1 when read EOF()
-    const size_t mMaxSize;
-    size_t mBufSize;
-    char* mBuf; // implements a circular buffer
-
-    int mRead;
-    int mFlushed;
+    FILE* mFile;
     std::string mStatus;
-    // end of read
-    inline bool EOR() { return mFd == -1 && mBufSize == 0; };
+};
+
+class EnumTypeMap
+{
+public:
+    EnumTypeMap() {};
+    EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount);
+    ~EnumTypeMap();
+
+    int parseValue(const std::string& value);
+
+private:
+    const char** mEnumNames;
+    const uint32_t* mEnumValues;
+    int mEnumCount;
 };
 
 /**
@@ -98,12 +123,15 @@
     Table(const char* names[], const uint64_t ids[], const int count);
     ~Table();
 
-    bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+    // Add enum names to values for parsing purpose.
+    void addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize);
 
+    bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
 private:
     const char** mFieldNames;
     const uint64_t* mFieldIds;
     const int mFieldCount;
+    map<int, EnumTypeMap> mEnums;
 };
 
 #endif  // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 3da87b9c..5ebe9bd 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "incident_helper"
 
+#include "parsers/CpuInfoParser.h"
 #include "parsers/KernelWakesParser.h"
 #include "parsers/PageTypeInfoParser.h"
 #include "parsers/ProcrankParser.h"
@@ -54,6 +55,8 @@
             return new PageTypeInfoParser();
         case 2002:
             return new KernelWakesParser();
+        case 2003:
+            return new CpuInfoParser();
         default:
             return NULL;
     }
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
new file mode 100644
index 0000000..3faca00
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.proto.h"
+#include "ih_util.h"
+#include "CpuInfoParser.h"
+
+using namespace android::os;
+
+static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId,
+        const string& line, const string& delimiter,
+        const int count, const char* names[], const uint64_t ids[])
+{
+    record_t record = parseRecord(line, delimiter);
+    long long token = proto->start(fieldId);
+    for (int i=0; i<(int)record.size(); i++) {
+        for (int j=0; j<count; j++) {
+            if (stripSuffix(&record[i], names[j], true)) {
+                proto->write(ids[j], toInt(record[i]));
+                break;
+            }
+        }
+    }
+    proto->end(token);
+}
+
+status_t
+CpuInfoParser::Parse(const int in, const int out) const
+{
+    Reader reader(in);
+    string line;
+    header_t header;
+    vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
+    record_t record;
+    int nline = 0;
+    bool nextToSwap = false;
+    bool nextToUsage = false;
+
+    ProtoOutputStream proto;
+    Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT);
+    table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES,
+            CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT);
+    table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES,
+            CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT);
+
+    // parse line by line
+    while (reader.readLine(&line)) {
+        if (line.empty()) continue;
+
+        nline++;
+
+        if (stripPrefix(&line, "Tasks:")) {
+            writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER,
+                CpuInfo::TaskStats::_FIELD_COUNT,
+                CpuInfo::TaskStats::_FIELD_NAMES,
+                CpuInfo::TaskStats::_FIELD_IDS);
+            continue;
+        }
+        if (stripPrefix(&line, "Mem:")) {
+            writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER,
+                CpuInfo::MemStats::_FIELD_COUNT,
+                CpuInfo::MemStats::_FIELD_NAMES,
+                CpuInfo::MemStats::_FIELD_IDS);
+            continue;
+        }
+        if (stripPrefix(&line, "Swap:")) {
+            writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER,
+                CpuInfo::MemStats::_FIELD_COUNT,
+                CpuInfo::MemStats::_FIELD_NAMES,
+                CpuInfo::MemStats::_FIELD_IDS);
+            nextToSwap = true;
+            continue;
+        }
+
+        if (nextToSwap) {
+            writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE,
+                CpuInfo::CpuUsage::_FIELD_COUNT,
+                CpuInfo::CpuUsage::_FIELD_NAMES,
+                CpuInfo::CpuUsage::_FIELD_IDS);
+            nextToUsage = true;
+            nextToSwap = false;
+            continue;
+        }
+
+        // Header of tasks must be next to usage line
+        if (nextToUsage) {
+            // How to parse Header of Tasks:
+            // PID   TID USER         PR  NI[%CPU]S VIRT  RES PCY CMD             NAME
+            // After parsing, header = { PID, TID, USER, PR, NI, CPU, S, VIRT, RES, PCY, CMD, NAME }
+            // And columnIndices will contain end index of each word.
+            header = parseHeader(line, "[ %]");
+            nextToUsage = false;
+
+            // NAME is not in the list since the last split index is default to the end of line.
+            const char* headerNames[11] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD" };
+            size_t lastIndex = 0;
+            for (int i = 0; i < 11; i++) {
+                string s = headerNames[i];
+                lastIndex = line.find(s, lastIndex);
+                if (lastIndex == string::npos) {
+                    fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
+                    return -1;
+                }
+                lastIndex += s.length();
+                columnIndices.push_back(lastIndex);
+            }
+            // Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
+            // for example: ... CMD             NAME
+            //              ... Jit thread pool com.google.android.gms.feedback
+            // If use end index of CMD, parsed result = { "Jit", "thread pool com.google.android.gms.feedback" }
+            // If use start index of NAME, parsed result = { "Jit thread pool", "com.google.android.gms.feedback" }
+            int endCMD = columnIndices.back();
+            columnIndices.pop_back();
+            columnIndices.push_back(line.find("NAME", endCMD) - 1);
+            continue;
+        }
+
+        record = parseRecordByColumns(line, columnIndices);
+        if (record.size() != header.size()) {
+            fprintf(stderr, "[%s]Line %d has missing fields:\n%s\n", this->name.string(), nline, line.c_str());
+            continue;
+        }
+
+        long long token = proto.start(CpuInfo::TASKS);
+        for (int i=0; i<(int)record.size(); i++) {
+            if (!table.insertField(&proto, header[i], record[i])) {
+                fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
+                        this->name.string(), nline, header[i].c_str(), record[i].c_str());
+            }
+        }
+        proto.end(token);
+    }
+
+    if (!reader.ok(&line)) {
+        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+        return -1;
+    }
+
+    if (!proto.flush(out)) {
+        fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+        return -1;
+    }
+    fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+    return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.h b/cmds/incident_helper/src/parsers/CpuInfoParser.h
new file mode 100644
index 0000000..f57bb4e
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CPU_INFO_PARSER_H
+#define CPU_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu info parser, parses text produced by command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ */
+class CpuInfoParser : public TextParserBase {
+public:
+    CpuInfoParser() : TextParserBase(String8("CpuInfoParser")) {};
+    ~CpuInfoParser() {};
+
+    virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif  // CPU_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index cc4a1e1..ada4a5d 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -23,8 +23,6 @@
 
 using namespace android::os;
 
-const std::string LINE_DELIMITER = "\t";
-
 status_t
 KernelWakesParser::Parse(const int in, const int out) const
 {
@@ -42,12 +40,12 @@
         if (line.empty()) continue;
         // parse head line
         if (nline++ == 0) {
-            header = parseHeader(line, LINE_DELIMITER);
+            header = parseHeader(line, TAB_DELIMITER);
             continue;
         }
 
         // parse for each record, the line delimiter is \t only!
-        record = parseRecord(line, LINE_DELIMITER);
+        record = parseRecord(line, TAB_DELIMITER);
 
         if (record.size() != header.size()) {
             // TODO: log this to incident report!
@@ -57,7 +55,7 @@
 
         long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
         for (int i=0; i<(int)record.size(); i++) {
-            if (!table.insertField(proto, header[i], record[i])) {
+            if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
                         this->name.string(), nline, header[i].c_str(), record[i].c_str());
             }
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 6047bd1..f1b93ff 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -23,8 +23,6 @@
 
 using namespace android::os;
 
-const std::string LINE_DELIMITER = ",";
-
 status_t
 PageTypeInfoParser::Parse(const int in, const int out) const
 {
@@ -44,37 +42,37 @@
             continue;
         }
 
-        if (hasPrefix(&line, "Page block order:")) {
+        if (stripPrefix(&line, "Page block order:")) {
             pageBlockOrder = toInt(line);
             proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
             continue;
         }
-        if (hasPrefix(&line, "Pages per block:")) {
+        if (stripPrefix(&line, "Pages per block:")) {
             proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
             continue;
         }
-        if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+        if (stripPrefix(&line, "Free pages count per migrate type at order")) {
             migrateTypeSession = true;
             continue;
         }
-        if (hasPrefix(&line, "Number of blocks type")) {
+        if (stripPrefix(&line, "Number of blocks type")) {
             blockHeader = parseHeader(line);
             continue;
         }
 
-        record_t record = parseRecord(line, LINE_DELIMITER);
+        record_t record = parseRecord(line, COMMA_DELIMITER);
         if (migrateTypeSession && record.size() == 3) {
             long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
             // expect part 0 starts with "Node"
-            if (hasPrefix(&record[0], "Node")) {
+            if (stripPrefix(&record[0], "Node")) {
                 proto.write(MigrateTypeProto::NODE, toInt(record[0]));
             } else return BAD_VALUE;
             // expect part 1 starts with "zone"
-            if (hasPrefix(&record[1], "zone")) {
+            if (stripPrefix(&record[1], "zone")) {
                 proto.write(MigrateTypeProto::ZONE, record[1]);
             } else return BAD_VALUE;
             // expect part 2 starts with "type"
-            if (hasPrefix(&record[2], "type")) {
+            if (stripPrefix(&record[2], "type")) {
                 // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
                 // An example looks like:
                 // header line:      type    0   1   2 3 4 5 6 7 8 9 10
@@ -94,16 +92,16 @@
             proto.end(token);
         } else if (!blockHeader.empty() && record.size() == 2) {
             long long token = proto.start(PageTypeInfo::BLOCKS);
-            if (hasPrefix(&record[0], "Node")) {
+            if (stripPrefix(&record[0], "Node")) {
                 proto.write(BlockProto::NODE, toInt(record[0]));
             } else return BAD_VALUE;
 
-            if (hasPrefix(&record[1], "zone")) {
+            if (stripPrefix(&record[1], "zone")) {
                 record_t blockCounts = parseRecord(record[1]);
                 proto.write(BlockProto::ZONE, blockCounts[0]);
 
                 for (size_t i=0; i<blockHeader.size(); i++) {
-                    if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) {
+                    if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
                         return BAD_VALUE;
                     }
                 }
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index 93f970f..a4eb0fd 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -46,11 +46,11 @@
             continue;
         }
 
-        if (hasPrefix(&line, "ZRAM:")) {
+        if (stripPrefix(&line, "ZRAM:")) {
             zram = line;
             continue;
         }
-        if (hasPrefix(&line, "RAM:")) {
+        if (stripPrefix(&line, "RAM:")) {
             ram = line;
             continue;
         }
@@ -68,7 +68,7 @@
 
         long long token = proto.start(Procrank::PROCESSES);
         for (int i=0; i<(int)record.size(); i++) {
-            if (!table.insertField(proto, header[i], record[i])) {
+            if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
                         this->name.string(), nline, header[i].c_str(), record[i].c_str());
             }
@@ -82,7 +82,7 @@
         record = parseRecord(total);
         long long token = proto.start(SummaryProto::TOTAL);
         for (int i=(int)record.size(); i>0; i--) {
-            table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
+            table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
         }
         proto.end(token);
     }
diff --git a/cmds/incident_helper/testdata/cpuinfo.txt b/cmds/incident_helper/testdata/cpuinfo.txt
new file mode 100644
index 0000000..ec4a839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpuinfo.txt
@@ -0,0 +1,15 @@
+Tasks: 2038 total,   1 running,2033 sleeping,   0 stopped,   0 zombie
+
+Mem:   3842668k total,  3761936k used,    80732k free,   220188k buffers
+
+Swap:   524284k total,    25892k used,   498392k free,  1316952k cached
+
+400%cpu  17%user   0%nice  43%sys 338%idle   0%iow   0%irq   1%sirq   0%host
+
+  PID   TID USER         PR  NI[%CPU]S VIRT  RES PCY CMD             NAME               
+
+
+29438 29438 rootabcdefghij 20 0 57.9 R  14M 3.8M     top test        top
+  916   916 system       18  -2  1.4 S 4.6G 404M  fg system_server   system_server
+   28    28 root         -2   0  1.4 S    0    0  bg rcuc/3          [rcuc/3]
+   27    27 root         RT   0  1.4 S    0    0  ta migration/3     [migration/3]
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
new file mode 100644
index 0000000..57ad15c
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CpuInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuInfoParserTest : public Test {
+public:
+    virtual void SetUp() override {
+        ASSERT_TRUE(tf.fd != -1);
+    }
+
+    string getSerializedString(::google::protobuf::Message& message) {
+        string expectedStr;
+        message.SerializeToFileDescriptor(tf.fd);
+        ReadFileToString(tf.path, &expectedStr);
+        return expectedStr;
+    }
+
+protected:
+    TemporaryFile tf;
+
+    const string kTestPath = GetExecutableDirectory();
+    const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuInfoParserTest, HasSwapInfo) {
+    const string testFile = kTestDataPath + "cpuinfo.txt";
+    CpuInfoParser parser;
+    CpuInfo expected;
+
+    CpuInfo::TaskStats* taskStats = expected.mutable_task_stats();
+    taskStats->set_total(2038);
+    taskStats->set_running(1);
+    taskStats->set_sleeping(2033);
+    taskStats->set_stopped(0);
+    taskStats->set_zombie(0);
+
+    CpuInfo::MemStats* mem = expected.mutable_mem();
+    mem->set_total(3842668);
+    mem->set_used(3761936);
+    mem->set_free(80732);
+    mem->set_buffers(220188);
+
+    CpuInfo::MemStats* swap = expected.mutable_swap();
+    swap->set_total(524284);
+    swap->set_used(25892);
+    swap->set_free(498392);
+    swap->set_cached(1316952);
+
+    CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage();
+    usage->set_cpu(400);
+    usage->set_user(17);
+    usage->set_nice(0);
+    usage->set_sys(43);
+    usage->set_idle(338);
+    usage->set_iow(0);
+    usage->set_irq(0);
+    usage->set_sirq(1);
+    usage->set_host(0);
+
+    // This is a special line which is able to be parsed by the CpuInfoParser
+    CpuInfo::Task* task1 = expected.add_tasks();
+    task1->set_pid(29438);
+    task1->set_tid(29438);
+    task1->set_user("rootabcdefghij");
+    task1->set_pr("20");
+    task1->set_ni(0);
+    task1->set_cpu(57.9);
+    task1->set_s(CpuInfo::Task::STATUS_R);
+    task1->set_virt("14M");
+    task1->set_res("3.8M");
+    task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN);
+    task1->set_cmd("top test");
+    task1->set_name("top");
+
+    CpuInfo::Task* task2 = expected.add_tasks();
+    task2->set_pid(916);
+    task2->set_tid(916);
+    task2->set_user("system");
+    task2->set_pr("18");
+    task2->set_ni(-2);
+    task2->set_cpu(1.4);
+    task2->set_s(CpuInfo::Task::STATUS_S);
+    task2->set_virt("4.6G");
+    task2->set_res("404M");
+    task2->set_pcy(CpuInfo::Task::POLICY_fg);
+    task2->set_cmd("system_server");
+    task2->set_name("system_server");
+
+    CpuInfo::Task* task3 = expected.add_tasks();
+    task3->set_pid(28);
+    task3->set_tid(28);
+    task3->set_user("root");
+    task3->set_pr("-2");
+    task3->set_ni(0);
+    task3->set_cpu(1.4);
+    task3->set_s(CpuInfo::Task::STATUS_S);
+    task3->set_virt("0");
+    task3->set_res("0");
+    task3->set_pcy(CpuInfo::Task::POLICY_bg);
+    task3->set_cmd("rcuc/3");
+    task3->set_name("[rcuc/3]");
+
+    CpuInfo::Task* task4 = expected.add_tasks();
+    task4->set_pid(27);
+    task4->set_tid(27);
+    task4->set_user("root");
+    task4->set_pr("RT");
+    task4->set_ni(0);
+    task4->set_cpu(1.4);
+    task4->set_s(CpuInfo::Task::STATUS_S);
+    task4->set_virt("0");
+    task4->set_res("0");
+    task4->set_pcy(CpuInfo::Task::POLICY_ta);
+    task4->set_cmd("migration/3");
+    task4->set_name("[migration/3]");
+
+    int fd = open(testFile.c_str(), O_RDONLY);
+    ASSERT_TRUE(fd != -1);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+    EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+    close(fd);
+}
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index 3cef6b3..5740b33 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -62,6 +62,59 @@
     EXPECT_EQ(expected, result);
 }
 
+TEST(IhUtilTest, ParseRecordByColumns) {
+    record_t result, expected;
+    std::vector<int> indices = { 3, 10 };
+
+    result = parseRecordByColumns("12345", indices);
+    expected = {};
+    EXPECT_EQ(expected, result);
+
+    result = parseRecordByColumns("abc \t2345  6789 ", indices);
+    expected = { "abc", "2345", "6789" };
+    EXPECT_EQ(expected, result);
+
+    result = parseRecordByColumns("abc \t23456789 bob", indices);
+    expected = { "abc", "23456789", "bob" };
+    EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, stripPrefix) {
+    string data1 = "Swap: abc ";
+    EXPECT_TRUE(stripPrefix(&data1, "Swap:"));
+    EXPECT_THAT(data1, StrEq("abc"));
+
+    string data2 = "Swap: abc ";
+    EXPECT_FALSE(stripPrefix(&data2, "Total:"));
+    EXPECT_THAT(data2, StrEq("Swap: abc "));
+
+    string data3 = "Swap: abc ";
+    EXPECT_TRUE(stripPrefix(&data3, "Swa"));
+    EXPECT_THAT(data3, StrEq("p: abc"));
+
+    string data4 = "Swap: abc ";
+    EXPECT_FALSE(stripPrefix(&data4, "Swa", true));
+    EXPECT_THAT(data4, StrEq("Swap: abc "));
+}
+
+TEST(IhUtilTest, stripSuffix) {
+    string data1 = " 243%abc";
+    EXPECT_TRUE(stripSuffix(&data1, "abc"));
+    EXPECT_THAT(data1, StrEq("243%"));
+
+    string data2 = " 243%abc";
+    EXPECT_FALSE(stripSuffix(&data2, "Not right"));
+    EXPECT_THAT(data2, StrEq(" 243%abc"));
+
+    string data3 = " 243%abc";
+    EXPECT_TRUE(stripSuffix(&data3, "bc"));
+    EXPECT_THAT(data3, StrEq("243%a"));
+
+    string data4 = " 243%abc";
+    EXPECT_FALSE(stripSuffix(&data4, "bc", true));
+    EXPECT_THAT(data4, StrEq(" 243%abc"));
+}
+
 TEST(IhUtilTest, Reader) {
     TemporaryFile tf;
     ASSERT_NE(tf.fd, -1);
@@ -79,23 +132,6 @@
     ASSERT_TRUE(r.ok(&line));
 }
 
-TEST(IhUtilTest, ReaderSmallBufSize) {
-    TemporaryFile tf;
-    ASSERT_NE(tf.fd, -1);
-    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooiecccojreo", tf.path));
-
-    Reader r(tf.fd, 5);
-    string line;
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq("test string"));
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq("second"));
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq("ooiecccojreo"));
-    ASSERT_FALSE(r.readLine(&line));
-    ASSERT_TRUE(r.ok(&line));
-}
-
 TEST(IhUtilTest, ReaderEmpty) {
     TemporaryFile tf;
     ASSERT_NE(tf.fd, -1);
@@ -103,9 +139,8 @@
 
     Reader r(tf.fd);
     string line;
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq(""));
     ASSERT_FALSE(r.readLine(&line));
+    EXPECT_THAT(line, StrEq(""));
     ASSERT_TRUE(r.ok(&line));
 }
 
@@ -130,15 +165,7 @@
     string line;
     EXPECT_FALSE(r.readLine(&line));
     EXPECT_FALSE(r.ok(&line));
-    EXPECT_THAT(line, StrEq("Negative fd"));
-}
-
-TEST(IhUtilTest, ReaderFailedZeroBufferSize) {
-    Reader r(23, 0);
-    string line;
-    EXPECT_FALSE(r.readLine(&line));
-    EXPECT_FALSE(r.ok(&line));
-    EXPECT_THAT(line, StrEq("Zero buffer capacity"));
+    EXPECT_THAT(line, StrEq("Invalid fd -123"));
 }
 
 TEST(IhUtilTest, ReaderFailedBadFd) {
@@ -146,5 +173,5 @@
     string line;
     EXPECT_FALSE(r.readLine(&line));
     EXPECT_FALSE(r.ok(&line));
-    EXPECT_THAT(line, StrEq("Fail to read from fd"));
+    EXPECT_THAT(line, StrEq("Invalid fd 1231432"));
 }
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
new file mode 100644
index 0000000..a95fa57
--- /dev/null
+++ b/core/proto/android/os/cpuinfo.proto
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuInfoProto";
+
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
+package android.os;
+
+/**
+ * Data structure of the linux command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ *
+ * Next Tag: 6
+ */
+message CpuInfo {
+
+    message TaskStats {
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 total = 1;    // total number of cpu tasks
+        optional int32 running = 2;  // number of running tasks
+        optional int32 sleeping = 3; // number of sleeping tasks
+        optional int32 stopped = 4;  // number of stopped tasks
+        optional int32 zombie = 5;   // number of zombie tasks
+    }
+    optional TaskStats task_stats = 1;
+
+    message MemStats { // unit in kB
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 total = 1;
+        optional int32 used = 2;
+        optional int32 free = 3;
+        optional int32 buffers = 4;
+        optional int32 cached = 5;
+    }
+    optional MemStats mem = 2;
+    optional MemStats swap = 3;
+
+    message CpuUsage { // unit is percentage %
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 cpu = 1;   // 400% cpu indicates 4 cores
+        optional int32 user = 2;
+        optional int32 nice = 3;
+        optional int32 sys = 4;
+        optional int32 idle = 5;
+        optional int32 iow = 6;
+        optional int32 irq = 7;
+        optional int32 sirq = 8;
+        optional int32 host = 9;
+    }
+    optional CpuUsage cpu_usage = 4;
+
+    // Next Tag: 13
+    message Task {
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 pid = 1;
+        optional int32 tid = 2;
+        optional string user = 3;
+        optional string pr = 4;     // priority of each task, using string type is because special value RT (real time)
+        optional sint32 ni = 5;     // niceness value
+        optional float cpu = 6;     // precentage of cpu usage of the task
+
+        enum Status {
+            option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+            STATUS_UNKNOWN = 0;
+            STATUS_D = 1;  // uninterruptible sleep
+            STATUS_R = 2;  // running
+            STATUS_S = 3;  // sleeping
+            STATUS_T = 4;  // traced or stopped
+            STATUS_Z = 5;  // zombie
+        }
+        optional Status s = 7;      // process status
+        optional string virt = 8;   // virtual memory size, i.e. 14.0G, 13.5M
+        optional string res = 9;    // Resident size, i.e. 0, 3.1G
+
+        // How Android memory manager will treat the task
+        enum Policy {
+            option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+            POLICY_UNKNOWN = 0;
+            POLICY_fg = 1;  // foreground, the name is lower case for parsing the value
+            POLICY_bg = 2;  // background, the name is lower case for parsing the value
+            POLICY_ta = 3;  // TODO: figure out what is this value
+        }
+        optional Policy pcy = 10;   // Policy of the task
+        optional string cmd = 11;   // thread name
+        optional string name = 12;  // program name
+    }
+    repeated Task tasks = 5;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e998b09..55ea285 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/os/cpuinfo.proto";
 import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
 import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
@@ -68,6 +69,11 @@
         (section).args = "/d/wakeup_sources"
     ];
 
+    optional CpuInfo cpu_info = 2003 [
+        (section).type = SECTION_COMMAND,
+        (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
+    ];
+
 
     // System Services
     optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index d032a45..eaad37a 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -29,7 +29,7 @@
 
 // Next Tag: 11
 message WakeupSourceProto {
-    option (stream_proto.stream).enable_fields_mapping = true;
+    option (stream_proto.stream_msg).enable_fields_mapping = true;
 
     // Name of the event which triggers application processor
     optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index 22b3d73..b86ee01 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -63,7 +63,7 @@
 
 // Next tag: 9
 message BlockProto {
-    option (stream_proto.stream).enable_fields_mapping = true;
+    option (stream_proto.stream_msg).enable_fields_mapping = true;
 
     optional int32 node = 1;
 
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 4d62a60..9945f2e 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -33,7 +33,7 @@
 
 // Next Tag: 11
 message ProcessProto {
-    option (stream_proto.stream).enable_fields_mapping = true;
+    option (stream_proto.stream_msg).enable_fields_mapping = true;
 
     // ID of the process
     optional int32 pid = 1;
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index 4816984..9aef562 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -18,6 +18,12 @@
     return file_descriptor.name() + ".h";
 }
 
+static inline bool
+should_generate_enums_mapping(const EnumDescriptorProto& enu)
+{
+    return enu.options().GetExtension(stream_enum).enable_enums_mapping();
+}
+
 static void
 write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
 {
@@ -29,6 +35,23 @@
                 << make_constant_name(value.name())
                 << " = " << value.number() << ";" << endl;
     }
+
+    if (should_generate_enums_mapping(enu)) {
+        string name = make_constant_name(enu.name());
+        string prefix = name + "_";
+        text << indent << "const int _ENUM_" << name << "_COUNT = " << N << ";" << endl;
+        text << indent << "const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl;
+        for (int i=0; i<N; i++) {
+            text << indent << INDENT << "\"" << stripPrefix(enu.value(i).name(), prefix) << "\"," << endl;
+        }
+        text << indent << "};" << endl;
+        text << indent << "const uint32_t _ENUM_" << name << "_VALUES[" << N << "] = {" << endl;
+        for (int i=0; i<N; i++) {
+            text << indent << INDENT << make_constant_name(enu.value(i).name()) << "," << endl;
+        }
+        text << indent << "};" << endl;
+    }
+
     text << endl;
 }
 
@@ -59,7 +82,7 @@
 static inline bool
 should_generate_fields_mapping(const DescriptorProto& message)
 {
-    return message.options().GetExtension(stream).enable_fields_mapping();
+    return message.options().GetExtension(stream_msg).enable_fields_mapping();
 }
 
 static void
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
index 123506c..c081209 100644
--- a/tools/streaming_proto/stream.proto
+++ b/tools/streaming_proto/stream.proto
@@ -21,12 +21,22 @@
 package android.stream_proto;
 
 // This option tells streaming proto plugin to compile .proto files with extra features.
-message StreamFlags {
+message MessageOptions {
   // creates a mapping of field names of the message to its field ids
   optional bool enable_fields_mapping = 1;
 }
 
 extend google.protobuf.MessageOptions {
     // Flags used by streaming proto plugins
-    optional StreamFlags stream = 126856794;
+    optional MessageOptions stream_msg = 126856794;
+}
+
+message EnumOptions {
+  // creates a mapping of enum names to its values, strip its prefix enum type for each value
+  optional bool enable_enums_mapping = 1;
+}
+
+extend google.protobuf.EnumOptions {
+    // Flags used by streaming proto plugins
+    optional EnumOptions stream_enum = 126856794;
 }
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index bd34ab7..607d820 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -108,6 +108,17 @@
     return result;
 }
 
+string
+stripPrefix(const string& str, const string& prefix)
+{
+    if (str.size() <= prefix.size()) return str;
+    size_t i = 0, len = prefix.size();
+    for (; i<len; i++) {
+        if (str[i] != prefix[i]) return str;
+    }
+    return str.substr(i);
+}
+
 } // namespace stream_proto
 } // namespace android
 
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index d6f195f..315b275 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -26,15 +26,20 @@
 string file_base_name(const string& str);
 
 /**
- * Replace all occurances of 'replace' with 'with'.
+ * Replaces all occurances of 'replace' with 'with'.
  */
 string replace_string(const string& str, const char replace, const char with);
 
 /**
- * Split a string to parts by delimiter.
+ * Splits a string to parts by delimiter.
  */
 vector<string> split(const string& str, const char delimiter);
 
+/**
+ * Returns the rest of str if it has prefix, otherwise return all.
+ */
+string stripPrefix(const string& str, const string& prefix);
+
 } // namespace stream_proto
 } // namespace android