1 /*
2 * Copyright (C) 2016 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 #include "bootio_collector.h"
18 #include <android-base/logging.h>
19 #include <android-base/file.h>
20 #include <log/log.h>
21 #include "protos.pb.h"
22 #include "time.h"
23 #include <unordered_map>
24 #include <inttypes.h>
25 #include <dirent.h>
26
27 namespace android {
28
29 #define CPU_STAT_FILE "/proc/stat"
30 #define SAMPLES_FILE "/samples"
31 #define PID_STAT_FILE "/proc/%d/stat"
32 #define PID_CMDLINE_FILE "/proc/%d/cmdline"
33 #define PID_IO_FILE "/proc/%d/io"
34 #define PROC_DIR "/proc"
35
36 static const int PROC_NAME_LEN = 64;
37 static const int THREAD_NAME_LEN = 32;
38 static const int MAX_LINE = 256;
39
40 #define die(...) { LOG(ERROR) << (__VA_ARGS__); exit(EXIT_FAILURE); }
41
PopulateCpu(CpuData & cpu)42 void PopulateCpu(CpuData& cpu) {
43 long unsigned utime, ntime, stime, itime;
44 long unsigned iowtime, irqtime, sirqtime;
45 FILE *file;
46 file = fopen(CPU_STAT_FILE, "r");
47 if (!file) die("Could not open /proc/stat.\n");
48 fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &utime, &ntime, &stime,
49 &itime, &iowtime, &irqtime, &sirqtime);
50 fclose(file);
51 cpu.set_utime(utime);
52 cpu.set_ntime(ntime);
53 cpu.set_stime(stime);
54 cpu.set_itime(itime);
55 cpu.set_iowtime(iowtime);
56 cpu.set_irqtime(irqtime);
57 cpu.set_sirqtime(sirqtime);
58 }
59
ClearPreviousResults(std::string path)60 void ClearPreviousResults(std::string path) {
61 std::string err;
62 if (!android::base::RemoveFileIfExists(path, &err)) {
63 LOG(ERROR) << "failed to remove the file " << path << " " << err;
64 return;
65 }
66 }
67
ReadIo(char * filename,AppSample * sample)68 int ReadIo(char *filename, AppSample *sample) {
69 FILE *file;
70 char line[MAX_LINE];
71 unsigned int rchar, wchar, syscr, syscw, readbytes, writebytes;
72
73 file = fopen(filename, "r");
74 if (!file) return 1;
75 while (fgets(line, MAX_LINE, file)) {
76 sscanf(line, "rchar: %u", &rchar);
77 sscanf(line, "wchar: %u", &wchar);
78 sscanf(line, "syscr: %u", &syscr);
79 sscanf(line, "syscw: %u", &syscw);
80 sscanf(line, "read_bytes: %u", &readbytes);
81 sscanf(line, "write_bytes: %u", &writebytes);
82 }
83 fclose(file);
84 sample->set_rchar(rchar);
85 sample->set_wchar(wchar);
86 sample->set_syscr(syscr);
87 sample->set_syscw(syscw);
88 sample->set_readbytes(readbytes);
89 sample->set_writebytes(writebytes);
90 return 0;
91 }
92
ReadStatForName(char * filename,AppData * app)93 int ReadStatForName(char *filename, AppData *app) {
94 FILE *file;
95 char buf[MAX_LINE], *open_paren, *close_paren;
96
97 file = fopen(filename, "r");
98 if (!file) return 1;
99 fgets(buf, MAX_LINE, file);
100 fclose(file);
101
102 /* Split at first '(' and last ')' to get process name. */
103 open_paren = strchr(buf, '(');
104 close_paren = strrchr(buf, ')');
105 if (!open_paren || !close_paren) return 1;
106
107 *open_paren = *close_paren = '\0';
108 if (!app->has_tname()) {
109 app->set_tname(open_paren + 1, close_paren - open_paren - 1);
110 }
111 return 0;
112 }
113
ReadStat(char * filename,AppSample * sample)114 int ReadStat(char *filename, AppSample *sample) {
115 FILE *file;
116 char buf[MAX_LINE], *open_paren, *close_paren;
117
118 file = fopen(filename, "r");
119 if (!file) return 1;
120 fgets(buf, MAX_LINE, file);
121 fclose(file);
122
123 /* Split at first '(' and last ')' to get process name. */
124 open_paren = strchr(buf, '(');
125 close_paren = strrchr(buf, ')');
126 if (!open_paren || !close_paren) return 1;
127
128 uint64_t utime;
129 uint64_t stime;
130 uint64_t rss;
131
132 /* Scan rest of string. */
133 sscanf(close_paren + 1,
134 " %*c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
135 "%" PRIu64 /*SCNu64*/
136 "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d "
137 "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d",
138 &utime,
139 &stime,
140 &rss);
141 sample->set_utime(utime);
142 sample->set_stime(stime);
143 sample->set_rss(rss);
144
145 return 0;
146 }
147
ReadCmdline(char * filename,AppData * app)148 int ReadCmdline(char *filename, AppData *app) {
149 FILE *file;
150 char line[MAX_LINE];
151
152 line[0] = '\0';
153 file = fopen(filename, "r");
154 if (!file) return 1;
155 fgets(line, MAX_LINE, file);
156 fclose(file);
157 if (strlen(line) > 0) {
158 app->set_name(line, strlen(line));
159 } else {
160 app->set_name("N/A");
161 }
162 return 0;
163 };
164
ReadProcData(std::unordered_map<int,AppData * > & pidDataMap,DataContainer & dataContainer,time_t currentTimeUtc,time_t currentUptime)165 void ReadProcData(std::unordered_map<int, AppData*>& pidDataMap, DataContainer& dataContainer,
166 time_t currentTimeUtc, time_t currentUptime) {
167 DIR *procDir;
168 struct dirent *pidDir;
169 pid_t pid;
170 char filename[64];
171 procDir = opendir(PROC_DIR);
172 if (!procDir) die("Could not open /proc.\n");
173 while ((pidDir = readdir(procDir))) {
174 if (!isdigit(pidDir->d_name[0])) {
175 continue;
176 }
177 pid = atoi(pidDir->d_name);
178 AppData *data;
179
180 // TODO: in theory same pid can be shared for multiple processes,
181 // might need add extra check.
182 if (pidDataMap.count(pid) == 0) {
183 data = dataContainer.add_app();
184 data->set_pid(pid);
185 sprintf(filename, PID_STAT_FILE, pid);
186 ReadStatForName(filename, data);
187 sprintf(filename, PID_CMDLINE_FILE, pid);
188 ReadCmdline(filename, data);
189 pidDataMap[pid] = data;
190 } else {
191 data = pidDataMap[pid];
192 }
193 AppSample *sample = data->add_samples();
194 sample->set_timestamp(currentTimeUtc);
195 sample->set_uptime(currentUptime);
196
197 sprintf(filename, PID_STAT_FILE, pid);
198 ReadStat(filename, sample);
199
200 sprintf(filename, PID_IO_FILE, pid);
201 ReadIo(filename, sample);
202 }
203 }
204
SumCpuValues(CpuData & cpu)205 uint64_t SumCpuValues(CpuData& cpu) {
206 return cpu.utime() + cpu.ntime() + cpu.stime() + cpu.itime() + cpu.iowtime() +
207 cpu.irqtime() + cpu.sirqtime();
208 }
209
GetUptime()210 time_t GetUptime() {
211 std::string uptime_str;
212 if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
213 LOG(ERROR) << "Failed to read /proc/uptime";
214 return -1;
215 }
216
217 // Cast intentionally rounds down.
218 return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
219 }
220
221 struct Stats {
222 int uptime;
223 float cpu;
224 uint64_t rbytes;
225 uint64_t wbytes;
226 };
227
PrintPids(DataContainer & data,std::unordered_map<int,uint64_t> & cpuDataMap)228 void PrintPids(DataContainer& data, std::unordered_map<int, uint64_t>& cpuDataMap) {
229 printf("rchar: number of bytes the process read, using any read-like system call "
230 "(from files, pipes, tty...).\n");
231 printf("wchar: number of bytes the process wrote using any write-like system call.\n");
232 printf("wchar: number of bytes the process wrote using any write-like system call.\n");
233 printf("syscr: number of write-like system call invocations that the process performed.\n");
234 printf("rbytes: number of bytes the process directly read from disk.\n");
235 printf("wbytes: number of bytes the process originally dirtied in the page-cache "
236 "(assuming they will go to disk later).\n\n");
237
238 std::unique_ptr<AppSample> bootZeroSample(new AppSample());
239 std::map<int, Stats> statsMap;
240 // Init stats map
241 Stats emptyStat {0, 0., 0, 0};
242 for (auto it = cpuDataMap.begin(); it != cpuDataMap.end(); it++) {
243 statsMap[it->first] = emptyStat;
244 }
245 for (int i = 0; i < data.app_size(); i++) {
246 const AppData appData = data.app(i);
247 printf("\n-----------------------------------------------------------------------------\n");
248 printf("PID:\t%u\n", appData.pid());
249 printf("Name:\t%s\n", appData.name().c_str());
250 printf("ThName:\t%s\n", appData.tname().c_str());
251 printf("%-15s%-13s%-13s%-13s%-13s%-13s%-13s%-13s\n", "Uptime inter.", "rchar", "wchar",
252 "syscr", "syscw", "rbytes", "wbytes", "cpu%");
253 const AppSample *olderSample = NULL;
254 const AppSample *newerSample = NULL;
255 bool isFirstSample = true;
256 for (int j = 0; j < appData.samples_size(); j++) {
257 olderSample = newerSample;
258 newerSample = &(appData.samples(j));
259 if (olderSample == NULL) {
260 olderSample = bootZeroSample.get();
261 }
262 float cpuLoad = 0.;
263 uint64_t cpuDelta;
264 if (isFirstSample) {
265 cpuDelta = cpuDataMap[newerSample->timestamp()];
266 } else {
267 cpuDelta = cpuDataMap[newerSample->timestamp()] -
268 cpuDataMap[olderSample->timestamp()];
269 }
270 if (cpuDelta != 0) {
271 cpuLoad = (newerSample->utime() - olderSample->utime() +
272 newerSample->stime() - olderSample->stime()) * 100. / cpuDelta;
273 }
274 Stats& stats = statsMap[newerSample->timestamp()];
275 stats.uptime = newerSample->uptime();
276 stats.cpu += cpuLoad;
277 stats.rbytes += (newerSample->readbytes() - olderSample->readbytes());
278 stats.wbytes += (newerSample->writebytes() - olderSample->writebytes());
279
280 printf("%5" PRId64 " - %-5" PRId64 " %-13" PRId64 "%-13" PRId64 "%-13" PRId64 "%-13" PRId64 "%-13"
281 PRId64 "%-13" PRId64 "%-9.2f\n",
282 olderSample->uptime(),
283 newerSample->uptime(),
284 newerSample->rchar() - olderSample->rchar(),
285 newerSample->wchar() - olderSample->wchar(),
286 newerSample->syscr() - olderSample->syscr(),
287 newerSample->syscw() - olderSample->syscw(),
288 newerSample->readbytes() - olderSample->readbytes(),
289 newerSample->writebytes() - olderSample->writebytes(),
290 cpuLoad);
291 isFirstSample = false;
292 }
293 printf("-----------------------------------------------------------------------------\n");
294 printf("%-15s%-13" PRId64 "%-13" PRId64 "%-13" PRId64 "%-13" PRId64 "%-13" PRId64 "%-13" PRId64 "\n",
295 "Total",
296 newerSample->rchar(),
297 newerSample->wchar(),
298 newerSample->syscr(),
299 newerSample->syscw(),
300 newerSample->readbytes(),
301 newerSample->writebytes());
302 }
303 printf("\nAggregations\n%-10s%-13s%-13s%-13s\n",
304 "Total",
305 "rbytes",
306 "wbytes",
307 "cpu%");
308
309 for (auto it = statsMap.begin(); it != statsMap.end(); it++) {
310 printf("%-10u%-13" PRIu64 "%-13" PRIu64 "%-9.2f\n",
311 it->second.uptime,
312 it->second.rbytes,
313 it->second.wbytes,
314 it->second.cpu);
315 }
316 }
317
318 }
319
BootioCollector(std::string path)320 BootioCollector::BootioCollector(std::string path) {
321 DCHECK_EQ('/', path.back());
322 path_ = path;
323 }
324
StartDataCollection(int timeout,int samples)325 void BootioCollector::StartDataCollection(int timeout, int samples) {
326 android::ClearPreviousResults(getStoragePath());
327 int remaining = samples + 1;
328 int delayS = timeout / samples;
329
330 std::unordered_map < int, AppData * > pidDataMap;
331 std::unique_ptr <DataContainer> data(new DataContainer());
332 while (remaining > 0) {
333 time_t currentTimeUtc = time(nullptr);
334 time_t currentUptime = android::GetUptime();
335 CpuData *cpu = data->add_cpu();
336 cpu->set_timestamp(currentTimeUtc);
337 cpu->set_uptime(currentUptime);
338 android::PopulateCpu(*cpu);
339 android::ReadProcData(pidDataMap, *data.get(), currentTimeUtc, currentUptime);
340 remaining--;
341 if (remaining == 0) {
342 continue;
343 }
344 sleep(delayS);
345 }
346 std::string file_data;
347 if (!data->SerializeToString(&file_data)) {
348 LOG(ERROR) << "Failed to serialize";
349 return;
350 }
351 if (!android::base::WriteStringToFile(file_data, getStoragePath())) {
352 LOG(ERROR) << "Failed to write samples";
353 }
354 }
355
Print()356 void BootioCollector::Print() {
357 std::string file_data;
358 if (!android::base::ReadFileToString(getStoragePath(), &file_data)) {
359 printf("Failed to read data from file.\n");
360 return;
361 }
362 std::unique_ptr <DataContainer> data(new DataContainer());
363 if (!data->ParsePartialFromString(file_data)) {
364 printf("Failed to parse data.\n");
365 return;
366 }
367 std::unordered_map<int, uint64_t> cpuDataMap;
368 for (int i = 0; i < data->cpu_size(); i++) {
369 CpuData cpu_data = data->cpu(i);
370 cpuDataMap[cpu_data.timestamp()] = android::SumCpuValues(cpu_data);
371 }
372 android::PrintPids(*data.get(), cpuDataMap);
373 }
374
375
getStoragePath()376 std::string BootioCollector::getStoragePath() {
377 return path_ + SAMPLES_FILE;
378 }
379