summaryrefslogtreecommitdiff
path: root/cmds/incident_helper/src
diff options
context:
space:
mode:
authorKweku Adams <kwekua@google.com>2017-12-20 17:59:17 -0800
committerKweku Adams <kwekua@google.com>2018-01-02 15:49:23 -0800
commitf5cc5759d55f803cd230c7a595e89e634c3c36ee (patch)
tree7632d267b45d11c144fc54e411f2151e71671176 /cmds/incident_helper/src
parentb944bc86078146c523d58d2b70d56044be3bf216 (diff)
incidentd: parsing ps dump into proto.
Also changing from execv to execvp so that we don't have to specify the full command path. Bug: 65750831 Test: atest incident_helper_test Change-Id: I92191afff4e7f9a6d08ea22ecfc2de5623d3bde5
Diffstat (limited to 'cmds/incident_helper/src')
-rw-r--r--cmds/incident_helper/src/ih_util.cpp75
-rw-r--r--cmds/incident_helper/src/ih_util.h11
-rw-r--r--cmds/incident_helper/src/main.cpp3
-rw-r--r--cmds/incident_helper/src/parsers/CpuInfoParser.cpp29
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.cpp6
-rw-r--r--cmds/incident_helper/src/parsers/PsParser.cpp95
-rw-r--r--cmds/incident_helper/src/parsers/PsParser.h33
7 files changed, 233 insertions, 19 deletions
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 4bf956a9a03d..e23e80ae21e8 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -52,6 +52,12 @@ static inline std::string trimHeader(const std::string& s) {
return toLowerStr(trimDefault(s));
}
+static inline bool isNumber(const std::string& s) {
+ std::string::const_iterator it = s.begin();
+ while (it != s.end() && std::isdigit(*it)) ++it;
+ return !s.empty() && it == s.end();
+}
+
// This is similiar to Split in android-base/file.h, but it won't add empty string
static void split(const std::string& line, std::vector<std::string>& words,
const trans_func& func, const std::string& delimiters) {
@@ -86,24 +92,80 @@ record_t parseRecord(const std::string& line, const std::string& delimiters) {
return record;
}
+bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) {
+ indices.clear();
+
+ size_t lastIndex = 0;
+ int i = 0;
+ while (headerNames[i] != NULL) {
+ string s = headerNames[i];
+ lastIndex = line.find(s, lastIndex);
+ if (lastIndex == string::npos) {
+ fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
+ return false;
+ }
+ lastIndex += s.length();
+ indices.push_back(lastIndex);
+ i++;
+ }
+
+ return true;
+}
+
record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
record_t record;
int lastIndex = 0;
+ int lastBeginning = 0;
int lineSize = (int)line.size();
for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
int idx = *it;
- if (lastIndex > idx || idx > lineSize) {
- record.clear(); // The indices is wrong, return empty;
+ if (idx <= lastIndex) {
+ // We saved up until lastIndex last time, so we should start at
+ // lastIndex + 1 this time.
+ idx = lastIndex + 1;
+ }
+ if (idx > lineSize) {
+ if (lastIndex < idx && lastIndex < lineSize) {
+ // There's a little bit more for us to save, which we'll do
+ // outside of the loop.
+ break;
+ }
+ // If we're past the end of the line AND we've already saved everything up to the end.
+ fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize);
+ record.clear(); // The indices are wrong, return empty.
return record;
}
while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
+ lastBeginning = lastIndex;
lastIndex = idx;
}
- record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex)));
+ if (lineSize - lastIndex > 0) {
+ int beginning = lastIndex;
+ if (record.size() == indices.size()) {
+ // We've already encountered all of the columns...put whatever is
+ // left in the last column.
+ record.pop_back();
+ beginning = lastBeginning;
+ }
+ record.push_back(trimDefault(line.substr(beginning, lineSize - beginning)));
+ }
return record;
}
+void printRecord(const record_t& record) {
+ fprintf(stderr, "Record: { ");
+ if (record.size() == 0) {
+ fprintf(stderr, "}\n");
+ return;
+ }
+ for(size_t i = 0; i < record.size(); ++i) {
+ if(i != 0) fprintf(stderr, "\", ");
+ fprintf(stderr, "\"%s", record[i].c_str());
+ }
+ fprintf(stderr, "\" }\n");
+}
+
bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
if (head == std::string::npos) return false;
@@ -210,7 +272,10 @@ Table::~Table()
void
Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
{
- if (mFields.find(field) == mFields.end()) return;
+ if (mFields.find(field) == mFields.end()) {
+ fprintf(stderr, "Field '%s' not found", string(field).c_str());
+ return;
+ }
map<std::string, int> enu;
for (int i = 0; i < enumSize; i++) {
@@ -268,6 +333,8 @@ Table::insertField(ProtoOutputStream* proto, const std::string& name, const std:
}
} else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
proto->write(found, mEnumValuesByName[value]);
+ } else if (isNumber(value)) {
+ proto->write(found, toInt(value));
} else {
return false;
}
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 58ef29044048..b063b2fe0bba 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -56,12 +56,23 @@ header_t parseHeader(const std::string& line, const std::string& delimiters = DE
record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
/**
+ * Gets the list of end indices of each word in the line and places it in the given vector,
+ * clearing out the vector beforehand. These indices can be used with parseRecordByColumns.
+ * Will return false if there was a problem getting the indices. headerNames
+ * must be NULL terminated.
+ */
+bool getColumnIndices(std::vector<int>& indices, const char* headerNames[], const std::string& line);
+
+/**
* When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters.
* This function allows to parse record by its header's column position' indices, must in ascending order.
* At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters.
*/
record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE);
+/** Prints record_t to stderr */
+void printRecord(const record_t& record);
+
/**
* When the line starts/ends with the given key, the function returns true
* as well as the line argument is changed to the rest trimmed part of the original.
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index ab92473b8ba3..8c6cd78d3bf2 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -22,6 +22,7 @@
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
#include "parsers/ProcrankParser.h"
+#include "parsers/PsParser.h"
#include "parsers/SystemPropertiesParser.h"
#include <android-base/file.h>
@@ -64,6 +65,8 @@ static TextParserBase* selectParser(int section) {
return new CpuInfoParser();
case 2004:
return new CpuFreqParser();
+ case 2005:
+ return new PsParser();
case 2006:
return new BatteryTypeParser();
default:
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
index 3faca00c1b88..d73de54d8c5d 100644
--- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -49,6 +49,7 @@ CpuInfoParser::Parse(const int in, const int out) const
vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
record_t record;
int nline = 0;
+ int diff = 0;
bool nextToSwap = false;
bool nextToUsage = false;
@@ -107,18 +108,10 @@ CpuInfoParser::Parse(const int in, const int out) const
header = parseHeader(line, "[ %]");
nextToUsage = false;
- // NAME is not in the list since the last split index is default to the end of line.
- const char* headerNames[11] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD" };
- size_t lastIndex = 0;
- for (int i = 0; i < 11; i++) {
- string s = headerNames[i];
- lastIndex = line.find(s, lastIndex);
- if (lastIndex == string::npos) {
- fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
- return -1;
- }
- lastIndex += s.length();
- columnIndices.push_back(lastIndex);
+ // NAME is not in the list since we need to modify the end of the CMD index.
+ const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", NULL };
+ if (!getColumnIndices(columnIndices, headerNames, line)) {
+ return -1;
}
// Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
// for example: ... CMD NAME
@@ -128,12 +121,20 @@ CpuInfoParser::Parse(const int in, const int out) const
int endCMD = columnIndices.back();
columnIndices.pop_back();
columnIndices.push_back(line.find("NAME", endCMD) - 1);
+ // Add NAME index to complete the column list.
+ columnIndices.push_back(columnIndices.back() + 4);
continue;
}
record = parseRecordByColumns(line, columnIndices);
- if (record.size() != header.size()) {
- fprintf(stderr, "[%s]Line %d has missing fields:\n%s\n", this->name.string(), nline, line.c_str());
+ diff = record.size() - header.size();
+ if (diff < 0) {
+ fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
+ printRecord(record);
+ continue;
+ } else if (diff > 0) {
+ fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
+ printRecord(record);
continue;
}
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index ada4a5d0ffe2..cae51abbe57f 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -47,10 +47,14 @@ KernelWakesParser::Parse(const int in, const int out) const
// parse for each record, the line delimiter is \t only!
record = parseRecord(line, TAB_DELIMITER);
- if (record.size() != header.size()) {
+ 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;
+ } else if (record.size() > header.size()) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has extra fields\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
}
long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp
new file mode 100644
index 000000000000..e9014cacfa0b
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PsParser.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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/ps.proto.h"
+#include "ih_util.h"
+#include "PsParser.h"
+
+using namespace android::os;
+
+status_t PsParser::Parse(const int in, const int out) const {
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
+ record_t record; // retain each record
+ int nline = 0;
+ int diff = 0;
+
+ ProtoOutputStream proto;
+ Table table(PsDumpProto::Process::_FIELD_NAMES, PsDumpProto::Process::_FIELD_IDS, PsDumpProto::Process::_FIELD_COUNT);
+ const char* pcyNames[] = { "fg", "bg", "ta" };
+ const int pcyValues[] = {PsDumpProto::Process::POLICY_FG, PsDumpProto::Process::POLICY_BG, PsDumpProto::Process::POLICY_TA};
+ table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3);
+ const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" };
+ const int sValues[] = {PsDumpProto::Process::STATE_D, PsDumpProto::Process::STATE_R, PsDumpProto::Process::STATE_S, PsDumpProto::Process::STATE_T, PsDumpProto::Process::STATE_TRACING, PsDumpProto::Process::STATE_X, PsDumpProto::Process::STATE_Z};
+ table.addEnumTypeMap("s", sNames, sValues, 7);
+
+ // Parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ if (nline++ == 0) {
+ header = parseHeader(line, DEFAULT_WHITESPACE);
+
+ const char* headerNames[] = { "LABEL", "USER", "PID", "TID", "PPID", "VSZ", "RSS", "WCHAN", "ADDR", "S", "PRI", "NI", "RTPRIO", "SCH", "PCY", "TIME", "CMD", NULL };
+ if (!getColumnIndices(columnIndices, headerNames, line)) {
+ return -1;
+ }
+
+ continue;
+ }
+
+ record = parseRecordByColumns(line, columnIndices);
+
+ diff = record.size() - header.size();
+ if (diff < 0) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
+ printRecord(record);
+ continue;
+ } else if (diff > 0) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
+ printRecord(record);
+ continue;
+ }
+
+ long long token = proto.start(PsDumpProto::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);
+ }
+
+ 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/PsParser.h b/cmds/incident_helper/src/parsers/PsParser.h
new file mode 100644
index 000000000000..9488e40e88fe
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PsParser.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 PS_PARSER_H
+#define PS_PARSER_H
+
+#include "TextParserBase.h"
+
+/**
+ * PS parser, parses output of 'ps' command to protobuf.
+ */
+class PsParser : public TextParserBase {
+public:
+ PsParser() : TextParserBase(String8("Ps")) {};
+ ~PsParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PS_PARSER_H