• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #define LOG_TAG "incident_helper"
17 
18 #include <android/util/ProtoOutputStream.h>
19 
20 #include "frameworks/base/core/proto/android/os/cpuinfo.proto.h"
21 #include "ih_util.h"
22 #include "CpuInfoParser.h"
23 
24 using namespace android::os;
25 
writeSuffixLine(ProtoOutputStream * proto,uint64_t fieldId,const string & line,const string & delimiter,const int count,const char * names[],const uint64_t ids[])26 static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId,
27         const string& line, const string& delimiter,
28         const int count, const char* names[], const uint64_t ids[])
29 {
30     record_t record = parseRecord(line, delimiter);
31     uint64_t token = proto->start(fieldId);
32     for (int i=0; i<(int)record.size(); i++) {
33         for (int j=0; j<count; j++) {
34             if (stripSuffix(&record[i], names[j], true)) {
35                 proto->write(ids[j], toInt(record[i]));
36                 break;
37             }
38         }
39     }
40     proto->end(token);
41 }
42 
43 status_t
Parse(const int in,const int out) const44 CpuInfoParser::Parse(const int in, const int out) const
45 {
46     Reader reader(in);
47     string line;
48     header_t header;
49     vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
50     record_t record;
51     int nline = 0;
52     int diff = 0;
53     bool nextToSwap = false;
54     bool nextToUsage = false;
55 
56     ProtoOutputStream proto;
57     Table table(CpuInfoProto::Task::_FIELD_NAMES, CpuInfoProto::Task::_FIELD_IDS, CpuInfoProto::Task::_FIELD_COUNT);
58     table.addEnumTypeMap("s", CpuInfoProto::Task::_ENUM_STATUS_NAMES,
59             CpuInfoProto::Task::_ENUM_STATUS_VALUES, CpuInfoProto::Task::_ENUM_STATUS_COUNT);
60     table.addEnumTypeMap("pcy", CpuInfoProto::Task::_ENUM_POLICY_NAMES,
61             CpuInfoProto::Task::_ENUM_POLICY_VALUES, CpuInfoProto::Task::_ENUM_POLICY_COUNT);
62 
63     // parse line by line
64     while (reader.readLine(&line)) {
65         if (line.empty()) continue;
66 
67         nline++;
68 
69         if (stripPrefix(&line, "Tasks:")) {
70             writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER,
71                 CpuInfoProto::TaskStats::_FIELD_COUNT,
72                 CpuInfoProto::TaskStats::_FIELD_NAMES,
73                 CpuInfoProto::TaskStats::_FIELD_IDS);
74             continue;
75         }
76         if (stripPrefix(&line, "Mem:")) {
77             writeSuffixLine(&proto, CpuInfoProto::MEM, line, COMMA_DELIMITER,
78                 CpuInfoProto::MemStats::_FIELD_COUNT,
79                 CpuInfoProto::MemStats::_FIELD_NAMES,
80                 CpuInfoProto::MemStats::_FIELD_IDS);
81             continue;
82         }
83         if (stripPrefix(&line, "Swap:")) {
84             writeSuffixLine(&proto, CpuInfoProto::SWAP, line, COMMA_DELIMITER,
85                 CpuInfoProto::MemStats::_FIELD_COUNT,
86                 CpuInfoProto::MemStats::_FIELD_NAMES,
87                 CpuInfoProto::MemStats::_FIELD_IDS);
88             nextToSwap = true;
89             continue;
90         }
91 
92         if (nextToSwap) {
93             writeSuffixLine(&proto, CpuInfoProto::CPU_USAGE, line, DEFAULT_WHITESPACE,
94                 CpuInfoProto::CpuUsage::_FIELD_COUNT,
95                 CpuInfoProto::CpuUsage::_FIELD_NAMES,
96                 CpuInfoProto::CpuUsage::_FIELD_IDS);
97             nextToUsage = true;
98             nextToSwap = false;
99             continue;
100         }
101 
102         // Header of tasks must be next to usage line
103         if (nextToUsage) {
104             // How to parse Header of Tasks:
105             // PID   TID USER         PR  NI[%CPU]S VIRT  RES PCY CMD             NAME
106             // After parsing, header = { PID, TID, USER, PR, NI, CPU, S, VIRT, RES, PCY, CMD, NAME }
107             // And columnIndices will contain end index of each word.
108             header = parseHeader(line, "[ %]");
109             nextToUsage = false;
110 
111             // NAME is not in the list since we need to modify the end of the CMD index.
112             const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", NULL };
113             if (!getColumnIndices(columnIndices, headerNames, line)) {
114                 return -1;
115             }
116             // Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
117             // for example: ... CMD             NAME
118             //              ... Jit thread pool com.google.android.gms.feedback
119             // If use end index of CMD, parsed result = { "Jit", "thread pool com.google.android.gms.feedback" }
120             // If use start index of NAME, parsed result = { "Jit thread pool", "com.google.android.gms.feedback" }
121             int endCMD = columnIndices.back();
122             columnIndices.pop_back();
123             columnIndices.push_back(line.find("NAME", endCMD) - 1);
124             // Add NAME index to complete the column list.
125             columnIndices.push_back(columnIndices.back() + 4);
126             continue;
127         }
128 
129         record = parseRecordByColumns(line, columnIndices);
130         diff = record.size() - header.size();
131         if (diff < 0) {
132             fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
133             printRecord(record);
134             continue;
135         } else if (diff > 0) {
136             fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
137             printRecord(record);
138             continue;
139         }
140 
141         uint64_t token = proto.start(CpuInfoProto::TASKS);
142         for (int i=0; i<(int)record.size(); i++) {
143             if (!table.insertField(&proto, header[i], record[i])) {
144                 fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
145                         this->name.string(), nline, header[i].c_str(), record[i].c_str());
146             }
147         }
148         proto.end(token);
149     }
150 
151     if (!reader.ok(&line)) {
152         fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
153         return -1;
154     }
155 
156     if (!proto.flush(out)) {
157         fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
158         return -1;
159     }
160     fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
161     return NO_ERROR;
162 }
163