summaryrefslogtreecommitdiff
path: root/cmds/incident_helper/src
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/incident_helper/src')
-rw-r--r--cmds/incident_helper/src/TextParserBase.cpp56
-rw-r--r--cmds/incident_helper/src/TextParserBase.h71
-rw-r--r--cmds/incident_helper/src/ih_util.cpp458
-rw-r--r--cmds/incident_helper/src/ih_util.h201
-rw-r--r--cmds/incident_helper/src/main.cpp113
-rw-r--r--cmds/incident_helper/src/parsers/BatteryTypeParser.cpp60
-rw-r--r--cmds/incident_helper/src/parsers/BatteryTypeParser.h36
-rw-r--r--cmds/incident_helper/src/parsers/CpuFreqParser.cpp90
-rw-r--r--cmds/incident_helper/src/parsers/CpuFreqParser.h35
-rw-r--r--cmds/incident_helper/src/parsers/CpuInfoParser.cpp162
-rw-r--r--cmds/incident_helper/src/parsers/CpuInfoParser.h36
-rw-r--r--cmds/incident_helper/src/parsers/EventLogTagsParser.cpp84
-rw-r--r--cmds/incident_helper/src/parsers/EventLogTagsParser.h35
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.cpp81
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.h33
-rw-r--r--cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp125
-rw-r--r--cmds/incident_helper/src/parsers/PageTypeInfoParser.h35
-rw-r--r--cmds/incident_helper/src/parsers/ProcrankParser.cpp112
-rw-r--r--cmds/incident_helper/src/parsers/ProcrankParser.h35
-rw-r--r--cmds/incident_helper/src/parsers/PsParser.cpp95
-rw-r--r--cmds/incident_helper/src/parsers/PsParser.h33
-rw-r--r--cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp227
-rw-r--r--cmds/incident_helper/src/parsers/SystemPropertiesParser.h35
23 files changed, 2248 insertions, 0 deletions
diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp
new file mode 100644
index 000000000000..a8f9968ee8f6
--- /dev/null
+++ b/cmds/incident_helper/src/TextParserBase.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "TextParserBase.h"
+
+#include <android-base/file.h>
+
+using namespace android::base;
+using namespace std;
+
+// ================================================================================
+status_t NoopParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+status_t ReverseParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ // reverse the content
+ reverse(content.begin(), content.end());
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+} \ No newline at end of file
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
new file mode 100644
index 000000000000..166796673e25
--- /dev/null
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -0,0 +1,71 @@
+/*
+ * 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 TEXT_PARSER_BASE_H
+#define TEXT_PARSER_BASE_H
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+/**
+ * Base class for text parser
+ */
+class TextParserBase {
+public:
+ String8 name;
+
+ TextParserBase(String8 name) : name(name) {};
+ virtual ~TextParserBase() {};
+
+ virtual status_t Parse(const int in, const int out) const = 0;
+};
+
+/**
+ * No op parser returns what it reads
+ */
+class NoopParser : public TextParserBase {
+public:
+ NoopParser() : TextParserBase(String8("NoopParser")) {};
+ ~NoopParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+/**
+ * This parser is used for testing only, results in timeout.
+ */
+class TimeoutParser : public TextParserBase {
+public:
+ TimeoutParser() : TextParserBase(String8("TimeoutParser")) {};
+ ~TimeoutParser() {};
+
+ virtual status_t Parse(const int /** in */, const int /** out */) const { while (true); };
+};
+
+/**
+ * This parser is used for testing only, results in reversed input text.
+ */
+class ReverseParser : public TextParserBase {
+public:
+ ReverseParser() : TextParserBase(String8("ReverseParser")) {};
+ ~ReverseParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // TEXT_PARSER_BASE_H
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
new file mode 100644
index 000000000000..847b26a39ffe
--- /dev/null
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -0,0 +1,458 @@
+/*
+ * 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 "ih_util.h"
+
+#include <algorithm>
+#include <sstream>
+#include <unistd.h>
+
+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)'_');
+}
+
+std::string trim(const std::string& s, const std::string& charset) {
+ const auto head = s.find_first_not_of(charset);
+ if (head == std::string::npos) return "";
+
+ const auto tail = s.find_last_not_of(charset);
+ return s.substr(head, tail - head + 1);
+}
+
+static inline std::string toLowerStr(const std::string& s) {
+ std::string res(s);
+ std::transform(res.begin(), res.end(), res.begin(), ::tolower);
+ return res;
+}
+
+static inline std::string trimDefault(const std::string& s) {
+ return trim(s, DEFAULT_WHITESPACE);
+}
+
+static inline std::string trimHeader(const std::string& s) {
+ return toLowerStr(trimDefault(s));
+}
+
+static inline bool isNumber(const std::string& s) {
+ std::string::const_iterator it = s.begin();
+ while (it != s.end() && std::isdigit(*it)) ++it;
+ return !s.empty() && it == s.end();
+}
+
+// This is similiar to Split in android-base/file.h, but it won't add empty string
+static void split(const std::string& line, std::vector<std::string>& words,
+ const trans_func& func, const std::string& delimiters) {
+ words.clear(); // clear the buffer before split
+
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = line.find_first_of(delimiters, base);
+ if (found != base) {
+ std::string word = (*func) (line.substr(base, found - base));
+ if (!word.empty()) {
+ words.push_back(word);
+ }
+ }
+ if (found == line.npos) break;
+ base = found + 1;
+ }
+}
+
+header_t parseHeader(const std::string& line, const std::string& delimiters) {
+ header_t header;
+ trans_func f = &trimHeader;
+ split(line, header, f, delimiters);
+ return header;
+}
+
+record_t parseRecord(const std::string& line, const std::string& delimiters) {
+ record_t record;
+ trans_func f = &trimDefault;
+ split(line, record, f, delimiters);
+ return record;
+}
+
+bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) {
+ indices.clear();
+
+ size_t lastIndex = 0;
+ int i = 0;
+ while (headerNames[i] != NULL) {
+ string s = headerNames[i];
+ lastIndex = line.find(s, lastIndex);
+ if (lastIndex == string::npos) {
+ fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
+ return false;
+ }
+ lastIndex += s.length();
+ indices.push_back(lastIndex);
+ i++;
+ }
+
+ return true;
+}
+
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
+ record_t record;
+ int lastIndex = 0;
+ int lastBeginning = 0;
+ int lineSize = (int)line.size();
+ for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
+ int idx = *it;
+ if (idx <= lastIndex) {
+ // We saved up until lastIndex last time, so we should start at
+ // lastIndex + 1 this time.
+ idx = lastIndex + 1;
+ }
+ if (idx > lineSize) {
+ if (lastIndex < idx && lastIndex < lineSize) {
+ // There's a little bit more for us to save, which we'll do
+ // outside of the loop.
+ break;
+ }
+ // If we're past the end of the line AND we've already saved everything up to the end.
+ fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize);
+ record.clear(); // The indices are wrong, return empty.
+ return record;
+ }
+ while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
+ record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
+ lastBeginning = lastIndex;
+ lastIndex = idx;
+ }
+ if (lineSize - lastIndex > 0) {
+ int beginning = lastIndex;
+ if (record.size() == indices.size()) {
+ // We've already encountered all of the columns...put whatever is
+ // left in the last column.
+ record.pop_back();
+ beginning = lastBeginning;
+ }
+ record.push_back(trimDefault(line.substr(beginning, lineSize - beginning)));
+ }
+ return record;
+}
+
+void printRecord(const record_t& record) {
+ fprintf(stderr, "Record: { ");
+ if (record.size() == 0) {
+ fprintf(stderr, "}\n");
+ return;
+ }
+ for(size_t i = 0; i < record.size(); ++i) {
+ if(i != 0) fprintf(stderr, "\", ");
+ fprintf(stderr, "\"%s", record[i].c_str());
+ }
+ fprintf(stderr, "\" }\n");
+}
+
+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;
+ int len = (int)line->length();
+ int i = 0;
+ int j = head;
+ while (key[i] != '\0') {
+ if (j >= len || key[i++] != line->at(j++)) {
+ return false;
+ }
+ }
+
+ 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;
+}
+
+std::string behead(std::string* line, const char cut) {
+ auto found = line->find_first_of(cut);
+ if (found == std::string::npos) {
+ std::string head = line->substr(0);
+ line->assign("");
+ return head;
+ }
+ std::string head = line->substr(0, found);
+ while(line->at(found) == cut) found++; // trim more cut of the rest
+ line->assign(line->substr(found));
+ return head;
+}
+
+int toInt(const std::string& s) {
+ return atoi(s.c_str());
+}
+
+long long toLongLong(const std::string& s) {
+ return atoll(s.c_str());
+}
+
+double toDouble(const std::string& s) {
+ return atof(s.c_str());
+}
+
+// ==============================================================================
+Reader::Reader(const int fd)
+{
+ mFile = fdopen(fd, "r");
+ mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
+}
+
+Reader::~Reader()
+{
+ if (mFile != NULL) fclose(mFile);
+}
+
+bool Reader::readLine(std::string* line) {
+ if (mFile == NULL) return false;
+
+ 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";
+ }
+ free(buf);
+ return read != -1;
+}
+
+bool Reader::ok(std::string* error) {
+ error->assign(mStatus);
+ return mStatus.empty();
+}
+
+// ==============================================================================
+Table::Table(const char* names[], const uint64_t ids[], const int count)
+ :mEnums(),
+ mEnumValuesByName()
+{
+ map<std::string, uint64_t> fields;
+ for (int i = 0; i < count; i++) {
+ fields[names[i]] = ids[i];
+ }
+ mFields = fields;
+}
+
+Table::~Table()
+{
+}
+
+void
+Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
+{
+ if (mFields.find(field) == mFields.end()) {
+ fprintf(stderr, "Field '%s' not found", string(field).c_str());
+ return;
+ }
+
+ map<std::string, int> enu;
+ for (int i = 0; i < enumSize; i++) {
+ enu[enumNames[i]] = enumValues[i];
+ }
+ mEnums[field] = enu;
+}
+
+void
+Table::addEnumNameToValue(const char* enumName, const int enumValue)
+{
+ mEnumValuesByName[enumName] = enumValue;
+}
+
+bool
+Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+ if (mFields.find(name) == mFields.end()) return false;
+
+ uint64_t found = mFields[name];
+ record_t repeats; // used for repeated fields
+ switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
+ proto->write(found, toDouble(value));
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
+ proto->write(found, value);
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
+ proto->write(found, toLongLong(value));
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
+ if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
+ proto->write(found, true);
+ break;
+ }
+ if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
+ proto->write(found, false);
+ break;
+ }
+ return false;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
+ // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
+ if (mEnums.find(name) != mEnums.end()) {
+ if (mEnums[name].find(value) != mEnums[name].end()) {
+ proto->write(found, mEnums[name][value]);
+ } else {
+ proto->write(found, 0); // TODO: should get the default enum value (Unknown)
+ }
+ } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
+ proto->write(found, mEnumValuesByName[value]);
+ } else if (isNumber(value)) {
+ proto->write(found, toInt(value));
+ } else {
+ return false;
+ }
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
+ proto->write(found, toInt(value));
+ break;
+ // REPEATED TYPE below:
+ case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
+ repeats = parseRecord(value, COMMA_DELIMITER);
+ for (size_t i=0; i<repeats.size(); i++) {
+ proto->write(found, toInt(repeats[i]));
+ }
+ break;
+ case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
+ repeats = parseRecord(value, COMMA_DELIMITER);
+ for (size_t i=0; i<repeats.size(); i++) {
+ proto->write(found, repeats[i]);
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+// ================================================================================
+Message::Message(Table* table)
+ :mTable(table),
+ mPreviousField(""),
+ mTokens(),
+ mSubMessages()
+{
+}
+
+Message::~Message()
+{
+}
+
+void
+Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
+{
+ for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
+ if (iter->second == fieldId) {
+ mSubMessages[iter->first] = fieldMsg;
+ return;
+ }
+ }
+}
+
+bool
+Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+ // If the field name can be found, it means the name is a primitive field.
+ if (mTable->mFields.find(name) != mTable->mFields.end()) {
+ endSession(proto);
+ // The only edge case is for example ro.hardware itself is a message, so a field called "value"
+ // would be defined in proto Ro::Hardware and it must be the first field.
+ if (mSubMessages.find(name) != mSubMessages.end()) {
+ startSession(proto, name);
+ return mSubMessages[name]->insertField(proto, "value", value);
+ } else {
+ return mTable->insertField(proto, name, value);
+ }
+ }
+
+ // Try to find the message field which is the prefix of name, so the value would be inserted
+ // recursively into the submessage.
+ string mutableName = name;
+ for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
+ string fieldName = iter->first;
+ string prefix = fieldName + "_"; // underscore is the delimiter in the name
+ if (stripPrefix(&mutableName, prefix.c_str())) {
+ if (mPreviousField != fieldName) {
+ endSession(proto);
+ startSession(proto, fieldName);
+ }
+ return mSubMessages[fieldName]->insertField(proto, mutableName, value);
+ }
+ }
+ // Can't find the name in proto definition, handle it separately.
+ return false;
+}
+
+void
+Message::startSession(ProtoOutputStream* proto, const string& name)
+{
+ uint64_t fieldId = mTable->mFields[name];
+ long long token = proto->start(fieldId);
+ mPreviousField = name;
+ mTokens.push(token);
+}
+
+void
+Message::endSession(ProtoOutputStream* proto)
+{
+ if (mPreviousField == "") return;
+ if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
+ mSubMessages[mPreviousField]->endSession(proto);
+ }
+ proto->end(mTokens.top());
+ mTokens.pop();
+ mPreviousField = "";
+}
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
new file mode 100644
index 000000000000..53f443873e4d
--- /dev/null
+++ b/cmds/incident_helper/src/ih_util.h
@@ -0,0 +1,201 @@
+/*
+ * 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 INCIDENT_HELPER_UTIL_H
+#define INCIDENT_HELPER_UTIL_H
+
+#include <map>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include <android/util/ProtoOutputStream.h>
+
+using namespace android::util;
+
+typedef std::vector<std::string> header_t;
+typedef std::vector<std::string> record_t;
+typedef std::string (*trans_func) (const std::string&);
+
+const std::string DEFAULT_WHITESPACE = " \t";
+const std::string DEFAULT_NEWLINE = "\r\n";
+const std::string TAB_DELIMITER = "\t";
+const std::string COMMA_DELIMITER = ",";
+const std::string PIPE_DELIMITER = "|";
+const std::string PARENTHESES_DELIMITER = "()";
+
+// returns true if c is a-zA-Z0-9 or underscore
+bool isValidChar(char c);
+
+// trim the string with the given charset
+std::string trim(const std::string& s, const std::string& charset);
+
+/**
+ * When a text has a table format like this
+ * line 1: HeadA HeadB HeadC
+ * line 2: v1 v2 v3
+ * line 3: v11 v12 v13
+ *
+ * We want to parse the line in structure given the delimiter.
+ * parseHeader is used to parse the firse line of the table and returns a list of strings in lower case
+ * parseRecord is used to parse other lines and returns a list of strings
+ * empty strings are skipped
+ */
+header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
+record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
+
+/**
+ * Gets the list of end indices of each word in the line and places it in the given vector,
+ * clearing out the vector beforehand. These indices can be used with parseRecordByColumns.
+ * Will return false if there was a problem getting the indices. headerNames
+ * must be NULL terminated.
+ */
+bool getColumnIndices(std::vector<int>& indices, const char* headerNames[], const std::string& line);
+
+/**
+ * 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);
+
+/** Prints record_t to stderr */
+void printRecord(const record_t& record);
+
+/**
+ * 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 stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
+
+/**
+ * behead the given line by the cut, return the head and reassign the line to be the rest.
+ */
+std::string behead(std::string* line, const char cut);
+
+/**
+ * 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.
+ * The buffer size is controlled by capacity parameter.
+ */
+class Reader
+{
+public:
+ Reader(const int fd);
+ ~Reader();
+
+ bool readLine(std::string* line);
+ bool ok(std::string* error);
+
+private:
+ FILE* mFile;
+ std::string mStatus;
+};
+
+/**
+ * The Table class is constructed from two arrays generated by the given message with
+ * option (stream_proto.stream_msg).enable_fields_mapping = true.
+ * The names are each field's names in the message and must corresponding to the header/name of
+ * the text to be parsed, and the ids are the streaming proto encoded field ids.
+ *
+ * This class then allows users to insert the table values to proto based on its header.
+ *
+ * Advance feature: if some fields in the message are enums, user must explicitly add the
+ * mapping from enum name string to its enum values.
+ */
+class Message;
+class Table
+{
+friend class Message;
+public:
+ Table(const char* names[], const uint64_t ids[], const int count);
+ ~Table();
+
+ // Add enum names to values for parsing purpose.
+ void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize);
+
+ // Manually add enum names to values mapping, useful when an Enum type is used by
+ // a number of fields, there must not be any enum name conflicts.
+ void addEnumNameToValue(const char* enumName, const int enumValue);
+
+ // Based on given name, find the right field id, parse the text value and insert to proto.
+ // Return false if the given name can't be found.
+ bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
+private:
+ map<std::string, uint64_t> mFields;
+ map<std::string, map<std::string, int>> mEnums;
+ map<std::string, int> mEnumValuesByName;
+};
+
+/**
+ * Reconstructs a typical proto message given its message Table, adds submessage fields explicitly.
+ * It allows user to insert nested proto values purely by the names. See insertField for detail.
+ */
+class Message
+{
+public:
+ Message(Table* table);
+ ~Message();
+
+ // Reconstructs the typical proto message by adding its message fields.
+ void addSubMessage(uint64_t fieldId, Message* fieldMsg);
+
+ // Inserts value if the given name has the corresponding field in its message and return true.
+ // It will recursively search the name in submessages and find the correct field to insert.
+ // For example, when the name is dalvik_vm_heapsize, and the message's corresponding proto is:
+ // message Properties {
+ // message DalvikVm {
+ // int32 heapsize = 1;
+ // bool usejit = 2;
+ // }
+ // DalvikVm dalvik_vm = 1;
+ // string hack_in = 2;
+ // }
+ // The value will be inserted into field heapsize in dalvik_vm submessage.
+ //
+ // Also value belongs to same submessage MUST be inserted contiguously.
+ // For example, dalvik_vm_usejit must be inserted directly after dalvik_vm_heapsize, otherwise
+ // if hack_in attempts to be inserted before dalvik_vm_usejit, value of usejit isn't added as expected.
+ bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
+
+ // Starts a new message field proto session.
+ void startSession(ProtoOutputStream* proto, const string& name);
+
+ // Ends the previous message field proto session.
+ void endSession(ProtoOutputStream* proto);
+private:
+ Table* mTable;
+ std::string mPreviousField;
+ stack<long long> mTokens;
+ map<std::string, Message*> mSubMessages;
+};
+
+#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
new file mode 100644
index 000000000000..418dc3fad761
--- /dev/null
+++ b/cmds/incident_helper/src/main.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "parsers/BatteryTypeParser.h"
+#include "parsers/CpuFreqParser.h"
+#include "parsers/CpuInfoParser.h"
+#include "parsers/EventLogTagsParser.h"
+#include "parsers/KernelWakesParser.h"
+#include "parsers/PageTypeInfoParser.h"
+#include "parsers/ProcrankParser.h"
+#include "parsers/PsParser.h"
+#include "parsers/SystemPropertiesParser.h"
+
+#include <android-base/file.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android::base;
+using namespace std;
+
+static void usage(FILE* out) {
+ fprintf(out, "incident_helper is not designed to run manually,");
+ fprintf(out, "it reads from stdin and writes to stdout, see README.md for details.\n");
+ fprintf(out, "usage: incident_helper -s SECTION\n");
+ fprintf(out, "REQUIRED:\n");
+ fprintf(out, " -s section id, must be positive\n");
+}
+
+//=============================================================================
+static TextParserBase* selectParser(int section) {
+ switch (section) {
+ // IDs smaller than or equal to 0 are reserved for testing
+ case -1:
+ return new TimeoutParser();
+ case 0:
+ return new NoopParser();
+ case 1: // 1 is reserved for incident header so it won't be section id
+ return new ReverseParser();
+/* ========================================================================= */
+ // IDs larger than 1 are section ids reserved in incident.proto
+ case 1000:
+ return new SystemPropertiesParser();
+ case 1100:
+ return new EventLogTagsParser();
+ case 2000:
+ return new ProcrankParser();
+ case 2001:
+ return new PageTypeInfoParser();
+ case 2002:
+ return new KernelWakesParser();
+ case 2003:
+ return new CpuInfoParser();
+ case 2004:
+ return new CpuFreqParser();
+ case 2005:
+ return new PsParser();
+ case 2006:
+ return new BatteryTypeParser();
+ default:
+ return NULL;
+ }
+}
+
+//=============================================================================
+int main(int argc, char** argv) {
+ fprintf(stderr, "Start incident_helper...\n");
+
+ // Parse the args
+ int opt;
+ int sectionID = 0;
+ while ((opt = getopt(argc, argv, "hs:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(stdout);
+ return 0;
+ case 's':
+ sectionID = atoi(optarg);
+ break;
+ }
+ }
+
+ fprintf(stderr, "Pasring section %d...\n", sectionID);
+ TextParserBase* parser = selectParser(sectionID);
+ if (parser != NULL) {
+ fprintf(stderr, "Running parser: %s\n", parser->name.string());
+ status_t err = parser->Parse(STDIN_FILENO, STDOUT_FILENO);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Parse error in section %d: %s\n", sectionID, strerror(-err));
+ return -1;
+ }
+
+ delete parser;
+ }
+ fprintf(stderr, "Finish section %d, exiting...\n", sectionID);
+
+ return 0;
+}
diff --git a/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp b/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
new file mode 100644
index 000000000000..ced6cf807e0d
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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/batterytype.proto.h"
+#include "ih_util.h"
+#include "BatteryTypeParser.h"
+
+using namespace android::os;
+
+status_t
+BatteryTypeParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ bool readLine = false;
+
+ ProtoOutputStream proto;
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ if (readLine) {
+ fprintf(stderr, "Multiple lines in file. Unsure what to do.\n");
+ break;
+ }
+
+ proto.write(BatteryTypeProto::TYPE, line);
+
+ readLine = true;
+ }
+
+ 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/BatteryTypeParser.h b/cmds/incident_helper/src/parsers/BatteryTypeParser.h
new file mode 100644
index 000000000000..ac0c098965d3
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/BatteryTypeParser.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 BATTERY_TYPE_PARSER_H
+#define BATTERY_TYPE_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Battery type parser, parses text in file
+ * /sys/class/power_supply/bms/battery_type.
+ */
+class BatteryTypeParser : public TextParserBase {
+public:
+ BatteryTypeParser() : TextParserBase(String8("BatteryTypeParser")) {};
+ ~BatteryTypeParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // BATTERY_TYPE_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
new file mode 100644
index 000000000000..02f1ce7cc0fc
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <unistd.h>
+
+#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
+#include "ih_util.h"
+#include "CpuFreqParser.h"
+
+using namespace android::os;
+
+status_t
+CpuFreqParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+
+ // parse header
+ reader.readLine(&line);
+ header_t header = parseHeader(line, TAB_DELIMITER);
+ if (header.size() < 1) {
+ fprintf(stderr, "Bad header: %s\n", line.c_str());
+ return BAD_VALUE;
+ }
+ const int numCpus = (int)header.size() - 1;
+ vector<pair<int, long long>> cpucores[numCpus];
+
+ // parse freq and time
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ record_t record = parseRecord(line, TAB_DELIMITER);
+ if (record.size() != header.size()) {
+ fprintf(stderr, "Bad line: %s\n", line.c_str());
+ continue;
+ }
+
+ int freq = toInt(record[0]);
+ for (int i=0; i<numCpus; i++) {
+ if (strcmp(record[i+1].c_str(), "N/A") == 0) {
+ continue;
+ }
+ cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
+ }
+ }
+
+ ProtoOutputStream proto;
+
+ long jiffyHz = sysconf(_SC_CLK_TCK);
+ proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+
+ for (int i=0; i<numCpus; i++) {
+ long long token = proto.start(CpuFreq::CPU_FREQS);
+ proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+ for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
+ long long stateToken = proto.start(CpuFreqStats::TIMES);
+ proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
+ proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+ proto.end(stateToken);
+ }
+ 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/CpuFreqParser.h b/cmds/incident_helper/src/parsers/CpuFreqParser.h
new file mode 100644
index 000000000000..470d56834781
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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_FREQ_PARSER_H
+#define CPU_FREQ_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
+ */
+class CpuFreqParser : public TextParserBase {
+public:
+ CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
+ ~CpuFreqParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // CPU_FREQ_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
new file mode 100644
index 000000000000..d73de54d8c5d
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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;
+ int diff = 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 we need to modify the end of the CMD index.
+ const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", NULL };
+ if (!getColumnIndices(columnIndices, headerNames, line)) {
+ return -1;
+ }
+ // 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);
+ // Add NAME index to complete the column list.
+ columnIndices.push_back(columnIndices.back() + 4);
+ continue;
+ }
+
+ record = parseRecordByColumns(line, columnIndices);
+ diff = record.size() - header.size();
+ if (diff < 0) {
+ fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
+ printRecord(record);
+ continue;
+ } else if (diff > 0) {
+ fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
+ printRecord(record);
+ 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 000000000000..f57bb4e169bd
--- /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/EventLogTagsParser.cpp b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
new file mode 100644
index 000000000000..73e37bd166cd
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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/util/event_log_tags.proto.h"
+#include "ih_util.h"
+#include "EventLogTagsParser.h"
+
+status_t
+EventLogTagsParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+
+ ProtoOutputStream proto;
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+ string debug = line;
+ string tagNumber = behead(&line, ' ');
+ string tagName = behead(&line, ' ');
+ if (tagNumber == "" || tagName == "") {
+ fprintf(stderr, "Bad line, expect at least two parts: %s[%s, %s]\n",
+ debug.c_str(), tagNumber.c_str(), tagName.c_str());
+ continue;
+ }
+
+ long long token = proto.start(EventLogTagMapProto::EVENT_LOG_TAGS);
+ proto.write(EventLogTag::TAG_NUMBER, toInt(tagNumber));
+ proto.write(EventLogTag::TAG_NAME, tagName);
+
+ record_t valueDescriptors = parseRecord(line, PARENTHESES_DELIMITER);
+ for (size_t i = 0; i < valueDescriptors.size(); i++) {
+ record_t valueDescriptor = parseRecord(valueDescriptors[i], PIPE_DELIMITER);
+ if (valueDescriptor.size() != 2 && valueDescriptor.size() != 3) {
+ // If the parts doesn't contains pipe, then skips it.
+ continue;
+ }
+ long long descriptorToken = proto.start(EventLogTag::VALUE_DESCRIPTORS);
+ proto.write(EventLogTag::ValueDescriptor::NAME, valueDescriptor[0]);
+ proto.write(EventLogTag::ValueDescriptor::TYPE, toInt(valueDescriptor[1]));
+ if (valueDescriptor.size() == 3) {
+ char c = valueDescriptor[2][0];
+ int unit = 0;
+ if (c < '0' || c > '9') {
+ unit = (int) c;
+ } else {
+ unit = toInt(valueDescriptor[2]);
+ }
+ proto.write(EventLogTag::ValueDescriptor::UNIT, unit);
+ }
+ proto.end(descriptorToken);
+ }
+ 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/EventLogTagsParser.h b/cmds/incident_helper/src/parsers/EventLogTagsParser.h
new file mode 100644
index 000000000000..79057ce0b3ca
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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 EVENT_LOG_TAGS_PARSER_H
+#define EVENT_LOG_TAGS_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * event.logtags parser, parse file in /system/etc/event-log-tags
+ */
+class EventLogTagsParser : public TextParserBase {
+public:
+ EventLogTagsParser() : TextParserBase(String8("EventLogTagsParser")) {};
+ ~EventLogTagsParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // EVENT_LOG_TAGS_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
new file mode 100644
index 000000000000..cae51abbe57f
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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/kernelwake.proto.h"
+#include "ih_util.h"
+#include "KernelWakesParser.h"
+
+using namespace android::os;
+
+status_t
+KernelWakesParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ record_t record; // retain each record
+ int nline = 0;
+
+ ProtoOutputStream proto;
+ Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+ // parse head line
+ if (nline++ == 0) {
+ header = parseHeader(line, TAB_DELIMITER);
+ continue;
+ }
+
+ // parse for each record, the line delimiter is \t only!
+ record = parseRecord(line, TAB_DELIMITER);
+
+ if (record.size() < header.size()) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
+ } else if (record.size() > header.size()) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has extra fields\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
+ }
+
+ 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])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %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/KernelWakesParser.h b/cmds/incident_helper/src/parsers/KernelWakesParser.h
new file mode 100644
index 000000000000..aabab7c64a4f
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.h
@@ -0,0 +1,33 @@
+/*
+ * 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 KERNEL_WAKES_PARSER_H
+#define KERNEL_WAKES_PARSER_H
+
+#include "TextParserBase.h"
+
+/**
+ * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources
+ */
+class KernelWakesParser : public TextParserBase {
+public:
+ KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {};
+ ~KernelWakesParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // KERNEL_WAKES_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
new file mode 100644
index 000000000000..f1b93ff9ec41
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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/pagetypeinfo.proto.h"
+#include "ih_util.h"
+#include "PageTypeInfoParser.h"
+
+using namespace android::os;
+
+status_t
+PageTypeInfoParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ bool migrateTypeSession = false;
+ int pageBlockOrder;
+ header_t blockHeader;
+
+ ProtoOutputStream proto;
+ Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT);
+
+ while (reader.readLine(&line)) {
+ if (line.empty()) {
+ migrateTypeSession = false;
+ blockHeader.clear();
+ continue;
+ }
+
+ if (stripPrefix(&line, "Page block order:")) {
+ pageBlockOrder = toInt(line);
+ proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
+ continue;
+ }
+ if (stripPrefix(&line, "Pages per block:")) {
+ proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
+ continue;
+ }
+ if (stripPrefix(&line, "Free pages count per migrate type at order")) {
+ migrateTypeSession = true;
+ continue;
+ }
+ if (stripPrefix(&line, "Number of blocks type")) {
+ blockHeader = parseHeader(line);
+ continue;
+ }
+
+ 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 (stripPrefix(&record[0], "Node")) {
+ proto.write(MigrateTypeProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+ // expect part 1 starts with "zone"
+ if (stripPrefix(&record[1], "zone")) {
+ proto.write(MigrateTypeProto::ZONE, record[1]);
+ } else return BAD_VALUE;
+ // expect part 2 starts with "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
+ // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
+ // The pageBlockOrder = 10 and it's zero-indexed. so total parts
+ // are 10 + 1(zero-indexed) + 1(the type part) = 12.
+ record_t pageCounts = parseRecord(record[2]);
+ int pageCountsSize = pageBlockOrder + 2;
+ if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
+
+ proto.write(MigrateTypeProto::TYPE, pageCounts[0]);
+ for (auto i=1; i<pageCountsSize; i++) {
+ proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i]));
+ }
+ } else return BAD_VALUE;
+
+ proto.end(token);
+ } else if (!blockHeader.empty() && record.size() == 2) {
+ long long token = proto.start(PageTypeInfo::BLOCKS);
+ if (stripPrefix(&record[0], "Node")) {
+ proto.write(BlockProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+
+ 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])) {
+ return BAD_VALUE;
+ }
+ }
+ } else return BAD_VALUE;
+ 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;
+} \ No newline at end of file
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.h b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h
new file mode 100644
index 000000000000..fb84d912a5f2
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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 PAGE_TYPE_INFO_PARSER_H
+#define PAGE_TYPE_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype
+ */
+class PageTypeInfoParser : public TextParserBase {
+public:
+ PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {};
+ ~PageTypeInfoParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PAGE_TYPE_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
new file mode 100644
index 000000000000..a4eb0fdfd988
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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/procrank.proto.h"
+#include "ih_util.h"
+#include "ProcrankParser.h"
+
+using namespace android::os;
+
+status_t
+ProcrankParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ record_t record; // retain each record
+ int nline = 0;
+
+ ProtoOutputStream proto;
+ Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT);
+ string zram, ram, total;
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ // parse head line
+ if (nline++ == 0) {
+ header = parseHeader(line);
+ continue;
+ }
+
+ if (stripPrefix(&line, "ZRAM:")) {
+ zram = line;
+ continue;
+ }
+ if (stripPrefix(&line, "RAM:")) {
+ ram = line;
+ continue;
+ }
+
+ record = parseRecord(line);
+ if (record.size() != header.size()) {
+ if (record[record.size() - 1] == "TOTAL") { // TOTAL record
+ total = line;
+ } else {
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
+ line.c_str());
+ }
+ continue;
+ }
+
+ long long token = proto.start(Procrank::PROCESSES);
+ for (int i=0; i<(int)record.size(); 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());
+ }
+ }
+ proto.end(token);
+ }
+
+ // add summary
+ long long token = proto.start(Procrank::SUMMARY);
+ if (!total.empty()) {
+ 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());
+ }
+ proto.end(token);
+ }
+ if (!zram.empty()) {
+ long long token = proto.start(SummaryProto::ZRAM);
+ proto.write(ZramProto::RAW_TEXT, zram);
+ proto.end(token);
+ }
+ if (!ram.empty()) {
+ long long token = proto.start(SummaryProto::RAM);
+ proto.write(RamProto::RAW_TEXT, ram);
+ proto.end(token);
+ }
+ 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/ProcrankParser.h b/cmds/incident_helper/src/parsers/ProcrankParser.h
new file mode 100644
index 000000000000..5d0ee48aa5b1
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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 PROCRANK_PARSER_H
+#define PROCRANK_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Procrank parser, parses text produced by command procrank
+ */
+class ProcrankParser : public TextParserBase {
+public:
+ ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
+ ~ProcrankParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PROCRANK_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp
new file mode 100644
index 000000000000..e9014cacfa0b
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PsParser.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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/ps.proto.h"
+#include "ih_util.h"
+#include "PsParser.h"
+
+using namespace android::os;
+
+status_t PsParser::Parse(const int in, const int out) const {
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
+ record_t record; // retain each record
+ int nline = 0;
+ int diff = 0;
+
+ ProtoOutputStream proto;
+ Table table(PsDumpProto::Process::_FIELD_NAMES, PsDumpProto::Process::_FIELD_IDS, PsDumpProto::Process::_FIELD_COUNT);
+ const char* pcyNames[] = { "fg", "bg", "ta" };
+ const int pcyValues[] = {PsDumpProto::Process::POLICY_FG, PsDumpProto::Process::POLICY_BG, PsDumpProto::Process::POLICY_TA};
+ table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3);
+ const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" };
+ const int sValues[] = {PsDumpProto::Process::STATE_D, PsDumpProto::Process::STATE_R, PsDumpProto::Process::STATE_S, PsDumpProto::Process::STATE_T, PsDumpProto::Process::STATE_TRACING, PsDumpProto::Process::STATE_X, PsDumpProto::Process::STATE_Z};
+ table.addEnumTypeMap("s", sNames, sValues, 7);
+
+ // Parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ if (nline++ == 0) {
+ header = parseHeader(line, DEFAULT_WHITESPACE);
+
+ const char* headerNames[] = { "LABEL", "USER", "PID", "TID", "PPID", "VSZ", "RSS", "WCHAN", "ADDR", "S", "PRI", "NI", "RTPRIO", "SCH", "PCY", "TIME", "CMD", NULL };
+ if (!getColumnIndices(columnIndices, headerNames, line)) {
+ return -1;
+ }
+
+ continue;
+ }
+
+ record = parseRecordByColumns(line, columnIndices);
+
+ diff = record.size() - header.size();
+ if (diff < 0) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
+ printRecord(record);
+ continue;
+ } else if (diff > 0) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
+ printRecord(record);
+ continue;
+ }
+
+ long long token = proto.start(PsDumpProto::PROCESSES);
+ for (int i=0; i<(int)record.size(); 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());
+ }
+ }
+ 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/PsParser.h b/cmds/incident_helper/src/parsers/PsParser.h
new file mode 100644
index 000000000000..9488e40e88fe
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PsParser.h
@@ -0,0 +1,33 @@
+/*
+ * 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 PS_PARSER_H
+#define PS_PARSER_H
+
+#include "TextParserBase.h"
+
+/**
+ * PS parser, parses output of 'ps' command to protobuf.
+ */
+class PsParser : public TextParserBase {
+public:
+ PsParser() : TextParserBase(String8("Ps")) {};
+ ~PsParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PS_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
new file mode 100644
index 000000000000..7b0ac0b8452b
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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/system_properties.proto.h"
+#include "ih_util.h"
+#include "SystemPropertiesParser.h"
+
+using namespace android::os;
+
+const string LINE_DELIMITER = "]: [";
+
+// system properties' names sometimes are not valid proto field names, make the names valid.
+static string convertToFieldName(const string& name) {
+ int len = (int)name.length();
+ char cstr[len + 1];
+ strcpy(cstr, name.c_str());
+ for (int i = 0; i < len; i++) {
+ if (!isValidChar(cstr[i])) {
+ cstr[i] = '_';
+ }
+ }
+ return string(cstr);
+}
+
+status_t
+SystemPropertiesParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ string name; // the name of the property
+ string value; // the string value of the property
+ ProtoOutputStream proto;
+ vector<pair<string, string>> extras;
+
+ Table sysPropTable(SystemPropertiesProto::_FIELD_NAMES,
+ SystemPropertiesProto::_FIELD_IDS,
+ SystemPropertiesProto::_FIELD_COUNT);
+ Message sysProp(&sysPropTable);
+
+ Table aacDrcTable(SystemPropertiesProto::AacDrc::_FIELD_NAMES,
+ SystemPropertiesProto::AacDrc::_FIELD_IDS,
+ SystemPropertiesProto::AacDrc::_FIELD_COUNT);
+ Message aacDrc(&aacDrcTable);
+ sysProp.addSubMessage(SystemPropertiesProto::AAC_DRC, &aacDrc);
+
+ Table aaudioTable(SystemPropertiesProto::Aaudio::_FIELD_NAMES,
+ SystemPropertiesProto::Aaudio::_FIELD_IDS,
+ SystemPropertiesProto::Aaudio::_FIELD_COUNT);
+ Message aaudio(&aaudioTable);
+ sysProp.addSubMessage(SystemPropertiesProto::AAUDIO, &aaudio);
+
+ Table cameraTable(SystemPropertiesProto::Camera::_FIELD_NAMES,
+ SystemPropertiesProto::Camera::_FIELD_IDS,
+ SystemPropertiesProto::Camera::_FIELD_COUNT);
+ Message camera(&cameraTable);
+ sysProp.addSubMessage(SystemPropertiesProto::CAMERA, &camera);
+
+ Table dalvikVmTable(SystemPropertiesProto::DalvikVm::_FIELD_NAMES,
+ SystemPropertiesProto::DalvikVm::_FIELD_IDS,
+ SystemPropertiesProto::DalvikVm::_FIELD_COUNT);
+ Message dalvikVm(&dalvikVmTable);
+ sysProp.addSubMessage(SystemPropertiesProto::DALVIK_VM, &dalvikVm);
+
+ Table initSvcTable(SystemPropertiesProto::InitSvc::_FIELD_NAMES,
+ SystemPropertiesProto::InitSvc::_FIELD_IDS,
+ SystemPropertiesProto::InitSvc::_FIELD_COUNT);
+ initSvcTable.addEnumNameToValue("running", SystemPropertiesProto::InitSvc::STATUS_RUNNING);
+ initSvcTable.addEnumNameToValue("stopped", SystemPropertiesProto::InitSvc::STATUS_STOPPED);
+ Message initSvc(&initSvcTable);
+ sysProp.addSubMessage(SystemPropertiesProto::INIT_SVC, &initSvc);
+
+ Table logTable(SystemPropertiesProto::Log::_FIELD_NAMES,
+ SystemPropertiesProto::Log::_FIELD_IDS,
+ SystemPropertiesProto::Log::_FIELD_COUNT);
+ Message logMsg(&logTable);
+ sysProp.addSubMessage(SystemPropertiesProto::LOG, &logMsg);
+
+ Table persistTable(SystemPropertiesProto::Persist::_FIELD_NAMES,
+ SystemPropertiesProto::Persist::_FIELD_IDS,
+ SystemPropertiesProto::Persist::_FIELD_COUNT);
+ Message persist(&persistTable);
+ sysProp.addSubMessage(SystemPropertiesProto::PERSIST, &persist);
+
+ Table pmDexoptTable(SystemPropertiesProto::PmDexopt::_FIELD_NAMES,
+ SystemPropertiesProto::PmDexopt::_FIELD_IDS,
+ SystemPropertiesProto::PmDexopt::_FIELD_COUNT);
+ Message pmDexopt(&pmDexoptTable);
+ sysProp.addSubMessage(SystemPropertiesProto::PM_DEXOPT, &pmDexopt);
+
+ Table roTable(SystemPropertiesProto::Ro::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::_FIELD_IDS,
+ SystemPropertiesProto::Ro::_FIELD_COUNT);
+ Message ro(&roTable);
+
+ Table bootTable(SystemPropertiesProto::Ro::Boot::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Boot::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Boot::_FIELD_COUNT);
+ Message boot(&bootTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::BOOT, &boot);
+
+ Table bootimageTable(SystemPropertiesProto::Ro::BootImage::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::BootImage::_FIELD_IDS,
+ SystemPropertiesProto::Ro::BootImage::_FIELD_COUNT);
+ Message bootimage(&bootimageTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::BOOTIMAGE, &bootimage);
+
+ Table buildTable(SystemPropertiesProto::Ro::Build::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Build::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Build::_FIELD_COUNT);
+ Message build(&buildTable);
+
+ Table versionTable(SystemPropertiesProto::Ro::Build::Version::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Build::Version::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Build::Version::_FIELD_COUNT);
+ Message version(&versionTable);
+ build.addSubMessage(SystemPropertiesProto::Ro::Build::VERSION, &version);
+ ro.addSubMessage(SystemPropertiesProto::Ro::BUILD, &build);
+
+ Table configTable(SystemPropertiesProto::Ro::Config::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Config::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Config::_FIELD_COUNT);
+ Message config(&configTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::CONFIG, &config);
+
+ Table hardwareTable(SystemPropertiesProto::Ro::Hardware::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Hardware::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Hardware::_FIELD_COUNT);
+ Message hardware(&hardwareTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::HARDWARE, &hardware);
+
+ Table productTable(SystemPropertiesProto::Ro::Product::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Product::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Product::_FIELD_COUNT);
+ Message product(&productTable);
+
+ Table pVendorTable(SystemPropertiesProto::Ro::Product::Vendor::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Product::Vendor::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Product::Vendor::_FIELD_COUNT);
+ Message pVendor(&pVendorTable);
+ product.addSubMessage(SystemPropertiesProto::Ro::Product::VENDOR, &pVendor);
+ ro.addSubMessage(SystemPropertiesProto::Ro::PRODUCT, &product);
+
+ Table telephonyTable(SystemPropertiesProto::Ro::Telephony::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Telephony::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Telephony::_FIELD_COUNT);
+ Message telephony(&telephonyTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::TELEPHONY, &telephony);
+
+ Table vendorTable(SystemPropertiesProto::Ro::Vendor::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Vendor::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Vendor::_FIELD_COUNT);
+ Message vendor(&vendorTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::VENDOR, &vendor);
+
+ sysProp.addSubMessage(SystemPropertiesProto::RO, &ro);
+
+ Table sysTable(SystemPropertiesProto::Sys::_FIELD_NAMES,
+ SystemPropertiesProto::Sys::_FIELD_IDS,
+ SystemPropertiesProto::Sys::_FIELD_COUNT);
+ Message sys(&sysTable);
+
+ Table usbTable(SystemPropertiesProto::Sys::Usb::_FIELD_NAMES,
+ SystemPropertiesProto::Sys::Usb::_FIELD_IDS,
+ SystemPropertiesProto::Sys::Usb::_FIELD_COUNT);
+ Message usb(&usbTable);
+ sys.addSubMessage(SystemPropertiesProto::Sys::USB, &usb);
+
+ sysProp.addSubMessage(SystemPropertiesProto::SYS, &sys);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ line = line.substr(1, line.size() - 2); // trim []
+ size_t index = line.find(LINE_DELIMITER); // split by "]: ["
+ if (index == string::npos) {
+ fprintf(stderr, "Bad Line %s\n", line.c_str());
+ continue;
+ }
+ name = line.substr(0, index);
+ value = trim(line.substr(index + 4), DEFAULT_WHITESPACE);
+ if (value.empty()) continue;
+
+ // if the property name couldn't be found in proto definition or the value has mistype,
+ // add to extra properties with its name and value
+ if (!sysProp.insertField(&proto, convertToFieldName(name), value)) {
+ extras.push_back(make_pair(name, value));
+ }
+ }
+ // end session for the last write.
+ sysProp.endSession(&proto);
+
+ for (auto it = extras.begin(); it != extras.end(); it++) {
+ long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES);
+ proto.write(SystemPropertiesProto::Property::NAME, it->first);
+ proto.write(SystemPropertiesProto::Property::VALUE, it->second);
+ 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/SystemPropertiesParser.h b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h
new file mode 100644
index 000000000000..c4016006a48a
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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 SYSTEM_PROPERTIES_PARSER_H
+#define SYSTEM_PROPERTIES_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * SystemProperties parser, parses text produced by command getprop.
+ */
+class SystemPropertiesParser : public TextParserBase {
+public:
+ SystemPropertiesParser() : TextParserBase(String8("SystemPropertiesParser")) {};
+ ~SystemPropertiesParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // SYSTEM_PROPERTIES_PARSER_H