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