• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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