blob: 7b06d42cbb551737adad504240321cd1e707b58c [file] [log] [blame]
Yi Jin0a3406f2017-06-22 19:23:11 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "incident_helper"
18
19#include "IncidentHelper.h"
Yi Jinb44f7d42017-07-21 12:12:59 -070020#include "ih_util.h"
Yi Jin0a3406f2017-06-22 19:23:11 -070021
22#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
Yi Jin810b14f2017-09-11 19:01:08 -070023#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
Yi Jinb44f7d42017-07-21 12:12:59 -070024#include "frameworks/base/core/proto/android/os/procrank.pb.h"
Yi Jin0a3406f2017-06-22 19:23:11 -070025
Yi Jin0a3406f2017-06-22 19:23:11 -070026#include <android-base/file.h>
27#include <unistd.h>
Yi Jin0a3406f2017-06-22 19:23:11 -070028#include <string>
29#include <vector>
30
31using namespace android::base;
32using namespace android::os;
Yi Jin4ef28b72017-08-14 14:45:28 -070033using namespace google::protobuf;
Yi Jin0a3406f2017-06-22 19:23:11 -070034using namespace std;
35
Yi Jin810b14f2017-09-11 19:01:08 -070036
37static const string TAB_DELIMITER = "\t";
38static const string COMMA_DELIMITER = ",";
39
40static inline int toInt(const string& s) {
41 return atoi(s.c_str());
42}
43
44static inline long toLong(const string& s) {
45 return atol(s.c_str());
46}
47
48/**
49 * Sets the given protobuf message when the field name matches one of the
50 * fields. It is useful to set values to proto from table-like plain texts.
51 */
Yi Jin4ef28b72017-08-14 14:45:28 -070052static bool
53SetTableField(::google::protobuf::Message* message, string field_name, string field_value) {
54 const Descriptor* descriptor = message->GetDescriptor();
55 const Reflection* reflection = message->GetReflection();
56
57 const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
58 switch (field->type()) {
59 case FieldDescriptor::TYPE_STRING:
60 reflection->SetString(message, field, field_value);
61 return true;
62 case FieldDescriptor::TYPE_INT64:
Yi Jin810b14f2017-09-11 19:01:08 -070063 reflection->SetInt64(message, field, toLong(field_value));
Yi Jin4ef28b72017-08-14 14:45:28 -070064 return true;
65 case FieldDescriptor::TYPE_UINT64:
Yi Jin810b14f2017-09-11 19:01:08 -070066 reflection->SetUInt64(message, field, toLong(field_value));
Yi Jin4ef28b72017-08-14 14:45:28 -070067 return true;
68 case FieldDescriptor::TYPE_INT32:
Yi Jin810b14f2017-09-11 19:01:08 -070069 reflection->SetInt32(message, field, toInt(field_value));
Yi Jin4ef28b72017-08-14 14:45:28 -070070 return true;
71 case FieldDescriptor::TYPE_UINT32:
Yi Jin810b14f2017-09-11 19:01:08 -070072 reflection->SetUInt32(message, field, toInt(field_value));
Yi Jin4ef28b72017-08-14 14:45:28 -070073 return true;
74 default:
75 // Add new scalar types
76 return false;
77 }
78}
79
Yi Jin0a3406f2017-06-22 19:23:11 -070080// ================================================================================
Yi Jin99c248f2017-08-25 18:11:58 -070081status_t NoopParser::Parse(const int in, const int out) const
82{
83 string content;
84 if (!ReadFdToString(in, &content)) {
85 fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
86 return -1;
87 }
88 if (!WriteStringToFd(content, out)) {
89 fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
90 return -1;
91 }
92 return NO_ERROR;
93}
94
95// ================================================================================
Yi Jin0a3406f2017-06-22 19:23:11 -070096status_t ReverseParser::Parse(const int in, const int out) const
97{
98 string content;
99 if (!ReadFdToString(in, &content)) {
100 fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
101 return -1;
102 }
103 // reverse the content
104 reverse(content.begin(), content.end());
105 if (!WriteStringToFd(content, out)) {
106 fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
107 return -1;
108 }
109 return NO_ERROR;
110}
111
112// ================================================================================
Yi Jin0a3406f2017-06-22 19:23:11 -0700113status_t KernelWakesParser::Parse(const int in, const int out) const {
Yi Jinb44f7d42017-07-21 12:12:59 -0700114 Reader reader(in);
Yi Jin0a3406f2017-06-22 19:23:11 -0700115 string line;
Yi Jinb44f7d42017-07-21 12:12:59 -0700116 header_t header; // the header of /d/wakeup_sources
117 record_t record; // retain each record
Yi Jin0a3406f2017-06-22 19:23:11 -0700118 int nline = 0;
119
120 KernelWakeSources proto;
121
122 // parse line by line
Yi Jin810b14f2017-09-11 19:01:08 -0700123 while (reader.readLine(&line)) {
Yi Jinb44f7d42017-07-21 12:12:59 -0700124 if (line.empty()) continue;
Yi Jin0a3406f2017-06-22 19:23:11 -0700125 // parse head line
Yi Jinb44f7d42017-07-21 12:12:59 -0700126 if (nline++ == 0) {
Yi Jin810b14f2017-09-11 19:01:08 -0700127 header = parseHeader(line, TAB_DELIMITER);
Yi Jinb44f7d42017-07-21 12:12:59 -0700128 continue;
Yi Jin0a3406f2017-06-22 19:23:11 -0700129 }
130
131 // parse for each record, the line delimiter is \t only!
Yi Jin810b14f2017-09-11 19:01:08 -0700132 record = parseRecord(line, TAB_DELIMITER);
Yi Jin0a3406f2017-06-22 19:23:11 -0700133
134 if (record.size() != header.size()) {
Yi Jinb44f7d42017-07-21 12:12:59 -0700135 // TODO: log this to incident report!
136 fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
137 continue;
Yi Jin0a3406f2017-06-22 19:23:11 -0700138 }
139
140 WakeupSourceProto* source = proto.add_wakeup_sources();
Yi Jin603f3b32017-08-03 18:50:48 -0700141 for (int i=0; i<(int)record.size(); i++) {
Yi Jin4ef28b72017-08-14 14:45:28 -0700142 if (!SetTableField(source, header[i], record[i])) {
143 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
144 this->name.string(), nline, header[i].c_str(), record[i].c_str());
145 }
Yi Jin603f3b32017-08-03 18:50:48 -0700146 }
Yi Jin0a3406f2017-06-22 19:23:11 -0700147 }
148
Yi Jin810b14f2017-09-11 19:01:08 -0700149 if (!reader.ok(&line)) {
Yi Jinb44f7d42017-07-21 12:12:59 -0700150 fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
151 return -1;
152 }
Yi Jin0a3406f2017-06-22 19:23:11 -0700153
154 if (!proto.SerializeToFileDescriptor(out)) {
155 fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
156 return -1;
157 }
Yi Jinb44f7d42017-07-21 12:12:59 -0700158 fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
Yi Jin0a3406f2017-06-22 19:23:11 -0700159 return NO_ERROR;
160}
Yi Jinb44f7d42017-07-21 12:12:59 -0700161
162// ================================================================================
Yi Jinb44f7d42017-07-21 12:12:59 -0700163status_t ProcrankParser::Parse(const int in, const int out) const {
164 Reader reader(in);
Yi Jin810b14f2017-09-11 19:01:08 -0700165 string line;
Yi Jinb44f7d42017-07-21 12:12:59 -0700166 header_t header; // the header of /d/wakeup_sources
167 record_t record; // retain each record
168 int nline = 0;
169
170 Procrank proto;
171
172 // parse line by line
Yi Jin810b14f2017-09-11 19:01:08 -0700173 while (reader.readLine(&line)) {
Yi Jinb44f7d42017-07-21 12:12:59 -0700174 if (line.empty()) continue;
175
176 // parse head line
177 if (nline++ == 0) {
Yi Jin4ef28b72017-08-14 14:45:28 -0700178 header = parseHeader(line);
Yi Jinb44f7d42017-07-21 12:12:59 -0700179 continue;
180 }
181
Yi Jin810b14f2017-09-11 19:01:08 -0700182 if (hasPrefix(&line, "ZRAM:")) {
183 proto.mutable_summary()->mutable_zram()->set_raw_text(line);
184 continue;
185 }
186 if (hasPrefix(&line, "RAM:")) {
187 proto.mutable_summary()->mutable_ram()->set_raw_text(line);
188 continue;
189 }
190
Yi Jin4ef28b72017-08-14 14:45:28 -0700191 record = parseRecord(line);
Yi Jinb44f7d42017-07-21 12:12:59 -0700192 if (record.size() != header.size()) {
193 if (record[record.size() - 1] == "TOTAL") { // TOTAL record
194 ProcessProto* total = proto.mutable_summary()->mutable_total();
Yi Jin603f3b32017-08-03 18:50:48 -0700195 for (int i=1; i<=(int)record.size(); i++) {
Yi Jin4ef28b72017-08-14 14:45:28 -0700196 SetTableField(total, header[header.size() - i], record[record.size() - i]);
Yi Jin603f3b32017-08-03 18:50:48 -0700197 }
Yi Jinb44f7d42017-07-21 12:12:59 -0700198 } else {
199 fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
200 line.c_str());
201 }
202 continue;
203 }
204
205 ProcessProto* process = proto.add_processes();
Yi Jin603f3b32017-08-03 18:50:48 -0700206 for (int i=0; i<(int)record.size(); i++) {
Yi Jin4ef28b72017-08-14 14:45:28 -0700207 if (!SetTableField(process, header[i], record[i])) {
208 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
209 this->name.string(), nline, header[i].c_str(), record[i].c_str());
210 }
Yi Jin603f3b32017-08-03 18:50:48 -0700211 }
Yi Jinb44f7d42017-07-21 12:12:59 -0700212 }
213
Yi Jin810b14f2017-09-11 19:01:08 -0700214 if (!reader.ok(&line)) {
Yi Jinb44f7d42017-07-21 12:12:59 -0700215 fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
216 return -1;
217 }
218
219 if (!proto.SerializeToFileDescriptor(out)) {
220 fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
221 return -1;
222 }
223 fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
224 return NO_ERROR;
Yi Jin99c248f2017-08-25 18:11:58 -0700225}
Yi Jin810b14f2017-09-11 19:01:08 -0700226
227// ================================================================================
228status_t PageTypeInfoParser::Parse(const int in, const int out) const {
229 Reader reader(in);
230 string line;
231 bool migrateTypeSession = false;
232 int pageBlockOrder;
233 header_t blockHeader;
234
235 PageTypeInfo pageTypeInfo;
236
237 while (reader.readLine(&line)) {
238 if (line.empty()) {
239 migrateTypeSession = false;
240 blockHeader.clear();
241 continue;
242 }
243
244 if (hasPrefix(&line, "Page block order:")) {
245 pageBlockOrder = toInt(line);
246 pageTypeInfo.set_page_block_order(pageBlockOrder);
247 continue;
248 }
249 if (hasPrefix(&line, "Pages per block:")) {
250 pageTypeInfo.set_pages_per_block(toInt(line));
251 continue;
252 }
253 if (hasPrefix(&line, "Free pages count per migrate type at order")) {
254 migrateTypeSession = true;
255 continue;
256 }
257 if (hasPrefix(&line, "Number of blocks type")) {
258 blockHeader = parseHeader(line);
259 continue;
260 }
261
262 record_t record = parseRecord(line, COMMA_DELIMITER);
263 if (migrateTypeSession && record.size() == 3) {
264 MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types();
265 // expect part 0 starts with "Node"
266 if (hasPrefix(&record[0], "Node")) {
267 migrateType->set_node(toInt(record[0]));
268 } else goto ERROR;
269 // expect part 1 starts with "zone"
270 if (hasPrefix(&record[1], "zone")) {
271 migrateType->set_zone(record[1]);
272 } else goto ERROR;
273 // expect part 2 starts with "type"
274 if (hasPrefix(&record[2], "type")) {
275 // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
276 // An example looks like:
277 // header line: type 0 1 2 3 4 5 6 7 8 9 10
278 // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
279 // The pageBlockOrder = 10 and it's zero-indexed. so total parts
280 // are 10 + 1(zero-indexed) + 1(the type part) = 12.
281 record_t pageCounts = parseRecord(record[2]);
282 int pageCountsSize = pageBlockOrder + 2;
283 if ((int)pageCounts.size() != pageCountsSize) goto ERROR;
284
285 migrateType->set_type(pageCounts[0]);
286 for (auto i=1; i<pageCountsSize; i++) {
287 migrateType->add_free_pages_count(toInt(pageCounts[i]));
288 }
289 } else goto ERROR;
290 continue;
291 }
292
293 if (!blockHeader.empty() && record.size() == 2) {
294 BlockProto* block = pageTypeInfo.add_blocks();
295
296 if (hasPrefix(&record[0], "Node")) {
297 block->set_node(toInt(record[0]));
298 } else goto ERROR;
299
300 if (hasPrefix(&record[1], "zone")) {
301 record_t blockCounts = parseRecord(record[1]);
302 block->set_zone(blockCounts[0]);
303 for (size_t i=0; i<blockHeader.size(); i++) {
304 if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR;
305 }
306 } else goto ERROR;
307
308 continue;
309 }
310
311ERROR: // print out error for this single line and continue parsing
312 fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str());
313 }
314
315 if (!reader.ok(&line)) {
316 fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
317 return -1;
318 }
319
320 if (!pageTypeInfo.SerializeToFileDescriptor(out)) {
321 fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
322 return -1;
323 }
324
325 fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize());
326 return NO_ERROR;
327}