blob: 2afe98b6b4d1109e8c4d994f45661b3e9a1fede3 [file] [log] [blame]
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001/* this code is used to generate a boot sequence profile that can be used
2 * with the 'bootchart' graphics generation tool. see www.bootchart.org
3 * note that unlike the original bootchartd, this is not a Bash script but
4 * some C code that is run right from the init script.
5 */
6
7#include <stdio.h>
8#include <time.h>
9#include <dirent.h>
10#include <unistd.h>
11#include <fcntl.h>
12#include <unistd.h>
13#include <fcntl.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <errno.h>
17#include <stdlib.h>
18#include <sys/stat.h>
19
20#define VERSION "0.8"
21#define SAMPLE_PERIOD 0.2
22#define LOG_ROOT "/tmp/bootchart"
23#define LOG_STAT LOG_ROOT"/proc_stat.log"
24#define LOG_PROCS LOG_ROOT"/proc_ps.log"
25#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
26#define LOG_HEADER LOG_ROOT"/header"
27#define LOG_ACCT LOG_ROOT"/kernel_pacct"
28
29#define LOG_STARTFILE "/data/bootchart"
30#define LOG_STOPFILE "/data/bootchart-stop"
31
32static int
33unix_read(int fd, void* buff, int len)
34{
35 int ret;
36 do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
37 return ret;
38}
39
40static int
41unix_write(int fd, const void* buff, int len)
42{
43 int ret;
44 do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
45 return ret;
46}
47
48static int
49proc_read(const char* filename, char* buff, size_t buffsize)
50{
51 int len = 0;
52 int fd = open(filename, O_RDONLY);
53 if (fd >= 0) {
54 len = unix_read(fd, buff, buffsize-1);
55 close(fd);
56 }
57 buff[len] = 0;
58 return len;
59}
60
61#define FILE_BUFF_SIZE 65536
62#define FILE_BUFF_RESERVE (FILE_BUFF_SIZE - 4096)
63
64typedef struct {
65 int count;
66 int fd;
67 char data[FILE_BUFF_SIZE];
68} FileBuffRec, *FileBuff;
69
70static void
71file_buff_open( FileBuff buff, const char* path )
72{
73 buff->count = 0;
74 buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
75}
76
77static void
78file_buff_write( FileBuff buff, const void* src, int len )
79{
80 while (len > 0) {
81 int avail = sizeof(buff->data) - buff->count;
82 if (avail > len)
83 avail = len;
84
85 memcpy( buff->data + buff->count, src, avail );
86 len -= avail;
87 src = (char*)src + avail;
88
89 buff->count += avail;
90 if (buff->count == FILE_BUFF_SIZE) {
91 unix_write( buff->fd, buff->data, buff->count );
92 buff->count = 0;
93 }
94 }
95}
96
97static void
98file_buff_done( FileBuff buff )
99{
100 if (buff->count > 0) {
101 unix_write( buff->fd, buff->data, buff->count );
102 buff->count = 0;
103 }
104}
105
106static void
107log_header(void)
108{
109 FILE* out;
110 char cmdline[1024];
111 char uname[128];
112 char cpuinfo[128];
113 char* cpu;
114 char date[32];
115 time_t now_t = time(NULL);
116 struct tm now = *localtime(&now_t);
117 strftime(date, sizeof(date), "%x %X", &now);
118
119 out = fopen( LOG_HEADER, "w" );
120 if (out == NULL)
121 return;
122
123 proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
124 proc_read("/proc/version", uname, sizeof(uname));
125 proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
126
127 cpu = strchr( cpuinfo, ':' );
128 if (cpu) {
129 char* p = strchr(cpu, '\n');
130 cpu += 2;
131 if (p)
132 *p = 0;
133 }
134
135 fprintf(out, "version = %s\n", VERSION);
136 fprintf(out, "title = Boot chart for Android ( %s )\n", date);
137 fprintf(out, "system.uname = %s\n", uname);
138 fprintf(out, "system.release = 0.0\n");
139 fprintf(out, "system.cpu = %s\n", cpu);
140 fprintf(out, "system.kernel.options = %s\n", cmdline);
141 fclose(out);
142}
143
144static void
145close_on_exec(int fd)
146{
147 fcntl(fd, F_SETFD, FD_CLOEXEC);
148}
149
150static void
151open_log_file(int* plogfd, const char* logfile)
152{
153 int logfd = *plogfd;
154
155 /* create log file if needed */
156 if (logfd < 0)
157 {
158 logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
159 if (logfd < 0) {
160 *plogfd = -2;
161 return;
162 }
163 close_on_exec(logfd);
164 *plogfd = logfd;
165 }
166}
167
168static void
169do_log_uptime(FileBuff log)
170{
171 char buff[65];
172 int fd, ret, len;
173
174 fd = open("/proc/uptime",O_RDONLY);
175 if (fd >= 0) {
176 int ret;
177 close_on_exec(fd);
178 ret = unix_read(fd, buff, 64);
179 close(fd);
180 buff[64] = 0;
181 if (ret >= 0) {
182 long long jiffies = 100LL*strtod(buff,NULL);
183 int len;
184 snprintf(buff,sizeof(buff),"%lld\n",jiffies);
185 len = strlen(buff);
186 file_buff_write(log, buff, len);
187 }
188 }
189}
190
191static void
192do_log_ln(FileBuff log)
193{
194 file_buff_write(log, "\n", 1);
195}
196
197
198static void
199do_log_file(FileBuff log, const char* procfile)
200{
201 char buff[1024];
202 int fd;
203
204 do_log_uptime(log);
205
206 /* append file content */
207 fd = open(procfile,O_RDONLY);
208 if (fd >= 0) {
209 close_on_exec(fd);
210 for (;;) {
211 int ret;
212 ret = unix_read(fd, buff, sizeof(buff));
213 if (ret <= 0)
214 break;
215
216 file_buff_write(log, buff, ret);
217 if (ret < (int)sizeof(buff))
218 break;
219 }
220 close(fd);
221 }
222
223 do_log_ln(log);
224}
225
226static void
227do_log_procs(FileBuff log)
228{
229 DIR* dir = opendir("/proc");
230 struct dirent* entry;
231
232 do_log_uptime(log);
233
234 while ((entry = readdir(dir)) != NULL) {
235 /* only match numeric values */
236 char* end;
237 int pid = strtol( entry->d_name, &end, 10);
238 if (end != NULL && end > entry->d_name && *end == 0) {
239 char filename[32];
240 char buff[1024];
241 char cmdline[1024];
242 int len;
243 int fd;
244
245 /* read command line and extract program name */
246 snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
247 proc_read(filename, cmdline, sizeof(cmdline));
248
249 /* read process stat line */
250 snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
251 fd = open(filename,O_RDONLY);
252 if (fd >= 0) {
253 len = unix_read(fd, buff, sizeof(buff)-1);
254 close(fd);
255 if (len > 0) {
256 int len2 = strlen(cmdline);
257 if (len2 > 0) {
258 /* we want to substitute the process name with its real name */
259 const char* p1;
260 const char* p2;
261 buff[len] = 0;
262 p1 = strchr(buff, '(');
263 p2 = strchr(p1, ')');
264 file_buff_write(log, buff, p1+1-buff);
265 file_buff_write(log, cmdline, strlen(cmdline));
266 file_buff_write(log, p2, strlen(p2));
267 } else {
268 /* no substitution */
269 file_buff_write(log,buff,len);
270 }
271 }
272 }
273 }
274 }
275 closedir(dir);
276 do_log_ln(log);
277}
278
279static FileBuffRec log_stat[1];
280static FileBuffRec log_procs[1];
281static FileBuffRec log_disks[1];
282
283/* called to setup bootcharting */
284int bootchart_init( void )
285{
286 int ret;
287 char buff[4];
288
289 buff[0] = 0;
290 proc_read( LOG_STARTFILE, buff, sizeof(buff) );
291 if (buff[0] != '1')
292 return -1;
293
294 do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
295
296 file_buff_open(log_stat, LOG_STAT);
297 file_buff_open(log_procs, LOG_PROCS);
298 file_buff_open(log_disks, LOG_DISK);
299
300 /* create kernel process accounting file */
301 {
302 int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
303 if (fd >= 0) {
304 close(fd);
305 acct( LOG_ACCT );
306 }
307 }
308
309 log_header();
310 return 0;
311}
312
313/* called each time you want to perform a bootchart sampling op */
314int bootchart_step( void )
315{
316 do_log_file(log_stat, "/proc/stat");
317 do_log_file(log_disks, "/proc/diskstats");
318 do_log_procs(log_procs);
319
320 /* we stop when /data/bootchart-stop contains 1 */
321 {
322 char buff[2];
323 if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
324 return -1;
325 }
326 }
327 return 0;
328}
329
330void bootchart_finish( void )
331{
332 unlink( LOG_STOPFILE );
333 file_buff_done(log_stat);
334 file_buff_done(log_disks);
335 file_buff_done(log_procs);
336 acct(NULL);
337}