• 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 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