platform_system_core/init/bootchart.c
Bo (Andover) Zhang 3700373c67 Fixed the pacing logic in bootchart data collection.
Ideally bootchart collects system data every BOOTCHART_POLLING_MS ms.
However, the current bootchart pacing logic would collect data every
time the main loop in init.c is executed. This results in:

1. Multiple data samples being taken consecutively or prematurely. In
many cases, these data samples have the same timestamp (in jiffies).
The off-line data processing tool would complain about dividing by
zero "interval" and bail out.

2. Because of the ineffective pacing, bootchart data collection would
terminate prematurely. The total duration of data collection is usually
much shorter than what the user specifies.

The fix is to check whether BOOTCHART_POLLING_MS ms has elapsed
before taking a new data sample. For this purpose, /proc/uptime
is used to get the time values, consistent with the precision of
bootchart timestamps.

Change-Id: I106bf91dbda01059b719df6c73b8bd1cd54a64f0
Signed-off-by: Bo (Andover) Zhang <zhang@broadcom.com>
2014-07-29 21:09:01 -04:00

385 lines
9.6 KiB
C

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* this code is used to generate a boot sequence profile that can be used
* with the 'bootchart' graphics generation tool. see www.bootchart.org
* note that unlike the original bootchartd, this is not a Bash script but
* some C code that is run right from the init script.
*/
#include <stdio.h>
#include <time.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "bootchart.h"
#define VERSION "0.8"
#define SAMPLE_PERIOD 0.2
#define LOG_ROOT "/data/bootchart"
#define LOG_STAT LOG_ROOT"/proc_stat.log"
#define LOG_PROCS LOG_ROOT"/proc_ps.log"
#define LOG_DISK LOG_ROOT"/proc_diskstats.log"
#define LOG_HEADER LOG_ROOT"/header"
#define LOG_ACCT LOG_ROOT"/kernel_pacct"
#define LOG_STARTFILE "/data/bootchart-start"
#define LOG_STOPFILE "/data/bootchart-stop"
static int
unix_read(int fd, void* buff, int len)
{
int ret;
do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
return ret;
}
static int
unix_write(int fd, const void* buff, int len)
{
int ret;
do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
return ret;
}
static int
proc_read(const char* filename, char* buff, size_t buffsize)
{
int len = 0;
int fd = open(filename, O_RDONLY);
if (fd >= 0) {
len = unix_read(fd, buff, buffsize-1);
close(fd);
}
buff[len > 0 ? len : 0] = 0;
return len;
}
#define FILE_BUFF_SIZE 65536
typedef struct {
int count;
int fd;
char data[FILE_BUFF_SIZE];
} FileBuffRec, *FileBuff;
static void
file_buff_open( FileBuff buff, const char* path )
{
buff->count = 0;
buff->fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
}
static void
file_buff_write( FileBuff buff, const void* src, int len )
{
while (len > 0) {
int avail = sizeof(buff->data) - buff->count;
if (avail > len)
avail = len;
memcpy( buff->data + buff->count, src, avail );
len -= avail;
src = (char*)src + avail;
buff->count += avail;
if (buff->count == FILE_BUFF_SIZE) {
unix_write( buff->fd, buff->data, buff->count );
buff->count = 0;
}
}
}
static void
file_buff_done( FileBuff buff )
{
if (buff->count > 0) {
unix_write( buff->fd, buff->data, buff->count );
buff->count = 0;
}
}
static long long
get_uptime_jiffies()
{
char buff[64];
long long jiffies = 0;
if (proc_read("/proc/uptime", buff, sizeof(buff)) > 0)
jiffies = 100LL*strtod(buff,NULL);
return jiffies;
}
static void
log_header(void)
{
FILE* out;
char cmdline[1024];
char uname[128];
char cpuinfo[128];
char* cpu;
char date[32];
time_t now_t = time(NULL);
struct tm now = *localtime(&now_t);
strftime(date, sizeof(date), "%x %X", &now);
out = fopen( LOG_HEADER, "w" );
if (out == NULL)
return;
proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
proc_read("/proc/version", uname, sizeof(uname));
proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
cpu = strchr( cpuinfo, ':' );
if (cpu) {
char* p = strchr(cpu, '\n');
cpu += 2;
if (p)
*p = 0;
}
fprintf(out, "version = %s\n", VERSION);
fprintf(out, "title = Boot chart for Android ( %s )\n", date);
fprintf(out, "system.uname = %s\n", uname);
fprintf(out, "system.release = 0.0\n");
fprintf(out, "system.cpu = %s\n", cpu);
fprintf(out, "system.kernel.options = %s\n", cmdline);
fclose(out);
}
static void
close_on_exec(int fd)
{
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
static void
open_log_file(int* plogfd, const char* logfile)
{
int logfd = *plogfd;
/* create log file if needed */
if (logfd < 0)
{
logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
if (logfd < 0) {
*plogfd = -2;
return;
}
close_on_exec(logfd);
*plogfd = logfd;
}
}
static void
do_log_uptime(FileBuff log)
{
char buff[65];
int len;
snprintf(buff,sizeof(buff),"%lld\n",get_uptime_jiffies());
len = strlen(buff);
file_buff_write(log, buff, len);
}
static void
do_log_ln(FileBuff log)
{
file_buff_write(log, "\n", 1);
}
static void
do_log_file(FileBuff log, const char* procfile)
{
char buff[1024];
int fd;
do_log_uptime(log);
/* append file content */
fd = open(procfile,O_RDONLY);
if (fd >= 0) {
close_on_exec(fd);
for (;;) {
int ret;
ret = unix_read(fd, buff, sizeof(buff));
if (ret <= 0)
break;
file_buff_write(log, buff, ret);
if (ret < (int)sizeof(buff))
break;
}
close(fd);
}
do_log_ln(log);
}
static void
do_log_procs(FileBuff log)
{
DIR* dir = opendir("/proc");
struct dirent* entry;
do_log_uptime(log);
while ((entry = readdir(dir)) != NULL) {
/* only match numeric values */
char* end;
int pid = strtol( entry->d_name, &end, 10);
if (end != NULL && end > entry->d_name && *end == 0) {
char filename[32];
char buff[1024];
char cmdline[1024];
int len;
int fd;
/* read command line and extract program name */
snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
proc_read(filename, cmdline, sizeof(cmdline));
/* read process stat line */
snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
fd = open(filename,O_RDONLY);
if (fd >= 0) {
len = unix_read(fd, buff, sizeof(buff)-1);
close(fd);
if (len > 0) {
int len2 = strlen(cmdline);
if (len2 > 0) {
/* we want to substitute the process name with its real name */
const char* p1;
const char* p2;
buff[len] = 0;
p1 = strchr(buff, '(');
p2 = strchr(p1, ')');
file_buff_write(log, buff, p1+1-buff);
file_buff_write(log, cmdline, strlen(cmdline));
file_buff_write(log, p2, strlen(p2));
} else {
/* no substitution */
file_buff_write(log,buff,len);
}
}
}
}
}
closedir(dir);
do_log_ln(log);
}
static FileBuffRec log_stat[1];
static FileBuffRec log_procs[1];
static FileBuffRec log_disks[1];
/* called to setup bootcharting */
int bootchart_init( void )
{
int ret;
char buff[4];
int timeout = 0, count = 0;
buff[0] = 0;
proc_read( LOG_STARTFILE, buff, sizeof(buff) );
if (buff[0] != 0) {
timeout = atoi(buff);
}
else {
/* when running with emulator, androidboot.bootchart=<timeout>
* might be passed by as kernel parameters to specify the bootchart
* timeout. this is useful when using -wipe-data since the /data
* partition is fresh
*/
char cmdline[1024];
char* s;
#define KERNEL_OPTION "androidboot.bootchart="
proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
s = strstr(cmdline, KERNEL_OPTION);
if (s) {
s += sizeof(KERNEL_OPTION)-1;
timeout = atoi(s);
}
}
if (timeout == 0)
return 0;
if (timeout > BOOTCHART_MAX_TIME_SEC)
timeout = BOOTCHART_MAX_TIME_SEC;
count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
file_buff_open(log_stat, LOG_STAT);
file_buff_open(log_procs, LOG_PROCS);
file_buff_open(log_disks, LOG_DISK);
/* create kernel process accounting file */
{
int fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
if (fd >= 0) {
close(fd);
acct( LOG_ACCT );
}
}
log_header();
return count;
}
/* called each time you want to perform a bootchart sampling op */
int bootchart_step( void )
{
do_log_file(log_stat, "/proc/stat");
do_log_file(log_disks, "/proc/diskstats");
do_log_procs(log_procs);
/* we stop when /data/bootchart-stop contains 1 */
{
char buff[2];
if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
return -1;
}
}
return 0;
}
void bootchart_finish( void )
{
unlink( LOG_STOPFILE );
file_buff_done(log_stat);
file_buff_done(log_disks);
file_buff_done(log_procs);
acct(NULL);
}
/* called to get time (in ms) used by bootchart */
long long bootchart_gettime( void )
{
return 10LL*get_uptime_jiffies();
}