diff options
author | 2017-10-17 18:29:33 -0700 | |
---|---|---|
committer | 2017-10-31 16:54:38 -0700 | |
commit | 04625ad4886a478bf74bbfc13937c10fa63eb272 (patch) | |
tree | ba39988adb170b2e6e744ab304de7f8846a4d444 | |
parent | eb7d335641ec1b9c91c1609a94cb1cbdba1d3987 (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
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; +} |