This cl does the following things:
0) Implements a skeleton of incident_helper
1) Implements FileSection class which calls incident_helper to parse
file content to protobuf
2) Adds Kernel Wake Sources to incident.proto and makes it parsed by
FileSection
3) Adds basic gtests to test FdBuffer, io_utils, FileSection
implementation
Bug: 62923266
Bug: 62926061
Test: manual - push incidentd, incident_helper and incident to my device
and verify kernel wakeup sources file is able to be parsed.
Change-Id: I2aa6b6158d962ce70e6fa6c8a9c42213a45ff41c
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
new file mode 100644
index 0000000..89d8947
--- /dev/null
+++ b/cmds/incident_helper/IncidentHelper.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "strutil.h"
+
+#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
+
+#include <algorithm>
+#include <android-base/file.h>
+#include <unistd.h>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+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;
+}
+
+// ================================================================================
+// This list must be in order and sync with kernelwake.proto
+const char* kernel_wake_headers[] = {
+ "name", // id: 1
+ "active_count", // id: 2
+ "event_count", // id: 3
+ "wakeup_count", // id: 4
+ "expire_count", // id: 5
+ "active_since", // id: 6
+ "total_time", // id: 7
+ "max_time", // id: 8
+ "last_change", // id: 9
+ "prevent_suspend_time", // id: 10
+};
+
+const string KERNEL_WAKEUP_LINE_DELIMITER = "\t";
+
+status_t KernelWakesParser::Parse(const int in, const int out) const {
+ // read the content, this is not memory-efficient though since it loads everything
+ // However the data will be held in proto anyway, and incident_helper is less critical
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+
+ istringstream iss(content);
+ string line;
+ vector<string> header; // the header of /d/wakeup_sources
+ vector<string> record; // retain each record
+ int nline = 0;
+
+ KernelWakeSources proto;
+
+ // parse line by line
+ while (getline(iss, line)) {
+ // parse head line
+ if (nline == 0) {
+ split(line, &header);
+ if (!assertHeaders(kernel_wake_headers, header)) {
+ fprintf(stderr, "[%s]Bad header:\n%s\n", this->name.string(), line.c_str());
+ return BAD_VALUE;
+ }
+ nline++;
+ continue;
+ }
+
+ // parse for each record, the line delimiter is \t only!
+ split(line, &record, KERNEL_WAKEUP_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;
+ }
+
+ WakeupSourceProto* source = proto.add_wakeup_sources();
+ source->set_name(record.at(0).c_str());
+ // below are int32
+ source->set_active_count(atoi(record.at(1).c_str()));
+ source->set_event_count(atoi(record.at(2).c_str()));
+ source->set_wakeup_count(atoi(record.at(3).c_str()));
+ source->set_expire_count(atoi(record.at(4).c_str()));
+ // below are int64
+ source->set_active_since(atol(record.at(5).c_str()));
+ source->set_total_time(atol(record.at(6).c_str()));
+ source->set_max_time(atol(record.at(7).c_str()));
+ source->set_last_change(atol(record.at(8).c_str()));
+ source->set_prevent_suspend_time(atol(record.at(9).c_str()));
+
+ nline++;
+ }
+
+ fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
+
+ if (!proto.SerializeToFileDescriptor(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ close(out);
+
+ return NO_ERROR;
+}