Refactor incident_helper to use protoutil and cppstream plugin.
1. Split the parsers to its own file to prevent all the parsers in one
gaint file.
2. Completely get rid of protobuf-cpp-full in incident_helper, use
ProtoOutputStream and cppstream instead, the incident_helper binary is
reduced from ~500K to ~113K.
3. Write data to protobuf even its values are zero/default, the reason
is for example we have a repeated int32 orders = 1; and people
explicitly append 0 so the total repeated field has 10 values, if zero
is not written to serialized data, this repeated field will only have 9
values which is not what we want at first place. This also aligns with
the default protobuf serialization behavior in incident_helper_test.
4. Use Android.bp for protoutil lib since it is not able to depend on
libs compiled by .mk file, it works the other way.
5. Add a new custom message option for streaming_proto, if specified,
the cppstream will create extra metadata to get field ids by field name.
A Table class is created in incident_helper to use it.
Bug: 67860303
Test: unit tested as well as on device test
Change-Id: I8e136fd15f343a4a623d20910ec64b622b478a3e
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 0532083..2ef0371 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -8,44 +8,53 @@
"-O0"
],
- srcs: [
- "IncidentHelper.cpp",
- "ih_util.cpp",
+ local_include_dirs: [
+ "src/",
+ "src/parsers/",
],
+ srcs: [
+ "src/parsers/*.cpp",
+ "src/TextParserBase.cpp",
+ "src/ih_util.cpp",
+ ],
+
+ generated_headers: ["gen-platform-proto-constants"],
+
shared_libs: [
"libbase",
"liblog",
- "libprotobuf-cpp-full",
+ "libprotoutil",
"libutils",
],
-
- static_libs: [
- "libplatformprotos",
- ],
}
cc_binary {
name: "incident_helper",
defaults: ["incident_helper_defaults"],
- srcs: ["main.cpp"],
+ srcs: ["src/main.cpp"],
}
cc_test {
name: "incident_helper_test",
defaults: ["incident_helper_defaults"],
+ local_include_dirs: ["src/"],
srcs: [
- "tests/IncidentHelper_test.cpp",
- "tests/ih_util_test.cpp",
+ "tests/*.cpp",
],
data: [
"testdata/*",
],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ ],
+
static_libs: [
"libgmock",
+ "libplatformprotos"
],
-}
\ No newline at end of file
+}
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
deleted file mode 100644
index 7b06d42..0000000
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * 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 "IncidentHelper.h"
-#include "ih_util.h"
-
-#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
-#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
-#include "frameworks/base/core/proto/android/os/procrank.pb.h"
-
-#include <android-base/file.h>
-#include <unistd.h>
-#include <string>
-#include <vector>
-
-using namespace android::base;
-using namespace android::os;
-using namespace google::protobuf;
-using namespace std;
-
-
-static const string TAB_DELIMITER = "\t";
-static const string COMMA_DELIMITER = ",";
-
-static inline int toInt(const string& s) {
- return atoi(s.c_str());
-}
-
-static inline long toLong(const string& s) {
- return atol(s.c_str());
-}
-
-/**
- * Sets the given protobuf message when the field name matches one of the
- * fields. It is useful to set values to proto from table-like plain texts.
- */
-static bool
-SetTableField(::google::protobuf::Message* message, string field_name, string field_value) {
- const Descriptor* descriptor = message->GetDescriptor();
- const Reflection* reflection = message->GetReflection();
-
- const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
- switch (field->type()) {
- case FieldDescriptor::TYPE_STRING:
- reflection->SetString(message, field, field_value);
- return true;
- case FieldDescriptor::TYPE_INT64:
- reflection->SetInt64(message, field, toLong(field_value));
- return true;
- case FieldDescriptor::TYPE_UINT64:
- reflection->SetUInt64(message, field, toLong(field_value));
- return true;
- case FieldDescriptor::TYPE_INT32:
- reflection->SetInt32(message, field, toInt(field_value));
- return true;
- case FieldDescriptor::TYPE_UINT32:
- reflection->SetUInt32(message, field, toInt(field_value));
- return true;
- default:
- // Add new scalar types
- return false;
- }
-}
-
-// ================================================================================
-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;
-}
-
-// ================================================================================
-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;
-
- KernelWakeSources proto;
-
- // 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;
- }
-
- WakeupSourceProto* source = proto.add_wakeup_sources();
- for (int i=0; i<(int)record.size(); i++) {
- if (!SetTableField(source, 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());
- }
- }
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!proto.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
- return NO_ERROR;
-}
-
-// ================================================================================
-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;
-
- Procrank proto;
-
- // parse line by line
- while (reader.readLine(&line)) {
- if (line.empty()) continue;
-
- // parse head line
- if (nline++ == 0) {
- header = parseHeader(line);
- continue;
- }
-
- if (hasPrefix(&line, "ZRAM:")) {
- proto.mutable_summary()->mutable_zram()->set_raw_text(line);
- continue;
- }
- if (hasPrefix(&line, "RAM:")) {
- proto.mutable_summary()->mutable_ram()->set_raw_text(line);
- continue;
- }
-
- record = parseRecord(line);
- if (record.size() != header.size()) {
- if (record[record.size() - 1] == "TOTAL") { // TOTAL record
- ProcessProto* total = proto.mutable_summary()->mutable_total();
- for (int i=1; i<=(int)record.size(); i++) {
- SetTableField(total, header[header.size() - i], record[record.size() - i]);
- }
- } else {
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
- line.c_str());
- }
- continue;
- }
-
- ProcessProto* process = proto.add_processes();
- for (int i=0; i<(int)record.size(); i++) {
- if (!SetTableField(process, 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());
- }
- }
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!proto.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t PageTypeInfoParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- bool migrateTypeSession = false;
- int pageBlockOrder;
- header_t blockHeader;
-
- PageTypeInfo pageTypeInfo;
-
- while (reader.readLine(&line)) {
- if (line.empty()) {
- migrateTypeSession = false;
- blockHeader.clear();
- continue;
- }
-
- if (hasPrefix(&line, "Page block order:")) {
- pageBlockOrder = toInt(line);
- pageTypeInfo.set_page_block_order(pageBlockOrder);
- continue;
- }
- if (hasPrefix(&line, "Pages per block:")) {
- pageTypeInfo.set_pages_per_block(toInt(line));
- continue;
- }
- if (hasPrefix(&line, "Free pages count per migrate type at order")) {
- migrateTypeSession = true;
- continue;
- }
- if (hasPrefix(&line, "Number of blocks type")) {
- blockHeader = parseHeader(line);
- continue;
- }
-
- record_t record = parseRecord(line, COMMA_DELIMITER);
- if (migrateTypeSession && record.size() == 3) {
- MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types();
- // expect part 0 starts with "Node"
- if (hasPrefix(&record[0], "Node")) {
- migrateType->set_node(toInt(record[0]));
- } else goto ERROR;
- // expect part 1 starts with "zone"
- if (hasPrefix(&record[1], "zone")) {
- migrateType->set_zone(record[1]);
- } else goto ERROR;
- // expect part 2 starts with "type"
- if (hasPrefix(&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) goto ERROR;
-
- migrateType->set_type(pageCounts[0]);
- for (auto i=1; i<pageCountsSize; i++) {
- migrateType->add_free_pages_count(toInt(pageCounts[i]));
- }
- } else goto ERROR;
- continue;
- }
-
- if (!blockHeader.empty() && record.size() == 2) {
- BlockProto* block = pageTypeInfo.add_blocks();
-
- if (hasPrefix(&record[0], "Node")) {
- block->set_node(toInt(record[0]));
- } else goto ERROR;
-
- if (hasPrefix(&record[1], "zone")) {
- record_t blockCounts = parseRecord(record[1]);
- block->set_zone(blockCounts[0]);
- for (size_t i=0; i<blockHeader.size(); i++) {
- if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR;
- }
- } else goto ERROR;
-
- continue;
- }
-
-ERROR: // print out error for this single line and continue parsing
- fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str());
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!pageTypeInfo.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
-
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize());
- return NO_ERROR;
-}
diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp
new file mode 100644
index 0000000..a8f9968
--- /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/IncidentHelper.h b/cmds/incident_helper/src/TextParserBase.h
similarity index 64%
rename from cmds/incident_helper/IncidentHelper.h
rename to cmds/incident_helper/src/TextParserBase.h
index d24d717..c41612d 100644
--- a/cmds/incident_helper/IncidentHelper.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef INCIDENT_HELPER_H
-#define INCIDENT_HELPER_H
+#ifndef TEXT_PARSER_BASE_H
+#define TEXT_PARSER_BASE_H
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -68,37 +68,4 @@
virtual status_t Parse(const int in, const int out) const;
};
-/**
- * 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;
-};
-
-/**
- * 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;
-};
-
-/**
- * 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 // INCIDENT_HELPER_H
+#endif // TEXT_PARSER_BASE_H
\ No newline at end of file
diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
similarity index 73%
rename from cmds/incident_helper/ih_util.cpp
rename to cmds/incident_helper/src/ih_util.cpp
index 2ab4b54..c7d1ca2 100644
--- a/cmds/incident_helper/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -87,6 +87,15 @@
return true;
}
+int toInt(const std::string& s) {
+ return atoi(s.c_str());
+}
+
+long long toLongLong(const std::string& s) {
+ return atoll(s.c_str());
+}
+
+// ==============================================================================
Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
Reader::Reader(const int fd, const size_t capacity)
@@ -151,3 +160,57 @@
error->assign(mStatus);
return mStatus.empty();
}
+
+// ==============================================================================
+Table::Table(const char* names[], const uint64_t ids[], const int count)
+ :mFieldNames(names),
+ mFieldIds(ids),
+ mFieldCount(count)
+{
+}
+
+Table::~Table()
+{
+}
+
+bool
+Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+{
+ uint64_t found = 0;
+ for (int i=0; i<mFieldCount; i++) {
+ if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
+ found = mFieldIds[i];
+ break;
+ }
+ }
+
+ switch (found & FIELD_TYPE_MASK) {
+ case FIELD_TYPE_DOUBLE:
+ case FIELD_TYPE_FLOAT:
+ // TODO: support parse string to float/double
+ return false;
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_BYTES:
+ 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));
+ break;
+ case FIELD_TYPE_BOOL:
+ case FIELD_TYPE_ENUM:
+ 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));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/src/ih_util.h
similarity index 79%
rename from cmds/incident_helper/ih_util.h
rename to cmds/incident_helper/src/ih_util.h
index ce5baee..86761e9 100644
--- a/cmds/incident_helper/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -21,6 +21,10 @@
#include <vector>
#include <sstream>
+#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&);
@@ -52,6 +56,12 @@
bool hasPrefix(std::string* line, const char* key);
/**
+ * Converts string to the desired type
+ */
+int toInt(const std::string& s);
+long long toLongLong(const std::string& s);
+
+/**
* Reader class reads data from given fd in streaming fashion.
* The buffer size is controlled by capacity parameter.
*/
@@ -78,4 +88,22 @@
inline bool EOR() { return mFd == -1 && mBufSize == 0; };
};
+/**
+ * The class contains a mapping between table headers to its field ids.
+ * And allow users to insert the field values to proto based on its header name.
+ */
+class Table
+{
+public:
+ Table(const char* names[], const uint64_t ids[], const int count);
+ ~Table();
+
+ bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+
+private:
+ const char** mFieldNames;
+ const uint64_t* mFieldIds;
+ const int mFieldCount;
+};
+
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/src/main.cpp
similarity index 96%
rename from cmds/incident_helper/main.cpp
rename to cmds/incident_helper/src/main.cpp
index 52ff777..3da87b9c 100644
--- a/cmds/incident_helper/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,7 +16,9 @@
#define LOG_TAG "incident_helper"
-#include "IncidentHelper.h"
+#include "parsers/KernelWakesParser.h"
+#include "parsers/PageTypeInfoParser.h"
+#include "parsers/ProcrankParser.h"
#include <android-base/file.h>
#include <getopt.h>
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
new file mode 100644
index 0000000..cc4a1e1
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+const std::string LINE_DELIMITER = "\t";
+
+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, LINE_DELIMITER);
+ continue;
+ }
+
+ // parse for each record, the line delimiter is \t only!
+ record = parseRecord(line, LINE_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;
+ }
+
+ 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 0000000..aabab7c
--- /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 0000000..6047bd1
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+const std::string LINE_DELIMITER = ",";
+
+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 (hasPrefix(&line, "Page block order:")) {
+ pageBlockOrder = toInt(line);
+ proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
+ continue;
+ }
+ if (hasPrefix(&line, "Pages per block:")) {
+ proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
+ continue;
+ }
+ if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+ migrateTypeSession = true;
+ continue;
+ }
+ if (hasPrefix(&line, "Number of blocks type")) {
+ blockHeader = parseHeader(line);
+ continue;
+ }
+
+ record_t record = parseRecord(line, LINE_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")) {
+ proto.write(MigrateTypeProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+ // expect part 1 starts with "zone"
+ if (hasPrefix(&record[1], "zone")) {
+ proto.write(MigrateTypeProto::ZONE, record[1]);
+ } else return BAD_VALUE;
+ // expect part 2 starts with "type"
+ if (hasPrefix(&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 (hasPrefix(&record[0], "Node")) {
+ proto.write(BlockProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+
+ if (hasPrefix(&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 0000000..fb84d91
--- /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 0000000..93f970f
--- /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 (hasPrefix(&line, "ZRAM:")) {
+ zram = line;
+ continue;
+ }
+ if (hasPrefix(&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 0000000..5d0ee48
--- /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/testdata/kernel_wakeups_short.txt b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
new file mode 100644
index 0000000..a51926e
--- /dev/null
+++ b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
@@ -0,0 +1,3 @@
+name active_count last_change
+ab 8 123456123456
+df 143 0
diff --git a/cmds/incident_helper/tests/IncidentHelper_test.cpp b/cmds/incident_helper/tests/IncidentHelper_test.cpp
deleted file mode 100644
index c44a163..0000000
--- a/cmds/incident_helper/tests/IncidentHelper_test.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * 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 "IncidentHelper.h"
-
-#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
-#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
-#include "frameworks/base/core/proto/android/os/procrank.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 IncidentHelperTest : 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(IncidentHelperTest, ReverseParser) {
- ReverseParser parser;
- TemporaryFile tf;
-
- ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile("TestData", tf.path, false));
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(tf.fd, STDOUT_FILENO));
- EXPECT_THAT(GetCapturedStdout(), StrEq("ataDtseT"));
-}
-
-TEST_F(IncidentHelperTest, KernelWakesParser) {
- const string testFile = kTestDataPath + "kernel_wakeups.txt";
- KernelWakesParser parser;
- KernelWakeSources expected;
-
- WakeupSourceProto* record1 = expected.add_wakeup_sources();
- record1->set_name("ipc000000ab_ATFWD-daemon");
- record1->set_active_count(8);
- record1->set_event_count(8);
- record1->set_wakeup_count(0);
- record1->set_expire_count(0);
- record1->set_active_since(0l);
- record1->set_total_time(0l);
- record1->set_max_time(0l);
- record1->set_last_change(131348l);
- record1->set_prevent_suspend_time(0l);
-
- WakeupSourceProto* record2 = expected.add_wakeup_sources();
- record2->set_name("ipc000000aa_ATFWD-daemon");
- record2->set_active_count(143);
- record2->set_event_count(143);
- record2->set_wakeup_count(0);
- record2->set_expire_count(0);
- record2->set_active_since(0l);
- record2->set_total_time(123l);
- record2->set_max_time(3l);
- record2->set_last_change(2067286206l);
- record2->set_prevent_suspend_time(0l);
-
- 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);
-}
-
-TEST_F(IncidentHelperTest, ProcrankParser) {
- const string testFile = kTestDataPath + "procrank.txt";
- ProcrankParser parser;
- Procrank expected;
-
- ProcessProto* process1 = expected.add_processes();
- process1->set_pid(1119);
- process1->set_vss(2607640);
- process1->set_rss(339564);
- process1->set_pss(180278);
- process1->set_uss(114216);
- process1->set_swap(1584);
- process1->set_pswap(46);
- process1->set_uswap(0);
- process1->set_zswap(10);
- process1->set_cmdline("system_server");
-
- ProcessProto* process2 = expected.add_processes();
- process2->set_pid(649);
- process2->set_vss(11016);
- process2->set_rss(1448);
- process2->set_pss(98);
- process2->set_uss(48);
- process2->set_swap(472);
- process2->set_pswap(342);
- process2->set_uswap(212);
- process2->set_zswap(75);
- process2->set_cmdline("/vendor/bin/qseecomd");
-
- ProcessProto* total = expected.mutable_summary()->mutable_total();
- total->set_pss(1201993);
- total->set_uss(935300);
- total->set_swap(88164);
- total->set_pswap(31069);
- total->set_uswap(27612);
- total->set_zswap(6826);
- total->set_cmdline("TOTAL");
-
- expected.mutable_summary()->mutable_zram()
- ->set_raw_text("6828K physical used for 31076K in swap (524284K total swap)");
- expected.mutable_summary()->mutable_ram()
- ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
-
- 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);
-}
-
-TEST_F(IncidentHelperTest, ProcrankParserShortHeader) {
- const string testFile = kTestDataPath + "procrank_short.txt";
- ProcrankParser parser;
- Procrank expected;
-
- ProcessProto* process1 = expected.add_processes();
- process1->set_pid(1119);
- process1->set_vss(2607640);
- process1->set_rss(339564);
- process1->set_pss(180278);
- process1->set_uss(114216);
- process1->set_cmdline("system_server");
-
- ProcessProto* process2 = expected.add_processes();
- process2->set_pid(649);
- process2->set_vss(11016);
- process2->set_rss(1448);
- process2->set_pss(98);
- process2->set_uss(48);
- process2->set_cmdline("/vendor/bin/qseecomd");
-
- ProcessProto* total = expected.mutable_summary()->mutable_total();
- total->set_pss(1201993);
- total->set_uss(935300);
- total->set_cmdline("TOTAL");
-
- expected.mutable_summary()->mutable_ram()
- ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
-
- 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);
-}
-
-TEST_F(IncidentHelperTest, PageTypeInfoParser) {
- const string testFile = kTestDataPath + "pagetypeinfo.txt";
- PageTypeInfoParser parser;
- PageTypeInfo expected;
-
- expected.set_page_block_order(10);
- expected.set_pages_per_block(1024);
-
- MigrateTypeProto* mt1 = expected.add_migrate_types();
- mt1->set_node(0);
- mt1->set_zone("DMA");
- mt1->set_type("Unmovable");
- int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0};
- for (auto i=0; i<11; i++) {
- mt1->add_free_pages_count(arr1[i]);
- }
-
- MigrateTypeProto* mt2 = expected.add_migrate_types();
- mt2->set_node(0);
- mt2->set_zone("Normal");
- mt2->set_type("Reclaimable");
- int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0};
- for (auto i=0; i<11; i++) {
- mt2->add_free_pages_count(arr2[i]);
- }
-
- BlockProto* block1 = expected.add_blocks();
- block1->set_node(0);
- block1->set_zone("DMA");
- block1->set_unmovable(74);
- block1->set_reclaimable(9);
- block1->set_movable(337);
- block1->set_cma(41);
- block1->set_reserve(1);
- block1->set_isolate(0);
-
-
- BlockProto* block2 = expected.add_blocks();
- block2->set_node(0);
- block2->set_zone("Normal");
- block2->set_unmovable(70);
- block2->set_reclaimable(12);
- block2->set_movable(423);
- block2->set_cma(0);
- block2->set_reserve(1);
- block2->set_isolate(0);
-
- 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);
-}
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
new file mode 100644
index 0000000..a8fa6208
--- /dev/null
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "KernelWakesParser.h"
+
+#include "frameworks/base/core/proto/android/os/kernelwake.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 KernelWakesParserTest : 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(KernelWakesParserTest, Short) {
+ const string testFile = kTestDataPath + "kernel_wakeups_short.txt";
+ KernelWakesParser parser;
+ KernelWakeSources expected;
+
+ WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ record1->set_name("ab");
+ record1->set_active_count(8);
+ record1->set_last_change(123456123456LL);
+
+ WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ record2->set_name("df");
+ record2->set_active_count(143);
+ record2->set_last_change(0LL);
+
+ 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);
+}
+
+TEST_F(KernelWakesParserTest, Normal) {
+ const string testFile = kTestDataPath + "kernel_wakeups.txt";
+ KernelWakesParser parser;
+ KernelWakeSources expected;
+
+ WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ record1->set_name("ipc000000ab_ATFWD-daemon");
+ record1->set_active_count(8);
+ record1->set_event_count(8);
+ record1->set_wakeup_count(0);
+ record1->set_expire_count(0);
+ record1->set_active_since(0l);
+ record1->set_total_time(0l);
+ record1->set_max_time(0l);
+ record1->set_last_change(131348LL);
+ record1->set_prevent_suspend_time(0LL);
+
+ WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ record2->set_name("ipc000000aa_ATFWD-daemon");
+ record2->set_active_count(143);
+ record2->set_event_count(143);
+ record2->set_wakeup_count(0);
+ record2->set_expire_count(0);
+ record2->set_active_since(0l);
+ record2->set_total_time(123l);
+ record2->set_max_time(3l);
+ record2->set_last_change(2067286206LL);
+ record2->set_prevent_suspend_time(0LL);
+
+ 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/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
new file mode 100644
index 0000000..de64e70
--- /dev/null
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.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.
+ */
+
+#include "PageTypeInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/pagetypeinfo.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 PageTypeInfoParserTest : 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(PageTypeInfoParserTest, Success) {
+ const string testFile = kTestDataPath + "pagetypeinfo.txt";
+ PageTypeInfoParser parser;
+ PageTypeInfo expected;
+
+ expected.set_page_block_order(10);
+ expected.set_pages_per_block(1024);
+
+ MigrateTypeProto* mt1 = expected.add_migrate_types();
+ mt1->set_node(0);
+ mt1->set_zone("DMA");
+ mt1->set_type("Unmovable");
+ int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0};
+ for (auto i=0; i<11; i++) {
+ mt1->add_free_pages_count(arr1[i]);
+ }
+
+ MigrateTypeProto* mt2 = expected.add_migrate_types();
+ mt2->set_node(0);
+ mt2->set_zone("Normal");
+ mt2->set_type("Reclaimable");
+ int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0};
+ for (auto i=0; i<11; i++) {
+ mt2->add_free_pages_count(arr2[i]);
+ }
+
+ BlockProto* block1 = expected.add_blocks();
+ block1->set_node(0);
+ block1->set_zone("DMA");
+ block1->set_unmovable(74);
+ block1->set_reclaimable(9);
+ block1->set_movable(337);
+ block1->set_cma(41);
+ block1->set_reserve(1);
+ block1->set_isolate(0);
+
+
+ BlockProto* block2 = expected.add_blocks();
+ block2->set_node(0);
+ block2->set_zone("Normal");
+ block2->set_unmovable(70);
+ block2->set_reclaimable(12);
+ block2->set_movable(423);
+ block2->set_cma(0);
+ block2->set_reserve(1);
+ block2->set_isolate(0);
+
+ 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);
+}
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/ProcrankParser_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
new file mode 100644
index 0000000..e86647a
--- /dev/null
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 "ProcrankParser.h"
+
+#include "frameworks/base/core/proto/android/os/procrank.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 ProcrankParserTest : 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(ProcrankParserTest, HasSwapInfo) {
+ const string testFile = kTestDataPath + "procrank.txt";
+ ProcrankParser parser;
+ Procrank expected;
+
+ ProcessProto* process1 = expected.add_processes();
+ process1->set_pid(1119);
+ process1->set_vss(2607640);
+ process1->set_rss(339564);
+ process1->set_pss(180278);
+ process1->set_uss(114216);
+ process1->set_swap(1584);
+ process1->set_pswap(46);
+ process1->set_uswap(0);
+ process1->set_zswap(10);
+ process1->set_cmdline("system_server");
+
+ ProcessProto* process2 = expected.add_processes();
+ process2->set_pid(649);
+ process2->set_vss(11016);
+ process2->set_rss(1448);
+ process2->set_pss(98);
+ process2->set_uss(48);
+ process2->set_swap(472);
+ process2->set_pswap(342);
+ process2->set_uswap(212);
+ process2->set_zswap(75);
+ process2->set_cmdline("/vendor/bin/qseecomd");
+
+ ProcessProto* total = expected.mutable_summary()->mutable_total();
+ total->set_pss(1201993);
+ total->set_uss(935300);
+ total->set_swap(88164);
+ total->set_pswap(31069);
+ total->set_uswap(27612);
+ total->set_zswap(6826);
+ total->set_cmdline("TOTAL");
+
+ expected.mutable_summary()->mutable_zram()
+ ->set_raw_text("6828K physical used for 31076K in swap (524284K total swap)");
+ expected.mutable_summary()->mutable_ram()
+ ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
+
+ 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);
+}
+
+TEST_F(ProcrankParserTest, NoSwapInfo) {
+ const string testFile = kTestDataPath + "procrank_short.txt";
+ ProcrankParser parser;
+ Procrank expected;
+
+ ProcessProto* process1 = expected.add_processes();
+ process1->set_pid(1119);
+ process1->set_vss(2607640);
+ process1->set_rss(339564);
+ process1->set_pss(180278);
+ process1->set_uss(114216);
+ process1->set_cmdline("system_server");
+
+ ProcessProto* process2 = expected.add_processes();
+ process2->set_pid(649);
+ process2->set_vss(11016);
+ process2->set_rss(1448);
+ process2->set_pss(98);
+ process2->set_uss(48);
+ process2->set_cmdline("/vendor/bin/qseecomd");
+
+ ProcessProto* total = expected.mutable_summary()->mutable_total();
+ total->set_pss(1201993);
+ total->set_uss(935300);
+ total->set_cmdline("TOTAL");
+
+ expected.mutable_summary()->mutable_ram()
+ ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
+
+ 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);
+}