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
This commit is contained in:
Yi Jin
2017-10-17 18:29:33 -07:00
parent eb7d335641
commit 04625ad488
29 changed files with 1100 additions and 660 deletions

View File

@@ -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/*",

View File

@@ -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)

View File

@@ -8,44 +8,53 @@ cc_defaults {
"-O0"
],
srcs: [
"IncidentHelper.cpp",
"ih_util.cpp",
local_include_dirs: [
"src/",
"src/parsers/",
],
srcs: [
"src/parsers/*.cpp",
"src/TextParserBase.cpp",
"src/ih_util.cpp",
],
generated_headers: ["gen-platform-proto-constants"],
shared_libs: [
"libbase",
"liblog",
"libprotobuf-cpp-full",
"libprotoutil",
"libutils",
],
static_libs: [
"libplatformprotos",
],
}
cc_binary {
name: "incident_helper",
defaults: ["incident_helper_defaults"],
srcs: ["main.cpp"],
srcs: ["src/main.cpp"],
}
cc_test {
name: "incident_helper_test",
defaults: ["incident_helper_defaults"],
local_include_dirs: ["src/"],
srcs: [
"tests/IncidentHelper_test.cpp",
"tests/ih_util_test.cpp",
"tests/*.cpp",
],
data: [
"testdata/*",
],
shared_libs: [
"libprotobuf-cpp-full",
],
static_libs: [
"libgmock",
"libplatformprotos"
],
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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&);
@@ -51,6 +55,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

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -0,0 +1,3 @@
name active_count last_change
ab 8 123456123456
df 143 0

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

39
libs/protoutil/Android.bp Normal file
View File

@@ -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",
],
}

View File

@@ -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)

View File

@@ -19,12 +19,58 @@
#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.
*
@@ -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);

View File

@@ -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]);

View File

@@ -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"],
}

View File

@@ -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;
}
}

View File

@@ -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;
}