Loading cmds/incident_helper/IncidentHelper.cpp +44 −60 Original line number Diff line number Diff line Loading @@ -29,8 +29,37 @@ using namespace android::base; using namespace android::os; using namespace google::protobuf; using namespace std; 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, atol(field_value.c_str())); return true; case FieldDescriptor::TYPE_UINT64: reflection->SetUInt64(message, field, atol(field_value.c_str())); return true; case FieldDescriptor::TYPE_INT32: reflection->SetInt32(message, field, atoi(field_value.c_str())); return true; case FieldDescriptor::TYPE_UINT32: reflection->SetUInt32(message, field, atoi(field_value.c_str())); return true; default: // Add new scalar types return false; } } // ================================================================================ status_t ReverseParser::Parse(const int in, const int out) const { Loading @@ -51,31 +80,6 @@ status_t ReverseParser::Parse(const int in, const int out) const // ================================================================================ static const string KERNEL_WAKEUP_LINE_DELIMITER = "\t"; static void SetWakeupSourceField(WakeupSourceProto* source, string name, string value) { if (name == "name") { source->set_name(value.c_str()); } else if (name == "active_count") { source->set_active_count(atoi(value.c_str())); } else if (name == "event_count") { source->set_event_count(atoi(value.c_str())); } else if (name == "wakeup_count") { source->set_wakeup_count(atoi(value.c_str())); } else if (name == "expire_count") { source->set_expire_count(atoi(value.c_str())); } else if (name == "active_count") { source->set_active_since(atol(value.c_str())); } else if (name == "total_time") { source->set_total_time(atol(value.c_str())); } else if (name == "max_time") { source->set_max_time(atol(value.c_str())); } else if (name == "last_change") { source->set_last_change(atol(value.c_str())); } else if (name == "prevent_suspend_time") { source->set_prevent_suspend_time(atol(value.c_str())); } // add new fields } status_t KernelWakesParser::Parse(const int in, const int out) const { Reader reader(in); string line; Loading @@ -90,12 +94,12 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { if (line.empty()) continue; // parse head line if (nline++ == 0) { split(line, header, KERNEL_WAKEUP_LINE_DELIMITER); header = parseHeader(line, KERNEL_WAKEUP_LINE_DELIMITER); continue; } // parse for each record, the line delimiter is \t only! split(line, record, KERNEL_WAKEUP_LINE_DELIMITER); record = parseRecord(line, KERNEL_WAKEUP_LINE_DELIMITER); if (record.size() != header.size()) { // TODO: log this to incident report! Loading @@ -105,7 +109,10 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { WakeupSourceProto* source = proto.add_wakeup_sources(); for (int i=0; i<(int)record.size(); i++) { SetWakeupSourceField(source, header[i], record[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()); } } } Loading @@ -123,32 +130,6 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { } // ================================================================================ // Remove K for numeric fields static void SetProcessField(ProcessProto* process, string name, string value) { ssize_t len = value.size(); if (name == "PID") { process->set_pid(atoi(value.c_str())); } else if (name == "Vss") { process->set_vss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Rss") { process->set_rss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Pss") { process->set_pss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Uss") { process->set_uss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Swap") { process->set_swap(atol(value.substr(0, len - 1).c_str())); } else if (name == "PSwap") { process->set_pswap(atol(value.substr(0, len - 1).c_str())); } else if (name == "USwap") { process->set_uswap(atol(value.substr(0, len - 1).c_str())); } else if (name == "ZSwap") { process->set_zswap(atol(value.substr(0, len - 1).c_str())); } else if (name == "cmdline") { process->set_cmdline(value); } } status_t ProcrankParser::Parse(const int in, const int out) const { Reader reader(in); string line, content; Loading @@ -164,22 +145,22 @@ status_t ProcrankParser::Parse(const int in, const int out) const { // parse head line if (nline++ == 0) { split(line, header); header = parseHeader(line); continue; } split(line, record); 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++) { SetProcessField(total, header[header.size() - i], record[record.size() - i]); SetTableField(total, header[header.size() - i], record[record.size() - i]); } } else if (record[0] == "ZRAM:") { split(line, record, ":"); record = parseRecord(line, ":"); proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]); } else if (record[0] == "RAM:") { split(line, record, ":"); record = parseRecord(line, ":"); proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]); } else { fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, Loading @@ -190,7 +171,10 @@ status_t ProcrankParser::Parse(const int in, const int out) const { ProcessProto* process = proto.add_processes(); for (int i=0; i<(int)record.size(); i++) { SetProcessField(process, header[i], record[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()); } } } Loading cmds/incident_helper/ih_util.cpp +25 −12 Original line number Diff line number Diff line Loading @@ -23,16 +23,24 @@ const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB std::string trim(const std::string& s, const std::string& whitespace) { const auto head = s.find_first_not_of(whitespace); static std::string trim(const std::string& s) { const auto head = s.find_first_not_of(DEFAULT_WHITESPACE); if (head == std::string::npos) return ""; const auto tail = s.find_last_not_of(whitespace); const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE); return s.substr(head, tail - head + 1); } static std::string trimHeader(const std::string& s) { std::string res = trim(s); std::transform(res.begin(), res.end(), res.begin(), ::tolower); return res; } // This is similiar to Split in android-base/file.h, but it won't add empty string void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters) { static void split(const std::string& line, std::vector<std::string>& words, const trans_func& func, const std::string& delimiters) { words.clear(); // clear the buffer before split size_t base = 0; Loading @@ -40,7 +48,7 @@ void split(const std::string& line, std::vector<std::string>& words, const std:: while (true) { found = line.find_first_of(delimiters, base); if (found != base) { std::string word = trim(line.substr(base, found - base)); std::string word = (*func) (line.substr(base, found - base)); if (!word.empty()) { words.push_back(word); } Loading @@ -50,13 +58,18 @@ void split(const std::string& line, std::vector<std::string>& words, const std:: } } bool assertHeaders(const char* expected[], const std::vector<std::string>& actual) { for (size_t i = 0; i < actual.size(); i++) { if (expected[i] == NULL || std::string(expected[i]) != actual[i]) { return false; header_t parseHeader(const std::string& line, const std::string& delimiters) { header_t header; trans_func f = &trimHeader; split(line, header, f, delimiters); return header; } } return true; record_t parseRecord(const std::string& line, const std::string& delimiters) { record_t record; trans_func f = &trim; split(line, record, f, delimiters); return record; } Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {}; Loading cmds/incident_helper/ih_util.h +3 −6 Original line number Diff line number Diff line Loading @@ -23,16 +23,13 @@ typedef std::vector<std::string> header_t; typedef std::vector<std::string> record_t; typedef std::string (*trans_func) (const std::string&); const char DEFAULT_NEWLINE = '\n'; const std::string DEFAULT_WHITESPACE = " \t"; std::string trim(const std::string& s, const std::string& whitespace = DEFAULT_WHITESPACE); void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters = DEFAULT_WHITESPACE); bool assertHeaders(const char* expected[], const std::vector<std::string>& actual); header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); /** * Reader class reads data from given fd in streaming fashion. Loading cmds/incident_helper/tests/ih_util_test.cpp +22 −9 Original line number Diff line number Diff line Loading @@ -26,25 +26,38 @@ using namespace android::base; using namespace std; using ::testing::StrEq; TEST(IhUtilTest, Trim) { EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw "), StrEq("100 00\toooh \t wqrw")); EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw ", " "), StrEq("\t 100 00\toooh \t wqrw")); TEST(IhUtilTest, ParseHeader) { header_t result, expected; result = parseHeader(" \t \t\t "); EXPECT_EQ(expected, result); result = parseHeader(" \t 100 00\tOpQ \t wqrw"); expected = { "100", "00", "opq", "wqrw" }; EXPECT_EQ(expected, result); result = parseHeader(" \t 100 00\toooh \t wTF", "\t"); expected = { "100 00", "oooh", "wtf" }; EXPECT_EQ(expected, result); result = parseHeader("123,456,78_9", ","); expected = { "123", "456", "78_9" }; EXPECT_EQ(expected, result); } TEST(IhUtilTest, Split) { vector<string> result, expected; split(" \t \t\t ", result); TEST(IhUtilTest, ParseRecord) { record_t result, expected; result = parseRecord(" \t \t\t "); EXPECT_EQ(expected, result); split(" \t 100 00\toooh \t wqrw", result); result = parseRecord(" \t 100 00\toooh \t wqrw"); expected = { "100", "00", "oooh", "wqrw" }; EXPECT_EQ(expected, result); split(" \t 100 00\toooh \t wqrw", result, "\t"); result = parseRecord(" \t 100 00\toooh \t wqrw", "\t"); expected = { "100 00", "oooh", "wqrw" }; EXPECT_EQ(expected, result); split("123,456,78_9", result, ","); result = parseRecord("123,456,78_9", ","); expected = { "123", "456", "78_9" }; EXPECT_EQ(expected, result); } Loading cmds/incidentd/src/Section.cpp +5 −4 Original line number Diff line number Diff line Loading @@ -330,11 +330,13 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const void CommandSection::init(const char* command, va_list args) { va_list copied_args; va_copy(copied_args, args); int numOfArgs = 0; while(va_arg(args, const char*) != NULL) { va_copy(copied_args, args); while(va_arg(copied_args, const char*) != NULL) { numOfArgs++; } va_end(copied_args); // allocate extra 1 for command and 1 for NULL terminator mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2)); Loading @@ -342,13 +344,12 @@ void CommandSection::init(const char* command, va_list args) mCommand[0] = command; name = command; for (int i=0; i<numOfArgs; i++) { const char* arg = va_arg(copied_args, const char*); const char* arg = va_arg(args, const char*); mCommand[i+1] = arg; name += " "; name += arg; } mCommand[numOfArgs+1] = NULL; va_end(copied_args); } CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...) Loading Loading
cmds/incident_helper/IncidentHelper.cpp +44 −60 Original line number Diff line number Diff line Loading @@ -29,8 +29,37 @@ using namespace android::base; using namespace android::os; using namespace google::protobuf; using namespace std; 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, atol(field_value.c_str())); return true; case FieldDescriptor::TYPE_UINT64: reflection->SetUInt64(message, field, atol(field_value.c_str())); return true; case FieldDescriptor::TYPE_INT32: reflection->SetInt32(message, field, atoi(field_value.c_str())); return true; case FieldDescriptor::TYPE_UINT32: reflection->SetUInt32(message, field, atoi(field_value.c_str())); return true; default: // Add new scalar types return false; } } // ================================================================================ status_t ReverseParser::Parse(const int in, const int out) const { Loading @@ -51,31 +80,6 @@ status_t ReverseParser::Parse(const int in, const int out) const // ================================================================================ static const string KERNEL_WAKEUP_LINE_DELIMITER = "\t"; static void SetWakeupSourceField(WakeupSourceProto* source, string name, string value) { if (name == "name") { source->set_name(value.c_str()); } else if (name == "active_count") { source->set_active_count(atoi(value.c_str())); } else if (name == "event_count") { source->set_event_count(atoi(value.c_str())); } else if (name == "wakeup_count") { source->set_wakeup_count(atoi(value.c_str())); } else if (name == "expire_count") { source->set_expire_count(atoi(value.c_str())); } else if (name == "active_count") { source->set_active_since(atol(value.c_str())); } else if (name == "total_time") { source->set_total_time(atol(value.c_str())); } else if (name == "max_time") { source->set_max_time(atol(value.c_str())); } else if (name == "last_change") { source->set_last_change(atol(value.c_str())); } else if (name == "prevent_suspend_time") { source->set_prevent_suspend_time(atol(value.c_str())); } // add new fields } status_t KernelWakesParser::Parse(const int in, const int out) const { Reader reader(in); string line; Loading @@ -90,12 +94,12 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { if (line.empty()) continue; // parse head line if (nline++ == 0) { split(line, header, KERNEL_WAKEUP_LINE_DELIMITER); header = parseHeader(line, KERNEL_WAKEUP_LINE_DELIMITER); continue; } // parse for each record, the line delimiter is \t only! split(line, record, KERNEL_WAKEUP_LINE_DELIMITER); record = parseRecord(line, KERNEL_WAKEUP_LINE_DELIMITER); if (record.size() != header.size()) { // TODO: log this to incident report! Loading @@ -105,7 +109,10 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { WakeupSourceProto* source = proto.add_wakeup_sources(); for (int i=0; i<(int)record.size(); i++) { SetWakeupSourceField(source, header[i], record[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()); } } } Loading @@ -123,32 +130,6 @@ status_t KernelWakesParser::Parse(const int in, const int out) const { } // ================================================================================ // Remove K for numeric fields static void SetProcessField(ProcessProto* process, string name, string value) { ssize_t len = value.size(); if (name == "PID") { process->set_pid(atoi(value.c_str())); } else if (name == "Vss") { process->set_vss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Rss") { process->set_rss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Pss") { process->set_pss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Uss") { process->set_uss(atol(value.substr(0, len - 1).c_str())); } else if (name == "Swap") { process->set_swap(atol(value.substr(0, len - 1).c_str())); } else if (name == "PSwap") { process->set_pswap(atol(value.substr(0, len - 1).c_str())); } else if (name == "USwap") { process->set_uswap(atol(value.substr(0, len - 1).c_str())); } else if (name == "ZSwap") { process->set_zswap(atol(value.substr(0, len - 1).c_str())); } else if (name == "cmdline") { process->set_cmdline(value); } } status_t ProcrankParser::Parse(const int in, const int out) const { Reader reader(in); string line, content; Loading @@ -164,22 +145,22 @@ status_t ProcrankParser::Parse(const int in, const int out) const { // parse head line if (nline++ == 0) { split(line, header); header = parseHeader(line); continue; } split(line, record); 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++) { SetProcessField(total, header[header.size() - i], record[record.size() - i]); SetTableField(total, header[header.size() - i], record[record.size() - i]); } } else if (record[0] == "ZRAM:") { split(line, record, ":"); record = parseRecord(line, ":"); proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]); } else if (record[0] == "RAM:") { split(line, record, ":"); record = parseRecord(line, ":"); proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]); } else { fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, Loading @@ -190,7 +171,10 @@ status_t ProcrankParser::Parse(const int in, const int out) const { ProcessProto* process = proto.add_processes(); for (int i=0; i<(int)record.size(); i++) { SetProcessField(process, header[i], record[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()); } } } Loading
cmds/incident_helper/ih_util.cpp +25 −12 Original line number Diff line number Diff line Loading @@ -23,16 +23,24 @@ const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB std::string trim(const std::string& s, const std::string& whitespace) { const auto head = s.find_first_not_of(whitespace); static std::string trim(const std::string& s) { const auto head = s.find_first_not_of(DEFAULT_WHITESPACE); if (head == std::string::npos) return ""; const auto tail = s.find_last_not_of(whitespace); const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE); return s.substr(head, tail - head + 1); } static std::string trimHeader(const std::string& s) { std::string res = trim(s); std::transform(res.begin(), res.end(), res.begin(), ::tolower); return res; } // This is similiar to Split in android-base/file.h, but it won't add empty string void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters) { static void split(const std::string& line, std::vector<std::string>& words, const trans_func& func, const std::string& delimiters) { words.clear(); // clear the buffer before split size_t base = 0; Loading @@ -40,7 +48,7 @@ void split(const std::string& line, std::vector<std::string>& words, const std:: while (true) { found = line.find_first_of(delimiters, base); if (found != base) { std::string word = trim(line.substr(base, found - base)); std::string word = (*func) (line.substr(base, found - base)); if (!word.empty()) { words.push_back(word); } Loading @@ -50,13 +58,18 @@ void split(const std::string& line, std::vector<std::string>& words, const std:: } } bool assertHeaders(const char* expected[], const std::vector<std::string>& actual) { for (size_t i = 0; i < actual.size(); i++) { if (expected[i] == NULL || std::string(expected[i]) != actual[i]) { return false; header_t parseHeader(const std::string& line, const std::string& delimiters) { header_t header; trans_func f = &trimHeader; split(line, header, f, delimiters); return header; } } return true; record_t parseRecord(const std::string& line, const std::string& delimiters) { record_t record; trans_func f = &trim; split(line, record, f, delimiters); return record; } Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {}; Loading
cmds/incident_helper/ih_util.h +3 −6 Original line number Diff line number Diff line Loading @@ -23,16 +23,13 @@ typedef std::vector<std::string> header_t; typedef std::vector<std::string> record_t; typedef std::string (*trans_func) (const std::string&); const char DEFAULT_NEWLINE = '\n'; const std::string DEFAULT_WHITESPACE = " \t"; std::string trim(const std::string& s, const std::string& whitespace = DEFAULT_WHITESPACE); void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters = DEFAULT_WHITESPACE); bool assertHeaders(const char* expected[], const std::vector<std::string>& actual); header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE); /** * Reader class reads data from given fd in streaming fashion. Loading
cmds/incident_helper/tests/ih_util_test.cpp +22 −9 Original line number Diff line number Diff line Loading @@ -26,25 +26,38 @@ using namespace android::base; using namespace std; using ::testing::StrEq; TEST(IhUtilTest, Trim) { EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw "), StrEq("100 00\toooh \t wqrw")); EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw ", " "), StrEq("\t 100 00\toooh \t wqrw")); TEST(IhUtilTest, ParseHeader) { header_t result, expected; result = parseHeader(" \t \t\t "); EXPECT_EQ(expected, result); result = parseHeader(" \t 100 00\tOpQ \t wqrw"); expected = { "100", "00", "opq", "wqrw" }; EXPECT_EQ(expected, result); result = parseHeader(" \t 100 00\toooh \t wTF", "\t"); expected = { "100 00", "oooh", "wtf" }; EXPECT_EQ(expected, result); result = parseHeader("123,456,78_9", ","); expected = { "123", "456", "78_9" }; EXPECT_EQ(expected, result); } TEST(IhUtilTest, Split) { vector<string> result, expected; split(" \t \t\t ", result); TEST(IhUtilTest, ParseRecord) { record_t result, expected; result = parseRecord(" \t \t\t "); EXPECT_EQ(expected, result); split(" \t 100 00\toooh \t wqrw", result); result = parseRecord(" \t 100 00\toooh \t wqrw"); expected = { "100", "00", "oooh", "wqrw" }; EXPECT_EQ(expected, result); split(" \t 100 00\toooh \t wqrw", result, "\t"); result = parseRecord(" \t 100 00\toooh \t wqrw", "\t"); expected = { "100 00", "oooh", "wqrw" }; EXPECT_EQ(expected, result); split("123,456,78_9", result, ","); result = parseRecord("123,456,78_9", ","); expected = { "123", "456", "78_9" }; EXPECT_EQ(expected, result); } Loading
cmds/incidentd/src/Section.cpp +5 −4 Original line number Diff line number Diff line Loading @@ -330,11 +330,13 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const void CommandSection::init(const char* command, va_list args) { va_list copied_args; va_copy(copied_args, args); int numOfArgs = 0; while(va_arg(args, const char*) != NULL) { va_copy(copied_args, args); while(va_arg(copied_args, const char*) != NULL) { numOfArgs++; } va_end(copied_args); // allocate extra 1 for command and 1 for NULL terminator mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2)); Loading @@ -342,13 +344,12 @@ void CommandSection::init(const char* command, va_list args) mCommand[0] = command; name = command; for (int i=0; i<numOfArgs; i++) { const char* arg = va_arg(copied_args, const char*); const char* arg = va_arg(args, const char*); mCommand[i+1] = arg; name += " "; name += arg; } mCommand[numOfArgs+1] = NULL; va_end(copied_args); } CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...) Loading