summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yi Jin <jinyithu@google.com> 2017-10-17 18:29:33 -0700
committer Yi Jin <jinyithu@google.com> 2017-10-31 16:54:38 -0700
commit04625ad4886a478bf74bbfc13937c10fa63eb272 (patch)
treeba39988adb170b2e6e744ab304de7f8846a4d444
parenteb7d335641ec1b9c91c1609a94cb1cbdba1d3987 (diff)
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
-rw-r--r--Android.bp24
-rw-r--r--Android.mk1
-rw-r--r--cmds/incident_helper/Android.bp31
-rw-r--r--cmds/incident_helper/IncidentHelper.cpp327
-rw-r--r--cmds/incident_helper/src/TextParserBase.cpp56
-rw-r--r--cmds/incident_helper/src/TextParserBase.h (renamed from cmds/incident_helper/IncidentHelper.h)39
-rw-r--r--cmds/incident_helper/src/ih_util.cpp (renamed from cmds/incident_helper/ih_util.cpp)63
-rw-r--r--cmds/incident_helper/src/ih_util.h (renamed from cmds/incident_helper/ih_util.h)28
-rw-r--r--cmds/incident_helper/src/main.cpp (renamed from cmds/incident_helper/main.cpp)4
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.cpp79
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.h33
-rw-r--r--cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp127
-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/testdata/kernel_wakeups_short.txt3
-rw-r--r--cmds/incident_helper/tests/KernelWakesParser_test.cpp119
-rw-r--r--cmds/incident_helper/tests/PageTypeInfoParser_test.cpp113
-rw-r--r--cmds/incident_helper/tests/ProcrankParser_test.cpp (renamed from cmds/incident_helper/tests/IncidentHelper_test.cpp)116
-rw-r--r--core/proto/android/os/kernelwake.proto6
-rw-r--r--core/proto/android/os/pagetypeinfo.proto3
-rw-r--r--core/proto/android/os/procrank.proto4
-rw-r--r--libs/protoutil/Android.bp39
-rw-r--r--libs/protoutil/Android.mk39
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h57
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp179
-rw-r--r--tools/streaming_proto/Android.bp20
-rw-r--r--tools/streaming_proto/cpp/main.cpp34
-rw-r--r--tools/streaming_proto/stream.proto32
29 files changed, 1099 insertions, 659 deletions
diff --git a/Android.bp b/Android.bp
index c89cc400171d..5c1ccb7cc639 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,6 +47,7 @@ cc_library {
srcs: [
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
+ "tools/streaming_proto/stream.proto",
],
},
android: {
@@ -61,7 +62,7 @@ cc_library {
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
"core/proto/android/service/graphicsstats.proto",
- "libs/incident/proto/android/privacy.proto",
+ "tools/streaming_proto/stream.proto",
],
shared: {
enabled: false,
@@ -70,6 +71,27 @@ cc_library {
},
}
+gensrcs {
+ name: "gen-platform-proto-constants",
+ depfile: true,
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-cppstream",
+ ],
+
+ srcs: [
+ "core/proto/android/os/kernelwake.proto",
+ "core/proto/android/os/pagetypeinfo.proto",
+ "core/proto/android/os/procrank.proto",
+ ],
+
+ // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
+ cmd: "PATH=$$PATH:$$(dirname $(location protoc-gen-cppstream)) $(location aprotoc) --plugin=protoc-gen-cpp-stream=$(location protoc-gen-cppstream) --dependency_out=$(depfile) --cppstream_out=$(genDir)/ -Iexternal/protobuf/src -I . $(in)",
+
+ output_extension = "proto.h",
+}
+
subdirs = [
"cmds/*",
"core/*",
diff --git a/Android.mk b/Android.mk
index 9843f175bf2d..1ed8a2506600 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1553,6 +1553,7 @@ LOCAL_PROTOC_FLAGS := \
-Iexternal/protobuf/src
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
+ tools/streaming_proto/stream.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 053208313b00..2ef037143f07 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -8,44 +8,53 @@ cc_defaults {
"-O0"
],
+ local_include_dirs: [
+ "src/",
+ "src/parsers/",
+ ],
+
srcs: [
- "IncidentHelper.cpp",
- "ih_util.cpp",
+ "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 7b06d42cbb55..000000000000
--- 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 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/IncidentHelper.h b/cmds/incident_helper/src/TextParserBase.h
index d24d7173aa26..c41612de4eb3 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 @@ public:
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
index 2ab4b54e193f..c7d1ca231a03 100644
--- a/cmds/incident_helper/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -87,6 +87,15 @@ bool hasPrefix(std::string* line, const char* key) {
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 @@ bool Reader::ok(std::string* error) {
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
index ce5baeef0dc3..86761e93f49c 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 @@ record_t parseRecord(const std::string& line, const std::string& delimiters = DE
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 @@ private:
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
index 52ff77720d70..3da87b9c801b 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 000000000000..cc4a1e1ecfa2
--- /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 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..6047bd189b95
--- /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 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..93f970f820d9
--- /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 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/testdata/kernel_wakeups_short.txt b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
new file mode 100644
index 000000000000..a51926e70def
--- /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/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
new file mode 100644
index 000000000000..a8fa62088450
--- /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 000000000000..de64e70c80c7
--- /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/IncidentHelper_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
index c44a163efa11..e86647ad479b 100644
--- a/cmds/incident_helper/tests/IncidentHelper_test.cpp
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-#include "IncidentHelper.h"
+#include "ProcrankParser.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>
@@ -38,7 +36,7 @@ using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;
-class IncidentHelperTest : public Test {
+class ProcrankParserTest : public Test {
public:
virtual void SetUp() override {
ASSERT_TRUE(tf.fd != -1);
@@ -58,57 +56,7 @@ protected:
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) {
+TEST_F(ProcrankParserTest, HasSwapInfo) {
const string testFile = kTestDataPath + "procrank.txt";
ProcrankParser parser;
Procrank expected;
@@ -160,7 +108,7 @@ TEST_F(IncidentHelperTest, ProcrankParser) {
close(fd);
}
-TEST_F(IncidentHelperTest, ProcrankParserShortHeader) {
+TEST_F(ProcrankParserTest, NoSwapInfo) {
const string testFile = kTestDataPath + "procrank_short.txt";
ProcrankParser parser;
Procrank expected;
@@ -197,59 +145,3 @@ TEST_F(IncidentHelperTest, ProcrankParserShortHeader) {
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/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index 12649e1f0ab2..d032a452c14e 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
syntax = "proto2";
+
option java_multiple_files = true;
option java_outer_classname = "WakeupSourcesProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
message KernelWakeSources {
@@ -27,6 +29,8 @@ message KernelWakeSources {
// Next Tag: 11
message WakeupSourceProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
+
// Name of the event which triggers application processor
optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index f82ea7672879..22b3d730f1a1 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -18,6 +18,8 @@ syntax = "proto2";
option java_multiple_files = true;
option java_outer_classname = "PageTypeInfoProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
/*
@@ -61,6 +63,7 @@ message MigrateTypeProto {
// Next tag: 9
message BlockProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
optional int32 node = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index ab6a6a32f698..4d62a60c8345 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -18,6 +18,8 @@ syntax = "proto2";
option java_multiple_files = true;
option java_outer_classname = "ProcrankProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
//Memory usage of running processes
@@ -31,6 +33,8 @@ message Procrank {
// Next Tag: 11
message ProcessProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
+
// ID of the process
optional int32 pid = 1;
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
new file mode 100644
index 000000000000..4f1d2d5a4fe5
--- /dev/null
+++ b/libs/protoutil/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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.
+
+cc_library {
+ name: "libprotoutil",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+
+ srcs: [
+ "src/EncodedBuffer.cpp",
+ "src/ProtoOutputStream.cpp",
+ "src/protobuf.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+}
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
deleted file mode 100644
index 2a2b087dc032..000000000000
--- a/libs/protoutil/Android.mk
+++ /dev/null
@@ -1,39 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libprotoutil
-
-LOCAL_CFLAGS := \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
- src/ProtoOutputStream.cpp \
- src/protobuf.cpp \
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index b8415b2e264b..2155084db534 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -19,13 +19,59 @@
#include <android/util/EncodedBuffer.h>
-#include <stdint.h>
#include <string>
namespace android {
namespace util {
/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+/**
+ * Mask for the field types stored in a fieldId. Leaves a whole
+ * byte for future expansion, even though there are currently only 17 types.
+ */
+const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
+
+const uint64_t FIELD_TYPE_UNKNOWN = 0;
+const uint64_t FIELD_TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
+const uint64_t FIELD_TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
+const uint64_t FIELD_TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT64 if negative
+ // values are likely.
+const uint64_t FIELD_TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
+const uint64_t FIELD_TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT32 if negative
+ // values are likely.
+const uint64_t FIELD_TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
+const uint64_t FIELD_TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
+const uint64_t FIELD_TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
+const uint64_t FIELD_TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
+const uint64_t FIELD_TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
+const uint64_t FIELD_TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
+
+const uint64_t FIELD_TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
+const uint64_t FIELD_TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
+const uint64_t FIELD_TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
+const uint64_t FIELD_TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
+const uint64_t FIELD_TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
+const uint64_t FIELD_TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
+const uint64_t FIELD_TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+/**
* Class to write to a protobuf stream.
*
* Each write method takes an ID code from the protoc generated classes
@@ -60,11 +106,12 @@ public:
void end(long long token);
/**
- * Flushes the protobuf data out to given fd.
+ * Flushes the protobuf data out to given fd. When the following functions are called,
+ * it is not able to write to ProtoOutputStream any more since the data is compact.
*/
- size_t size();
- EncodedBuffer::iterator data();
- bool flush(int fd);
+ size_t size(); // Get the size of the serialized protobuf.
+ EncodedBuffer::iterator data(); // Get the reader apis of the data.
+ bool flush(int fd); // Flush data directly to a file descriptor.
// Please don't use the following functions to dump protos unless you are familiar with protobuf encoding.
void writeRawVarint(uint64_t varint);
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index b91e3db0e39c..9d8ee729a80d 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -18,58 +18,10 @@
#include <android/util/protobuf.h>
#include <android/util/ProtoOutputStream.h>
#include <cutils/log.h>
-#include <cstring>
namespace android {
namespace util {
-/**
- * Position of the field type in a (long long) fieldId.
- */
-const uint64_t FIELD_TYPE_SHIFT = 32;
-
-/**
- * Mask for the field types stored in a fieldId. Leaves a whole
- * byte for future expansion, even though there are currently only 17 types.
- */
-const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
-
-const uint64_t FIELD_TYPE_UNKNOWN = 0;
-const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
-const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
-const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
- // take 10 bytes. Use TYPE_SINT64 if negative
- // values are likely.
-const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
-const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
- // take 10 bytes. Use TYPE_SINT32 if negative
- // values are likely.
-const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
-const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
-const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
-const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
-const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
-const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
-
-const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
-const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
-const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
-const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
-const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
-const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
-const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
-
-//
-// FieldId flags for whether the field is single, repeated or packed.
-// TODO: packed is not supported yet.
-//
-const uint64_t FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_UNKNOWN = 0;
-const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
-
ProtoOutputStream::ProtoOutputStream()
:mBuffer(),
mCopyBegin(0),
@@ -90,18 +42,18 @@ ProtoOutputStream::write(uint64_t fieldId, double val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
default:
ALOGW("Field type %d is not supported when writing double val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -116,18 +68,18 @@ ProtoOutputStream::write(uint64_t fieldId, float val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
default:
ALOGW("Field type %d is not supported when writing float val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -142,20 +94,20 @@ ProtoOutputStream::write(uint64_t fieldId, int val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
default:
ALOGW("Field type %d is not supported when writing int val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -170,20 +122,20 @@ ProtoOutputStream::write(uint64_t fieldId, long long val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
default:
ALOGW("Field type %d is not supported when writing long long val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -198,7 +150,7 @@ ProtoOutputStream::write(uint64_t fieldId, bool val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_BOOL:
+ case FIELD_TYPE_BOOL:
writeBoolImpl(id, val);
return true;
default:
@@ -214,7 +166,7 @@ ProtoOutputStream::write(uint64_t fieldId, string val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_STRING:
+ case FIELD_TYPE_STRING:
writeUtf8StringImpl(id, val.c_str(), val.size());
return true;
default:
@@ -230,11 +182,11 @@ ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_STRING:
- case TYPE_BYTES:
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_BYTES:
writeUtf8StringImpl(id, val, size);
return true;
- case TYPE_MESSAGE:
+ case FIELD_TYPE_MESSAGE:
// can directly write valid format of message bytes into ProtoOutputStream without calling start/end
writeMessageBytesImpl(id, val, size);
return true;
@@ -291,7 +243,7 @@ static int getSizePosFromToken(long long token) {
long long
ProtoOutputStream::start(uint64_t fieldId)
{
- if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) {
+ if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) {
ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId);
return 0;
}
@@ -564,7 +516,6 @@ inline To bit_cast(From const &from) {
inline void
ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
{
- if (val == 0.0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
}
@@ -572,7 +523,6 @@ ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
inline void
ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
{
- if (val == 0.0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
}
@@ -580,7 +530,6 @@ ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
inline void
ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((uint64_t)val);
}
@@ -588,7 +537,6 @@ ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
inline void
ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((uint32_t)val);
}
@@ -596,7 +544,6 @@ ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
inline void
ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64(val);
}
@@ -604,7 +551,6 @@ ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
inline void
ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32(val);
}
@@ -612,7 +558,6 @@ ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
inline void
ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64(val);
}
@@ -620,7 +565,6 @@ ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
inline void
ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32(val);
}
@@ -628,7 +572,6 @@ ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
inline void
ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64((uint64_t)val);
}
@@ -636,7 +579,6 @@ ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
inline void
ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32((uint32_t)val);
}
@@ -644,7 +586,6 @@ ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
inline void
ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
}
@@ -652,7 +593,6 @@ ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
inline void
ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
}
@@ -667,7 +607,6 @@ ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
inline void
ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
{
- if (!val) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32(val ? 1 : 0);
}
@@ -675,7 +614,7 @@ ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
inline void
ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
{
- if (val == NULL || size == 0) return;
+ if (val == NULL) return;
writeLengthDelimitedHeader(id, size);
for (size_t i=0; i<size; i++) {
mBuffer.writeRawByte((uint8_t)val[i]);
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 756549c5e880..96e060d0fc26 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -25,6 +25,25 @@ cc_defaults {
],
}
+cc_library {
+ name: "streamingflags",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ target: {
+ host: {
+ proto: {
+ type: "full",
+ },
+ srcs: [
+ "stream.proto",
+ ],
+ },
+ },
+}
cc_binary_host {
name: "protoc-gen-javastream",
@@ -44,4 +63,5 @@ cc_binary_host {
defaults: ["protoc-gen-stream-defaults"],
shared_libs: ["libprotoc"],
+ static_libs: ["streamingflags"],
}
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index d4e1b7aede92..dc96d5c54c16 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -1,6 +1,8 @@
#include "Errors.h"
#include "string_utils.h"
+#include <frameworks/base/tools/streaming_proto/stream.pb.h>
+
#include "google/protobuf/compiler/plugin.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"
@@ -160,6 +162,12 @@ write_field(stringstream& text, const FieldDescriptorProto& field, const string&
text << endl;
}
+static inline bool
+should_generate_fields_mapping(const DescriptorProto& message)
+{
+ return message.options().GetExtension(stream).enable_fields_mapping();
+}
+
static void
write_message(stringstream& text, const DescriptorProto& message, const string& indent)
{
@@ -167,8 +175,7 @@ write_message(stringstream& text, const DescriptorProto& message, const string&
const string indented = indent + INDENT;
text << indent << "// message " << message.name() << endl;
- text << indent << "class " << message.name() << " {" << endl;
- text << indent << "public:" << endl;
+ text << indent << "namespace " << message.name() << " {" << endl;
// Enums
N = message.enum_type_size();
@@ -188,12 +195,27 @@ write_message(stringstream& text, const DescriptorProto& message, const string&
write_field(text, message.field(i), indented);
}
- text << indent << "};" << endl;
+ if (should_generate_fields_mapping(message)) {
+ N = message.field_size();
+ text << indented << "const int _FIELD_COUNT = " << N << ";" << endl;
+ text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indented << INDENT << "\"" << message.field(i).name() << "\"," << endl;
+ }
+ text << indented << "};" << endl;
+ text << indented << "const uint64_t _FIELD_IDS[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indented << INDENT << make_constant_name(message.field(i).name()) << "," << endl;
+ }
+ text << indented << "};" << endl << endl;
+ }
+
+ text << indent << "} //" << message.name() << endl;
text << endl;
}
static void
-write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+write_header_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
{
stringstream text;
@@ -255,7 +277,7 @@ int main(int argc, char const *argv[])
for (int i=0; i<N; i++) {
const FileDescriptorProto& file_descriptor = request.proto_file(i);
if (should_generate_for_file(request, file_descriptor.name())) {
- write_cpp_file(&response, file_descriptor);
+ write_header_file(&response, file_descriptor);
}
}
@@ -270,4 +292,4 @@ int main(int argc, char const *argv[])
/* code */
return 0;
-} \ No newline at end of file
+}
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
new file mode 100644
index 000000000000..123506c03cfd
--- /dev/null
+++ b/tools/streaming_proto/stream.proto
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+import "google/protobuf/descriptor.proto";
+
+package android.stream_proto;
+
+// This option tells streaming proto plugin to compile .proto files with extra features.
+message StreamFlags {
+ // creates a mapping of field names of the message to its field ids
+ optional bool enable_fields_mapping = 1;
+}
+
+extend google.protobuf.MessageOptions {
+ // Flags used by streaming proto plugins
+ optional StreamFlags stream = 126856794;
+}