diff options
author | Yi Jin <jinyithu@google.com> | 2017-10-17 18:29:33 -0700 |
---|---|---|
committer | Yi Jin <jinyithu@google.com> | 2017-10-31 16:54:38 -0700 |
commit | 04625ad4886a478bf74bbfc13937c10fa63eb272 (patch) | |
tree | ba39988adb170b2e6e744ab304de7f8846a4d444 /cmds/incident_helper | |
parent | eb7d335641ec1b9c91c1609a94cb1cbdba1d3987 (diff) |
Refactor incident_helper to use protoutil and cppstream plugin.
1. Split the parsers to its own file to prevent all the parsers in one
gaint file.
2. Completely get rid of protobuf-cpp-full in incident_helper, use
ProtoOutputStream and cppstream instead, the incident_helper binary is
reduced from ~500K to ~113K.
3. Write data to protobuf even its values are zero/default, the reason
is for example we have a repeated int32 orders = 1; and people
explicitly append 0 so the total repeated field has 10 values, if zero
is not written to serialized data, this repeated field will only have 9
values which is not what we want at first place. This also aligns with
the default protobuf serialization behavior in incident_helper_test.
4. Use Android.bp for protoutil lib since it is not able to depend on
libs compiled by .mk file, it works the other way.
5. Add a new custom message option for streaming_proto, if specified,
the cppstream will create extra metadata to get field ids by field name.
A Table class is created in incident_helper to use it.
Bug: 67860303
Test: unit tested as well as on device test
Change-Id: I8e136fd15f343a4a623d20910ec64b622b478a3e
Diffstat (limited to 'cmds/incident_helper')
17 files changed, 833 insertions, 487 deletions
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp index 053208313b00..2ef037143f07 100644 --- a/cmds/incident_helper/Android.bp +++ b/cmds/incident_helper/Android.bp @@ -8,44 +8,53 @@ cc_defaults { "-O0" ], + local_include_dirs: [ + "src/", + "src/parsers/", + ], + srcs: [ - "IncidentHelper.cpp", - "ih_util.cpp", + "src/parsers/*.cpp", + "src/TextParserBase.cpp", + "src/ih_util.cpp", ], + generated_headers: ["gen-platform-proto-constants"], + shared_libs: [ "libbase", "liblog", - "libprotobuf-cpp-full", + "libprotoutil", "libutils", ], - - static_libs: [ - "libplatformprotos", - ], } cc_binary { name: "incident_helper", defaults: ["incident_helper_defaults"], - srcs: ["main.cpp"], + srcs: ["src/main.cpp"], } cc_test { name: "incident_helper_test", defaults: ["incident_helper_defaults"], + local_include_dirs: ["src/"], srcs: [ - "tests/IncidentHelper_test.cpp", - "tests/ih_util_test.cpp", + "tests/*.cpp", ], data: [ "testdata/*", ], + shared_libs: [ + "libprotobuf-cpp-full", + ], + static_libs: [ "libgmock", + "libplatformprotos" ], -}
\ No newline at end of file +} diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp deleted file mode 100644 index 7b06d42cbb55..000000000000 --- a/cmds/incident_helper/IncidentHelper.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "incident_helper" - -#include "IncidentHelper.h" -#include "ih_util.h" - -#include "frameworks/base/core/proto/android/os/kernelwake.pb.h" -#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" -#include "frameworks/base/core/proto/android/os/procrank.pb.h" - -#include <android-base/file.h> -#include <unistd.h> -#include <string> -#include <vector> - -using namespace android::base; -using namespace android::os; -using namespace google::protobuf; -using namespace std; - - -static const string TAB_DELIMITER = "\t"; -static const string COMMA_DELIMITER = ","; - -static inline int toInt(const string& s) { - return atoi(s.c_str()); -} - -static inline long toLong(const string& s) { - return atol(s.c_str()); -} - -/** - * Sets the given protobuf message when the field name matches one of the - * fields. It is useful to set values to proto from table-like plain texts. - */ -static bool -SetTableField(::google::protobuf::Message* message, string field_name, string field_value) { - const Descriptor* descriptor = message->GetDescriptor(); - const Reflection* reflection = message->GetReflection(); - - const FieldDescriptor* field = descriptor->FindFieldByName(field_name); - switch (field->type()) { - case FieldDescriptor::TYPE_STRING: - reflection->SetString(message, field, field_value); - return true; - case FieldDescriptor::TYPE_INT64: - reflection->SetInt64(message, field, toLong(field_value)); - return true; - case FieldDescriptor::TYPE_UINT64: - reflection->SetUInt64(message, field, toLong(field_value)); - return true; - case FieldDescriptor::TYPE_INT32: - reflection->SetInt32(message, field, toInt(field_value)); - return true; - case FieldDescriptor::TYPE_UINT32: - reflection->SetUInt32(message, field, toInt(field_value)); - return true; - default: - // Add new scalar types - return false; - } -} - -// ================================================================================ -status_t NoopParser::Parse(const int in, const int out) const -{ - string content; - if (!ReadFdToString(in, &content)) { - fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); - return -1; - } - if (!WriteStringToFd(content, out)) { - fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); - return -1; - } - return NO_ERROR; -} - -// ================================================================================ -status_t ReverseParser::Parse(const int in, const int out) const -{ - string content; - if (!ReadFdToString(in, &content)) { - fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); - return -1; - } - // reverse the content - reverse(content.begin(), content.end()); - if (!WriteStringToFd(content, out)) { - fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); - return -1; - } - return NO_ERROR; -} - -// ================================================================================ -status_t KernelWakesParser::Parse(const int in, const int out) const { - Reader reader(in); - string line; - header_t header; // the header of /d/wakeup_sources - record_t record; // retain each record - int nline = 0; - - KernelWakeSources proto; - - // parse line by line - while (reader.readLine(&line)) { - if (line.empty()) continue; - // parse head line - if (nline++ == 0) { - header = parseHeader(line, TAB_DELIMITER); - continue; - } - - // parse for each record, the line delimiter is \t only! - record = parseRecord(line, TAB_DELIMITER); - - if (record.size() != header.size()) { - // TODO: log this to incident report! - fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); - continue; - } - - WakeupSourceProto* source = proto.add_wakeup_sources(); - for (int i=0; i<(int)record.size(); i++) { - if (!SetTableField(source, header[i], record[i])) { - fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", - this->name.string(), nline, header[i].c_str(), record[i].c_str()); - } - } - } - - if (!reader.ok(&line)) { - fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); - return -1; - } - - if (!proto.SerializeToFileDescriptor(out)) { - fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); - return -1; - } - fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); - return NO_ERROR; -} - -// ================================================================================ -status_t ProcrankParser::Parse(const int in, const int out) const { - Reader reader(in); - string line; - header_t header; // the header of /d/wakeup_sources - record_t record; // retain each record - int nline = 0; - - Procrank proto; - - // parse line by line - while (reader.readLine(&line)) { - if (line.empty()) continue; - - // parse head line - if (nline++ == 0) { - header = parseHeader(line); - continue; - } - - if (hasPrefix(&line, "ZRAM:")) { - proto.mutable_summary()->mutable_zram()->set_raw_text(line); - continue; - } - if (hasPrefix(&line, "RAM:")) { - proto.mutable_summary()->mutable_ram()->set_raw_text(line); - continue; - } - - record = parseRecord(line); - if (record.size() != header.size()) { - if (record[record.size() - 1] == "TOTAL") { // TOTAL record - ProcessProto* total = proto.mutable_summary()->mutable_total(); - for (int i=1; i<=(int)record.size(); i++) { - SetTableField(total, header[header.size() - i], record[record.size() - i]); - } - } else { - fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, - line.c_str()); - } - continue; - } - - ProcessProto* process = proto.add_processes(); - for (int i=0; i<(int)record.size(); i++) { - if (!SetTableField(process, header[i], record[i])) { - fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", - this->name.string(), nline, header[i].c_str(), record[i].c_str()); - } - } - } - - if (!reader.ok(&line)) { - fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); - return -1; - } - - if (!proto.SerializeToFileDescriptor(out)) { - fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); - return -1; - } - fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); - return NO_ERROR; -} - -// ================================================================================ -status_t PageTypeInfoParser::Parse(const int in, const int out) const { - Reader reader(in); - string line; - bool migrateTypeSession = false; - int pageBlockOrder; - header_t blockHeader; - - PageTypeInfo pageTypeInfo; - - while (reader.readLine(&line)) { - if (line.empty()) { - migrateTypeSession = false; - blockHeader.clear(); - continue; - } - - if (hasPrefix(&line, "Page block order:")) { - pageBlockOrder = toInt(line); - pageTypeInfo.set_page_block_order(pageBlockOrder); - continue; - } - if (hasPrefix(&line, "Pages per block:")) { - pageTypeInfo.set_pages_per_block(toInt(line)); - continue; - } - if (hasPrefix(&line, "Free pages count per migrate type at order")) { - migrateTypeSession = true; - continue; - } - if (hasPrefix(&line, "Number of blocks type")) { - blockHeader = parseHeader(line); - continue; - } - - record_t record = parseRecord(line, COMMA_DELIMITER); - if (migrateTypeSession && record.size() == 3) { - MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types(); - // expect part 0 starts with "Node" - if (hasPrefix(&record[0], "Node")) { - migrateType->set_node(toInt(record[0])); - } else goto ERROR; - // expect part 1 starts with "zone" - if (hasPrefix(&record[1], "zone")) { - migrateType->set_zone(record[1]); - } else goto ERROR; - // expect part 2 starts with "type" - if (hasPrefix(&record[2], "type")) { - // expect the rest of part 2 has number of (pageBlockOrder + 2) parts - // An example looks like: - // header line: type 0 1 2 3 4 5 6 7 8 9 10 - // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 - // The pageBlockOrder = 10 and it's zero-indexed. so total parts - // are 10 + 1(zero-indexed) + 1(the type part) = 12. - record_t pageCounts = parseRecord(record[2]); - int pageCountsSize = pageBlockOrder + 2; - if ((int)pageCounts.size() != pageCountsSize) goto ERROR; - - migrateType->set_type(pageCounts[0]); - for (auto i=1; i<pageCountsSize; i++) { - migrateType->add_free_pages_count(toInt(pageCounts[i])); - } - } else goto ERROR; - continue; - } - - if (!blockHeader.empty() && record.size() == 2) { - BlockProto* block = pageTypeInfo.add_blocks(); - - if (hasPrefix(&record[0], "Node")) { - block->set_node(toInt(record[0])); - } else goto ERROR; - - if (hasPrefix(&record[1], "zone")) { - record_t blockCounts = parseRecord(record[1]); - block->set_zone(blockCounts[0]); - for (size_t i=0; i<blockHeader.size(); i++) { - if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR; - } - } else goto ERROR; - - continue; - } - -ERROR: // print out error for this single line and continue parsing - fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str()); - } - - if (!reader.ok(&line)) { - fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); - return -1; - } - - if (!pageTypeInfo.SerializeToFileDescriptor(out)) { - fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); - return -1; - } - - fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize()); - return NO_ERROR; -} diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp new file mode 100644 index 000000000000..a8f9968ee8f6 --- /dev/null +++ b/cmds/incident_helper/src/TextParserBase.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "incident_helper" + +#include "TextParserBase.h" + +#include <android-base/file.h> + +using namespace android::base; +using namespace std; + +// ================================================================================ +status_t NoopParser::Parse(const int in, const int out) const +{ + string content; + if (!ReadFdToString(in, &content)) { + fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); + return -1; + } + if (!WriteStringToFd(content, out)) { + fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); + return -1; + } + return NO_ERROR; +} + +// ================================================================================ +status_t ReverseParser::Parse(const int in, const int out) const +{ + string content; + if (!ReadFdToString(in, &content)) { + fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); + return -1; + } + // reverse the content + reverse(content.begin(), content.end()); + if (!WriteStringToFd(content, out)) { + fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); + return -1; + } + return NO_ERROR; +}
\ No newline at end of file diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/src/TextParserBase.h index d24d7173aa26..c41612de4eb3 100644 --- a/cmds/incident_helper/IncidentHelper.h +++ b/cmds/incident_helper/src/TextParserBase.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef INCIDENT_HELPER_H -#define INCIDENT_HELPER_H +#ifndef TEXT_PARSER_BASE_H +#define TEXT_PARSER_BASE_H #include <utils/Errors.h> #include <utils/String8.h> @@ -68,37 +68,4 @@ public: virtual status_t Parse(const int in, const int out) const; }; -/** - * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources - */ -class KernelWakesParser : public TextParserBase { -public: - KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {}; - ~KernelWakesParser() {}; - - virtual status_t Parse(const int in, const int out) const; -}; - -/** - * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype - */ -class PageTypeInfoParser : public TextParserBase { -public: - PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {}; - ~PageTypeInfoParser() {}; - - virtual status_t Parse(const int in, const int out) const; -}; - -/** - * Procrank parser, parses text produced by command procrank - */ -class ProcrankParser : public TextParserBase { -public: - ProcrankParser() : TextParserBase(String8("ProcrankParser")) {}; - ~ProcrankParser() {}; - - virtual status_t Parse(const int in, const int out) const; -}; - -#endif // INCIDENT_HELPER_H +#endif // TEXT_PARSER_BASE_H
\ No newline at end of file diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 2ab4b54e193f..c7d1ca231a03 100644 --- a/cmds/incident_helper/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -87,6 +87,15 @@ bool hasPrefix(std::string* line, const char* key) { return true; } +int toInt(const std::string& s) { + return atoi(s.c_str()); +} + +long long toLongLong(const std::string& s) { + return atoll(s.c_str()); +} + +// ============================================================================== Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {}; Reader::Reader(const int fd, const size_t capacity) @@ -151,3 +160,57 @@ bool Reader::ok(std::string* error) { error->assign(mStatus); return mStatus.empty(); } + +// ============================================================================== +Table::Table(const char* names[], const uint64_t ids[], const int count) + :mFieldNames(names), + mFieldIds(ids), + mFieldCount(count) +{ +} + +Table::~Table() +{ +} + +bool +Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value) +{ + uint64_t found = 0; + for (int i=0; i<mFieldCount; i++) { + if (strcmp(name.c_str(), mFieldNames[i]) == 0) { + found = mFieldIds[i]; + break; + } + } + + switch (found & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_FLOAT: + // TODO: support parse string to float/double + return false; + case FIELD_TYPE_STRING: + case FIELD_TYPE_BYTES: + proto.write(found, value); + break; + case FIELD_TYPE_INT64: + case FIELD_TYPE_SINT64: + case FIELD_TYPE_UINT64: + case FIELD_TYPE_FIXED64: + case FIELD_TYPE_SFIXED64: + proto.write(found, toLongLong(value)); + break; + case FIELD_TYPE_BOOL: + case FIELD_TYPE_ENUM: + case FIELD_TYPE_INT32: + case FIELD_TYPE_SINT32: + case FIELD_TYPE_UINT32: + case FIELD_TYPE_FIXED32: + case FIELD_TYPE_SFIXED32: + proto.write(found, toInt(value)); + break; + default: + return false; + } + return true; +} diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/src/ih_util.h index ce5baeef0dc3..86761e93f49c 100644 --- a/cmds/incident_helper/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -21,6 +21,10 @@ #include <vector> #include <sstream> +#include <android/util/ProtoOutputStream.h> + +using namespace android::util; + typedef std::vector<std::string> header_t; typedef std::vector<std::string> record_t; typedef std::string (*trans_func) (const std::string&); @@ -52,6 +56,12 @@ record_t parseRecord(const std::string& line, const std::string& delimiters = DE bool hasPrefix(std::string* line, const char* key); /** + * Converts string to the desired type + */ +int toInt(const std::string& s); +long long toLongLong(const std::string& s); + +/** * Reader class reads data from given fd in streaming fashion. * The buffer size is controlled by capacity parameter. */ @@ -78,4 +88,22 @@ private: inline bool EOR() { return mFd == -1 && mBufSize == 0; }; }; +/** + * The class contains a mapping between table headers to its field ids. + * And allow users to insert the field values to proto based on its header name. + */ +class Table +{ +public: + Table(const char* names[], const uint64_t ids[], const int count); + ~Table(); + + bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value); + +private: + const char** mFieldNames; + const uint64_t* mFieldIds; + const int mFieldCount; +}; + #endif // INCIDENT_HELPER_UTIL_H diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/src/main.cpp index 52ff77720d70..3da87b9c801b 100644 --- a/cmds/incident_helper/main.cpp +++ b/cmds/incident_helper/src/main.cpp @@ -16,7 +16,9 @@ #define LOG_TAG "incident_helper" -#include "IncidentHelper.h" +#include "parsers/KernelWakesParser.h" +#include "parsers/PageTypeInfoParser.h" +#include "parsers/ProcrankParser.h" #include <android-base/file.h> #include <getopt.h> diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp new file mode 100644 index 000000000000..cc4a1e1ecfa2 --- /dev/null +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/kernelwake.proto.h" +#include "ih_util.h" +#include "KernelWakesParser.h" + +using namespace android::os; + +const std::string LINE_DELIMITER = "\t"; + +status_t +KernelWakesParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + header_t header; // the header of /d/wakeup_sources + record_t record; // retain each record + int nline = 0; + + ProtoOutputStream proto; + Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT); + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + // parse head line + if (nline++ == 0) { + header = parseHeader(line, LINE_DELIMITER); + continue; + } + + // parse for each record, the line delimiter is \t only! + record = parseRecord(line, LINE_DELIMITER); + + if (record.size() != header.size()) { + // TODO: log this to incident report! + fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); + continue; + } + + long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES); + for (int i=0; i<(int)record.size(); i++) { + if (!table.insertField(proto, header[i], record[i])) { + fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", + this->name.string(), nline, header[i].c_str(), record[i].c_str()); + } + } + proto.end(token); + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.h b/cmds/incident_helper/src/parsers/KernelWakesParser.h new file mode 100644 index 000000000000..aabab7c64a4f --- /dev/null +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KERNEL_WAKES_PARSER_H +#define KERNEL_WAKES_PARSER_H + +#include "TextParserBase.h" + +/** + * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources + */ +class KernelWakesParser : public TextParserBase { +public: + KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {}; + ~KernelWakesParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // KERNEL_WAKES_PARSER_H diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp new file mode 100644 index 000000000000..6047bd189b95 --- /dev/null +++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/pagetypeinfo.proto.h" +#include "ih_util.h" +#include "PageTypeInfoParser.h" + +using namespace android::os; + +const std::string LINE_DELIMITER = ","; + +status_t +PageTypeInfoParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + bool migrateTypeSession = false; + int pageBlockOrder; + header_t blockHeader; + + ProtoOutputStream proto; + Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT); + + while (reader.readLine(&line)) { + if (line.empty()) { + migrateTypeSession = false; + blockHeader.clear(); + continue; + } + + if (hasPrefix(&line, "Page block order:")) { + pageBlockOrder = toInt(line); + proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder); + continue; + } + if (hasPrefix(&line, "Pages per block:")) { + proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line)); + continue; + } + if (hasPrefix(&line, "Free pages count per migrate type at order")) { + migrateTypeSession = true; + continue; + } + if (hasPrefix(&line, "Number of blocks type")) { + blockHeader = parseHeader(line); + continue; + } + + record_t record = parseRecord(line, LINE_DELIMITER); + if (migrateTypeSession && record.size() == 3) { + long long token = proto.start(PageTypeInfo::MIGRATE_TYPES); + // expect part 0 starts with "Node" + if (hasPrefix(&record[0], "Node")) { + proto.write(MigrateTypeProto::NODE, toInt(record[0])); + } else return BAD_VALUE; + // expect part 1 starts with "zone" + if (hasPrefix(&record[1], "zone")) { + proto.write(MigrateTypeProto::ZONE, record[1]); + } else return BAD_VALUE; + // expect part 2 starts with "type" + if (hasPrefix(&record[2], "type")) { + // expect the rest of part 2 has number of (pageBlockOrder + 2) parts + // An example looks like: + // header line: type 0 1 2 3 4 5 6 7 8 9 10 + // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 + // The pageBlockOrder = 10 and it's zero-indexed. so total parts + // are 10 + 1(zero-indexed) + 1(the type part) = 12. + record_t pageCounts = parseRecord(record[2]); + int pageCountsSize = pageBlockOrder + 2; + if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE; + + proto.write(MigrateTypeProto::TYPE, pageCounts[0]); + for (auto i=1; i<pageCountsSize; i++) { + proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i])); + } + } else return BAD_VALUE; + + proto.end(token); + } else if (!blockHeader.empty() && record.size() == 2) { + long long token = proto.start(PageTypeInfo::BLOCKS); + if (hasPrefix(&record[0], "Node")) { + proto.write(BlockProto::NODE, toInt(record[0])); + } else return BAD_VALUE; + + if (hasPrefix(&record[1], "zone")) { + record_t blockCounts = parseRecord(record[1]); + proto.write(BlockProto::ZONE, blockCounts[0]); + + for (size_t i=0; i<blockHeader.size(); i++) { + if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) { + return BAD_VALUE; + } + } + } else return BAD_VALUE; + proto.end(token); + } + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +}
\ No newline at end of file diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.h b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h new file mode 100644 index 000000000000..fb84d912a5f2 --- /dev/null +++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PAGE_TYPE_INFO_PARSER_H +#define PAGE_TYPE_INFO_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype + */ +class PageTypeInfoParser : public TextParserBase { +public: + PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {}; + ~PageTypeInfoParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // PAGE_TYPE_INFO_PARSER_H diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp new file mode 100644 index 000000000000..93f970f820d9 --- /dev/null +++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/procrank.proto.h" +#include "ih_util.h" +#include "ProcrankParser.h" + +using namespace android::os; + +status_t +ProcrankParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + header_t header; // the header of /d/wakeup_sources + record_t record; // retain each record + int nline = 0; + + ProtoOutputStream proto; + Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT); + string zram, ram, total; + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + + // parse head line + if (nline++ == 0) { + header = parseHeader(line); + continue; + } + + if (hasPrefix(&line, "ZRAM:")) { + zram = line; + continue; + } + if (hasPrefix(&line, "RAM:")) { + ram = line; + continue; + } + + record = parseRecord(line); + if (record.size() != header.size()) { + if (record[record.size() - 1] == "TOTAL") { // TOTAL record + total = line; + } else { + fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, + line.c_str()); + } + continue; + } + + long long token = proto.start(Procrank::PROCESSES); + for (int i=0; i<(int)record.size(); i++) { + if (!table.insertField(proto, header[i], record[i])) { + fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", + this->name.string(), nline, header[i].c_str(), record[i].c_str()); + } + } + proto.end(token); + } + + // add summary + long long token = proto.start(Procrank::SUMMARY); + if (!total.empty()) { + record = parseRecord(total); + long long token = proto.start(SummaryProto::TOTAL); + for (int i=(int)record.size(); i>0; i--) { + table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str()); + } + proto.end(token); + } + if (!zram.empty()) { + long long token = proto.start(SummaryProto::ZRAM); + proto.write(ZramProto::RAW_TEXT, zram); + proto.end(token); + } + if (!ram.empty()) { + long long token = proto.start(SummaryProto::RAM); + proto.write(RamProto::RAW_TEXT, ram); + proto.end(token); + } + proto.end(token); + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.h b/cmds/incident_helper/src/parsers/ProcrankParser.h new file mode 100644 index 000000000000..5d0ee48aa5b1 --- /dev/null +++ b/cmds/incident_helper/src/parsers/ProcrankParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROCRANK_PARSER_H +#define PROCRANK_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * Procrank parser, parses text produced by command procrank + */ +class ProcrankParser : public TextParserBase { +public: + ProcrankParser() : TextParserBase(String8("ProcrankParser")) {}; + ~ProcrankParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // PROCRANK_PARSER_H diff --git a/cmds/incident_helper/testdata/kernel_wakeups_short.txt b/cmds/incident_helper/testdata/kernel_wakeups_short.txt new file mode 100644 index 000000000000..a51926e70def --- /dev/null +++ b/cmds/incident_helper/testdata/kernel_wakeups_short.txt @@ -0,0 +1,3 @@ +name active_count last_change +ab 8 123456123456 +df 143 0 diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp new file mode 100644 index 000000000000..a8fa62088450 --- /dev/null +++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "KernelWakesParser.h" + +#include "frameworks/base/core/proto/android/os/kernelwake.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class KernelWakesParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + + string getSerializedString(::google::protobuf::Message& message) { + string expectedStr; + message.SerializeToFileDescriptor(tf.fd); + ReadFileToString(tf.path, &expectedStr); + return expectedStr; + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(KernelWakesParserTest, Short) { + const string testFile = kTestDataPath + "kernel_wakeups_short.txt"; + KernelWakesParser parser; + KernelWakeSources expected; + + WakeupSourceProto* record1 = expected.add_wakeup_sources(); + record1->set_name("ab"); + record1->set_active_count(8); + record1->set_last_change(123456123456LL); + + WakeupSourceProto* record2 = expected.add_wakeup_sources(); + record2->set_name("df"); + record2->set_active_count(143); + record2->set_last_change(0LL); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +} + +TEST_F(KernelWakesParserTest, Normal) { + const string testFile = kTestDataPath + "kernel_wakeups.txt"; + KernelWakesParser parser; + KernelWakeSources expected; + + WakeupSourceProto* record1 = expected.add_wakeup_sources(); + record1->set_name("ipc000000ab_ATFWD-daemon"); + record1->set_active_count(8); + record1->set_event_count(8); + record1->set_wakeup_count(0); + record1->set_expire_count(0); + record1->set_active_since(0l); + record1->set_total_time(0l); + record1->set_max_time(0l); + record1->set_last_change(131348LL); + record1->set_prevent_suspend_time(0LL); + + WakeupSourceProto* record2 = expected.add_wakeup_sources(); + record2->set_name("ipc000000aa_ATFWD-daemon"); + record2->set_active_count(143); + record2->set_event_count(143); + record2->set_wakeup_count(0); + record2->set_expire_count(0); + record2->set_active_since(0l); + record2->set_total_time(123l); + record2->set_max_time(3l); + record2->set_last_change(2067286206LL); + record2->set_prevent_suspend_time(0LL); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +} diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp new file mode 100644 index 000000000000..de64e70c80c7 --- /dev/null +++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PageTypeInfoParser.h" + +#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class PageTypeInfoParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + + string getSerializedString(::google::protobuf::Message& message) { + string expectedStr; + message.SerializeToFileDescriptor(tf.fd); + ReadFileToString(tf.path, &expectedStr); + return expectedStr; + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(PageTypeInfoParserTest, Success) { + const string testFile = kTestDataPath + "pagetypeinfo.txt"; + PageTypeInfoParser parser; + PageTypeInfo expected; + + expected.set_page_block_order(10); + expected.set_pages_per_block(1024); + + MigrateTypeProto* mt1 = expected.add_migrate_types(); + mt1->set_node(0); + mt1->set_zone("DMA"); + mt1->set_type("Unmovable"); + int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0}; + for (auto i=0; i<11; i++) { + mt1->add_free_pages_count(arr1[i]); + } + + MigrateTypeProto* mt2 = expected.add_migrate_types(); + mt2->set_node(0); + mt2->set_zone("Normal"); + mt2->set_type("Reclaimable"); + int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0}; + for (auto i=0; i<11; i++) { + mt2->add_free_pages_count(arr2[i]); + } + + BlockProto* block1 = expected.add_blocks(); + block1->set_node(0); + block1->set_zone("DMA"); + block1->set_unmovable(74); + block1->set_reclaimable(9); + block1->set_movable(337); + block1->set_cma(41); + block1->set_reserve(1); + block1->set_isolate(0); + + + BlockProto* block2 = expected.add_blocks(); + block2->set_node(0); + block2->set_zone("Normal"); + block2->set_unmovable(70); + block2->set_reclaimable(12); + block2->set_movable(423); + block2->set_cma(0); + block2->set_reserve(1); + block2->set_isolate(0); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +}
\ No newline at end of file diff --git a/cmds/incident_helper/tests/IncidentHelper_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp index c44a163efa11..e86647ad479b 100644 --- a/cmds/incident_helper/tests/IncidentHelper_test.cpp +++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp @@ -14,10 +14,8 @@ * limitations under the License. */ -#include "IncidentHelper.h" +#include "ProcrankParser.h" -#include "frameworks/base/core/proto/android/os/kernelwake.pb.h" -#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" #include "frameworks/base/core/proto/android/os/procrank.pb.h" #include <android-base/file.h> @@ -38,7 +36,7 @@ using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; -class IncidentHelperTest : public Test { +class ProcrankParserTest : public Test { public: virtual void SetUp() override { ASSERT_TRUE(tf.fd != -1); @@ -58,57 +56,7 @@ protected: const string kTestDataPath = kTestPath + "/testdata/"; }; -TEST_F(IncidentHelperTest, ReverseParser) { - ReverseParser parser; - TemporaryFile tf; - - ASSERT_TRUE(tf.fd != -1); - ASSERT_TRUE(WriteStringToFile("TestData", tf.path, false)); - - CaptureStdout(); - ASSERT_EQ(NO_ERROR, parser.Parse(tf.fd, STDOUT_FILENO)); - EXPECT_THAT(GetCapturedStdout(), StrEq("ataDtseT")); -} - -TEST_F(IncidentHelperTest, KernelWakesParser) { - const string testFile = kTestDataPath + "kernel_wakeups.txt"; - KernelWakesParser parser; - KernelWakeSources expected; - - WakeupSourceProto* record1 = expected.add_wakeup_sources(); - record1->set_name("ipc000000ab_ATFWD-daemon"); - record1->set_active_count(8); - record1->set_event_count(8); - record1->set_wakeup_count(0); - record1->set_expire_count(0); - record1->set_active_since(0l); - record1->set_total_time(0l); - record1->set_max_time(0l); - record1->set_last_change(131348l); - record1->set_prevent_suspend_time(0l); - - WakeupSourceProto* record2 = expected.add_wakeup_sources(); - record2->set_name("ipc000000aa_ATFWD-daemon"); - record2->set_active_count(143); - record2->set_event_count(143); - record2->set_wakeup_count(0); - record2->set_expire_count(0); - record2->set_active_since(0l); - record2->set_total_time(123l); - record2->set_max_time(3l); - record2->set_last_change(2067286206l); - record2->set_prevent_suspend_time(0l); - - int fd = open(testFile.c_str(), O_RDONLY); - ASSERT_TRUE(fd != -1); - - CaptureStdout(); - ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); - EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); - close(fd); -} - -TEST_F(IncidentHelperTest, ProcrankParser) { +TEST_F(ProcrankParserTest, HasSwapInfo) { const string testFile = kTestDataPath + "procrank.txt"; ProcrankParser parser; Procrank expected; @@ -160,7 +108,7 @@ TEST_F(IncidentHelperTest, ProcrankParser) { close(fd); } -TEST_F(IncidentHelperTest, ProcrankParserShortHeader) { +TEST_F(ProcrankParserTest, NoSwapInfo) { const string testFile = kTestDataPath + "procrank_short.txt"; ProcrankParser parser; Procrank expected; @@ -197,59 +145,3 @@ TEST_F(IncidentHelperTest, ProcrankParserShortHeader) { EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); close(fd); } - -TEST_F(IncidentHelperTest, PageTypeInfoParser) { - const string testFile = kTestDataPath + "pagetypeinfo.txt"; - PageTypeInfoParser parser; - PageTypeInfo expected; - - expected.set_page_block_order(10); - expected.set_pages_per_block(1024); - - MigrateTypeProto* mt1 = expected.add_migrate_types(); - mt1->set_node(0); - mt1->set_zone("DMA"); - mt1->set_type("Unmovable"); - int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0}; - for (auto i=0; i<11; i++) { - mt1->add_free_pages_count(arr1[i]); - } - - MigrateTypeProto* mt2 = expected.add_migrate_types(); - mt2->set_node(0); - mt2->set_zone("Normal"); - mt2->set_type("Reclaimable"); - int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0}; - for (auto i=0; i<11; i++) { - mt2->add_free_pages_count(arr2[i]); - } - - BlockProto* block1 = expected.add_blocks(); - block1->set_node(0); - block1->set_zone("DMA"); - block1->set_unmovable(74); - block1->set_reclaimable(9); - block1->set_movable(337); - block1->set_cma(41); - block1->set_reserve(1); - block1->set_isolate(0); - - - BlockProto* block2 = expected.add_blocks(); - block2->set_node(0); - block2->set_zone("Normal"); - block2->set_unmovable(70); - block2->set_reclaimable(12); - block2->set_movable(423); - block2->set_cma(0); - block2->set_reserve(1); - block2->set_isolate(0); - - int fd = open(testFile.c_str(), O_RDONLY); - ASSERT_TRUE(fd != -1); - - CaptureStdout(); - ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); - EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); - close(fd); -}
\ No newline at end of file |