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:
24
Android.bp
24
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/*",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
56
cmds/incident_helper/src/TextParserBase.cpp
Normal file
56
cmds/incident_helper/src/TextParserBase.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
79
cmds/incident_helper/src/parsers/KernelWakesParser.cpp
Normal file
79
cmds/incident_helper/src/parsers/KernelWakesParser.cpp
Normal 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;
|
||||
}
|
||||
33
cmds/incident_helper/src/parsers/KernelWakesParser.h
Normal file
33
cmds/incident_helper/src/parsers/KernelWakesParser.h
Normal 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
|
||||
127
cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
Normal file
127
cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
Normal 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;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/PageTypeInfoParser.h
Normal file
35
cmds/incident_helper/src/parsers/PageTypeInfoParser.h
Normal 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
|
||||
112
cmds/incident_helper/src/parsers/ProcrankParser.cpp
Normal file
112
cmds/incident_helper/src/parsers/ProcrankParser.cpp
Normal 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;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/ProcrankParser.h
Normal file
35
cmds/incident_helper/src/parsers/ProcrankParser.h
Normal 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
|
||||
3
cmds/incident_helper/testdata/kernel_wakeups_short.txt
vendored
Normal file
3
cmds/incident_helper/testdata/kernel_wakeups_short.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
name active_count last_change
|
||||
ab 8 123456123456
|
||||
df 143 0
|
||||
119
cmds/incident_helper/tests/KernelWakesParser_test.cpp
Normal file
119
cmds/incident_helper/tests/KernelWakesParser_test.cpp
Normal 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);
|
||||
}
|
||||
113
cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
Normal file
113
cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
39
libs/protoutil/Android.bp
Normal 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",
|
||||
],
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
32
tools/streaming_proto/stream.proto
Normal file
32
tools/streaming_proto/stream.proto
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user