diff options
Diffstat (limited to 'cmds/incident_helper/src')
-rw-r--r-- | cmds/incident_helper/src/ih_util.cpp | 149 | ||||
-rw-r--r-- | cmds/incident_helper/src/ih_util.h | 30 | ||||
-rw-r--r-- | cmds/incident_helper/src/main.cpp | 3 | ||||
-rw-r--r-- | cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp | 89 | ||||
-rw-r--r-- | cmds/incident_helper/src/parsers/SystemPropertiesParser.h | 35 |
5 files changed, 218 insertions, 88 deletions
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 0b51e66c2108..db4f586c7e31 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -30,22 +30,26 @@ bool isValidChar(char c) { || (v == (uint8_t)'_'); } -static std::string trim(const std::string& s, const std::string& chars) { - const auto head = s.find_first_not_of(chars); +std::string trim(const std::string& s, const std::string& charset) { + const auto head = s.find_first_not_of(charset); if (head == std::string::npos) return ""; - const auto tail = s.find_last_not_of(chars); + const auto tail = s.find_last_not_of(charset); return s.substr(head, tail - head + 1); } -static std::string trimDefault(const std::string& s) { +static inline std::string toLowerStr(const std::string& s) { + std::string res(s); + std::transform(res.begin(), res.end(), res.begin(), ::tolower); + return res; +} + +static inline std::string trimDefault(const std::string& s) { return trim(s, DEFAULT_WHITESPACE); } -static std::string trimHeader(const std::string& s) { - std::string res = trimDefault(s); - std::transform(res.begin(), res.end(), res.begin(), ::tolower); - return res; +static inline std::string trimHeader(const std::string& s) { + return toLowerStr(trimDefault(s)); } // This is similiar to Split in android-base/file.h, but it won't add empty string @@ -188,97 +192,106 @@ bool Reader::ok(std::string* error) { } // ============================================================================== -static int -lookupName(const char** names, const int size, const char* name) +Table::Table(const char* names[], const uint64_t ids[], const int count) + :mEnums(), + mEnumValuesByName() { - for (int i=0; i<size; i++) { - if (strcmp(name, names[i]) == 0) { - return i; - } + map<std::string, uint64_t> fields; + for (int i = 0; i < count; i++) { + fields[names[i]] = ids[i]; } - return -1; -} - -EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount) - :mEnumNames(enumNames), - mEnumValues(enumValues), - mEnumCount(enumCount) -{ -} - -EnumTypeMap::~EnumTypeMap() -{ + mFields = fields; } -int -EnumTypeMap::parseValue(const std::string& value) +Table::~Table() { - int index = lookupName(mEnumNames, mEnumCount, value.c_str()); - if (index < 0) return mEnumValues[0]; // Assume value 0 is default - return mEnumValues[index]; } -Table::Table(const char* names[], const uint64_t ids[], const int count) - :mFieldNames(names), - mFieldIds(ids), - mFieldCount(count), - mEnums() +void +Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize) { -} + if (mFields.find(field) == mFields.end()) return; -Table::~Table() -{ + map<std::string, int> enu; + for (int i = 0; i < enumSize; i++) { + enu[enumNames[i]] = enumValues[i]; + } + mEnums[field] = enu; } void -Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize) +Table::addEnumNameToValue(const char* enumName, const int enumValue) { - int index = lookupName(mFieldNames, mFieldCount, field); - if (index < 0) return; - - EnumTypeMap enu(enumNames, enumValues, enumSize); - mEnums[index] = enu; + mEnumValuesByName[enumName] = enumValue; } bool Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value) { - int index = lookupName(mFieldNames, mFieldCount, name.c_str()); - if (index < 0) return false; + if (mFields.find(name) == mFields.end()) return false; - uint64_t found = mFieldIds[index]; - switch (found & FIELD_TYPE_MASK) { - case FIELD_TYPE_DOUBLE: - case FIELD_TYPE_FLOAT: + uint64_t found = mFields[name]; + record_t repeats; // used for repeated fields + switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) { + case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE: + case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT: proto->write(found, toDouble(value)); break; - case FIELD_TYPE_STRING: - case FIELD_TYPE_BYTES: + case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING: + case FIELD_COUNT_SINGLE | 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: + case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64: proto->write(found, toLongLong(value)); break; - case FIELD_TYPE_BOOL: + case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL: + if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) { + proto->write(found, true); + break; + } + if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) { + proto->write(found, false); + break; + } return false; - case FIELD_TYPE_ENUM: - if (mEnums.find(index) == mEnums.end()) { - // forget to add enum type mapping + case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM: + // if the field has its own enum mapping, use this, otherwise use general name to value mapping. + if (mEnums.find(name) != mEnums.end()) { + if (mEnums[name].find(value) != mEnums[name].end()) { + proto->write(found, mEnums[name][value]); + } else { + proto->write(found, 0); // TODO: should get the default enum value (Unknown) + } + } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) { + proto->write(found, mEnumValuesByName[value]); + } else { return false; } - proto->write(found, mEnums[index].parseValue(value)); break; - case FIELD_TYPE_INT32: - case FIELD_TYPE_SINT32: - case FIELD_TYPE_UINT32: - case FIELD_TYPE_FIXED32: - case FIELD_TYPE_SFIXED32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32: proto->write(found, toInt(value)); break; + // REPEATED TYPE below: + case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32: + repeats = parseRecord(value, COMMA_DELIMITER); + for (size_t i=0; i<repeats.size(); i++) { + proto->write(found, toInt(repeats[i])); + } + break; + case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING: + repeats = parseRecord(value, COMMA_DELIMITER); + for (size_t i=0; i<repeats.size(); i++) { + proto->write(found, repeats[i]); + } + break; default: return false; } diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index e8366fa599e2..4a5fe1dd7a42 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -37,6 +37,9 @@ const std::string COMMA_DELIMITER = ","; // returns true if c is a-zA-Z0-9 or underscore _ bool isValidChar(char c); +// trim the string with the given charset +std::string trim(const std::string& s, const std::string& charset); + /** * When a text has a table format like this * line 1: HeadA HeadB HeadC @@ -98,21 +101,6 @@ private: std::string mStatus; }; -class EnumTypeMap -{ -public: - EnumTypeMap() {}; - EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount); - ~EnumTypeMap(); - - int parseValue(const std::string& value); - -private: - const char** mEnumNames; - const uint32_t* mEnumValues; - int mEnumCount; -}; - /** * 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. @@ -124,14 +112,16 @@ public: ~Table(); // Add enum names to values for parsing purpose. - void addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize); + void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize); + + // manually add enum names to values mapping, useful when an Enum type is used by a lot of fields, and there are no name conflicts + void addEnumNameToValue(const char* enumName, const int enumValue); bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value); private: - const char** mFieldNames; - const uint64_t* mFieldIds; - const int mFieldCount; - map<int, EnumTypeMap> mEnums; + map<std::string, uint64_t> mFields; + map<std::string, map<std::string, int>> mEnums; + map<std::string, int> mEnumValuesByName; }; #endif // INCIDENT_HELPER_UTIL_H diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp index 5c9a468dea0b..c8a0883d493c 100644 --- a/cmds/incident_helper/src/main.cpp +++ b/cmds/incident_helper/src/main.cpp @@ -21,6 +21,7 @@ #include "parsers/KernelWakesParser.h" #include "parsers/PageTypeInfoParser.h" #include "parsers/ProcrankParser.h" +#include "parsers/SystemPropertiesParser.h" #include <android-base/file.h> #include <getopt.h> @@ -50,6 +51,8 @@ static TextParserBase* selectParser(int section) { return new ReverseParser(); /* ========================================================================= */ // IDs larger than 1 are section ids reserved in incident.proto + case 1000: + return new SystemPropertiesParser(); case 2000: return new ProcrankParser(); case 2001: diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp new file mode 100644 index 000000000000..ee5feb03242e --- /dev/null +++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp @@ -0,0 +1,89 @@ +/* + * 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/system_properties.proto.h" +#include "ih_util.h" +#include "SystemPropertiesParser.h" + +using namespace android::os; + +const string LINE_DELIMITER = "]: ["; + +// system properties' names sometimes are not valid proto field names, make the names valid. +static string convertToFieldName(const string& name) { + int len = (int)name.length(); + char cstr[len + 1]; + strcpy(cstr, name.c_str()); + for (int i = 0; i < len; i++) { + if (!isValidChar(cstr[i])) { + cstr[i] = '_'; + } + } + return string(cstr); +} + +status_t +SystemPropertiesParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + string name; // the name of the property + string value; // the string value of the property + + ProtoOutputStream proto; + Table table(SystemPropertiesProto::_FIELD_NAMES, SystemPropertiesProto::_FIELD_IDS, SystemPropertiesProto::_FIELD_COUNT); + table.addEnumNameToValue("running", SystemPropertiesProto::STATUS_RUNNING); + table.addEnumNameToValue("stopped", SystemPropertiesProto::STATUS_STOPPED); + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + + line = line.substr(1, line.size() - 2); // trim [] + size_t index = line.find(LINE_DELIMITER); // split by "]: [" + if (index == string::npos) { + fprintf(stderr, "Bad Line %s\n", line.c_str()); + continue; + } + name = line.substr(0, index); + value = trim(line.substr(index + 4), DEFAULT_WHITESPACE); + if (value.empty()) continue; + + // if the property name couldn't be found in proto definition or the value has mistype, + // add to extra properties with its name and value + if (!table.insertField(&proto, convertToFieldName(name), value)) { + long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES); + proto.write(SystemPropertiesProto::Property::NAME, name); + proto.write(SystemPropertiesProto::Property::VALUE, 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; +} diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.h b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h new file mode 100644 index 000000000000..c4016006a48a --- /dev/null +++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.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 SYSTEM_PROPERTIES_PARSER_H +#define SYSTEM_PROPERTIES_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * SystemProperties parser, parses text produced by command getprop. + */ +class SystemPropertiesParser : public TextParserBase { +public: + SystemPropertiesParser() : TextParserBase(String8("SystemPropertiesParser")) {}; + ~SystemPropertiesParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // SYSTEM_PROPERTIES_PARSER_H |