1b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor/*
2b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * Copyright (C) 2010 The Android Open Source Project
3b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor *
4b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * Licensed under the Apache License, Version 2.0 (the "License");
5b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * you may not use this file except in compliance with the License.
6b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * You may obtain a copy of the License at
7b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor *
8b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor *      http://www.apache.org/licenses/LICENSE-2.0
9b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor *
10b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * Unless required by applicable law or agreed to in writing, software
11b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * distributed under the License is distributed on an "AS IS" BASIS,
12b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * See the License for the specific language governing permissions and
14b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor * limitations under the License.
15b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor */
16b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
17b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <assert.h>
18b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <ctype.h>
19b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <dirent.h>
20b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <fcntl.h>
21b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <stdio.h>
22b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <stdlib.h>
23b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <string.h>
24b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <sys/stat.h>
25b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <sys/time.h>
26b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <sys/types.h>
27b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <time.h>
28b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor#include <unistd.h>
29b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
30b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// This program is as dumb as possible -- it reads a whole bunch of data
31b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// from /proc and reports when it changes.  It's up to analysis tools to
32b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// actually parse the data.  This program only does enough parsing to split
33b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// large files (/proc/stat, /proc/yaffs) into individual values.
34b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//
35b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// The output format is a repeating series of observed differences:
36b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//
37b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//   T + <beforetime.stamp>
38b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//   /proc/<new_filename> + <contents of newly discovered file>
39b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//   /proc/<changed_filename> = <contents of changed file>
40b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//   /proc/<deleted_filename> -
41b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//   /proc/<filename>:<label> = <part of a multiline file>
42b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//   T - <aftertime.stamp>
43b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//
44b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//
45b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Files read:
46b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor//
47b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/*/stat       - for all running/selected processes
48b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/*/wchan      - for all running/selected processes
49b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/binder/stats - per line: "/proc/binder/stats:BC_REPLY"
50b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/diskstats    - per device: "/proc/diskstats:mmcblk0"
51b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/net/dev      - per interface: "/proc/net/dev:rmnet0"
52b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/stat         - per line: "/proc/stat:intr"
53b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /proc/yaffs        - per device/line: "/proc/yaffs:userdata:nBlockErasures"
54b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
55b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor//                    - per line: "/sys/.../time_in_state:245000"
56b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
57b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorstruct data {
58b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char *name;            // filename, plus ":var" for many-valued files
59b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char *value;           // text to be reported when it changes
60b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor};
61b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
62376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor// Like memcpy, but replaces spaces and unprintables with '_'.
63376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnorstatic void unspace(char *dest, const char *src, int len) {
64376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    while (len-- > 0) {
65376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        char ch = *src++;
66376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        *dest++ = isgraph(ch) ? ch : '_';
67376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    }
68376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor}
69376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
70b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Set data->name and data->value to malloc'd strings with the
71b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// filename and contents of the file.  Trims trailing whitespace.
72b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorstatic void read_data(struct data *data, const char *filename) {
73b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char buf[4096];
74b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    data->name = strdup(filename);
75b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int fd = open(filename, O_RDONLY);
76b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (fd < 0) {
77b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        data->value = NULL;
78b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        return;
79b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
80b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
81b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int len = read(fd, buf, sizeof(buf));
82b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (len < 0) {
83b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        perror(filename);
84b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        close(fd);
85b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        data->value = NULL;
86b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        return;
87b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
88b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
89b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    close(fd);
90b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    while (len > 0 && isspace(buf[len - 1])) --len;
91b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    data->value = malloc(len + 1);
92b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    memcpy(data->value, buf, len);
93b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    data->value[len] = '\0';
94b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
95b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
96376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor// Read a name/value file and write data entries for each line.
97b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Returns the number of entries written (always <= stats_count).
98b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor//
99b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// delimiter: used to split each line into name and value
100b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// terminator: if non-NULL, processing stops after this string
101b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor// skip_words: skip this many words at the start of each line
102376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnorstatic int read_lines(
103b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        const char *filename,
104b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        char delimiter, const char *terminator, int skip_words,
105376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        struct data *stats, int stats_count) {
106376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    char buf[8192];
107376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    int fd = open(filename, O_RDONLY);
108b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (fd < 0) return 0;
109b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
110b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int len = read(fd, buf, sizeof(buf) - 1);
111b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (len < 0) {
112376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        perror(filename);
113b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        close(fd);
114b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        return 0;
115b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
116b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    buf[len] = '\0';
117b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    close(fd);
118b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
119376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    if (terminator != NULL) {
120376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        char *end = strstr(buf, terminator);
121376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if (end != NULL) *end = '\0';
122376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    }
123376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
124376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    int filename_len = strlen(filename);
125b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int num = 0;
126b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char *line;
127b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    for (line = strtok(buf, "\n");
128b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor         line != NULL && num < stats_count;
129b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor         line = strtok(NULL, "\n")) {
130376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        // Line format: <sp>name<delim><sp>value
131376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
132b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        int i;
133376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        while (isspace(*line)) ++line;
134b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        for (i = 0; i < skip_words; ++i) {
135b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            while (isgraph(*line)) ++line;
136b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            while (isspace(*line)) ++line;
137b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor        }
138b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor
139376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        char *name_end = strchr(line, delimiter);
140b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (name_end == NULL) continue;
141b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
142376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        // Key format: <filename>:<name>
143b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        struct data *data = &stats[num++];
144376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        data->name = malloc(filename_len + 1 + (name_end - line) + 1);
145376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        unspace(data->name, filename, filename_len);
146376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        data->name[filename_len] = ':';
147376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        unspace(data->name + filename_len + 1, line, name_end - line);
148376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        data->name[filename_len + 1 + (name_end - line)] = '\0';
149b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
150376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        char *value = name_end + 1;
151b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        while (isspace(*value)) ++value;
152b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        data->value = strdup(value);
153b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
154b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
155b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    return num;
156b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
157b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
158b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Read /proc/yaffs and write data entries for each line.
159b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Returns the number of entries written (always <= stats_count).
160b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorstatic int read_proc_yaffs(struct data *stats, int stats_count) {
161b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char buf[8192];
162b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int fd = open("/proc/yaffs", O_RDONLY);
163b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (fd < 0) return 0;
164b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
165b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int len = read(fd, buf, sizeof(buf) - 1);
166b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (len < 0) {
167b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        perror("/proc/yaffs");
168b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        close(fd);
169b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        return 0;
170b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
171b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    buf[len] = '\0';
172b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    close(fd);
173b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
174b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int num = 0, device_len = 0;
175b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char *line, *device = NULL;
176b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    for (line = strtok(buf, "\n");
177b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor         line != NULL && num < stats_count;
178b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor         line = strtok(NULL, "\n")) {
179b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (strncmp(line, "Device ", 7) == 0) {
180b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            device = strchr(line, '"');
181b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            if (device != NULL) {
182b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                char *end = strchr(++device, '"');
183b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                if (end != NULL) *end = '\0';
184b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                device_len = strlen(device);
185b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            }
186b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            continue;
187b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        }
188b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (device == NULL) continue;
189b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
190b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        char *name_end = line + strcspn(line, " .");
191b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (name_end == line || *name_end == '\0') continue;
192b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
193b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        struct data *data = &stats[num++];
194b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        data->name = malloc(12 + device_len + 1 + (name_end - line) + 1);
195376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        memcpy(data->name, "/proc/yaffs:", 12);
196376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        unspace(data->name + 12, device, device_len);
197376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        data->name[12 + device_len] = ':';
198376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        unspace(data->name + 12 + device_len + 1, line, name_end - line);
199b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        data->name[12 + device_len + 1 + (name_end - line)] = '\0';
200b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
201b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        char *value = name_end;
202b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        while (*value == '.' || isspace(*value)) ++value;
203b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        data->value = strdup(value);
204b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
205b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
206b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    return num;
207b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
208b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
209b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Compare two "struct data" records by their name.
210b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorstatic int compare_data(const void *a, const void *b) {
211b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    const struct data *data_a = (const struct data *) a;
212b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    const struct data *data_b = (const struct data *) b;
213b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    return strcmp(data_a->name, data_b->name);
214b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
215b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
216b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Return a malloc'd array of "struct data" read from all over /proc.
217b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// The array is sorted by name and terminated by a record with name == NULL.
218376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnorstatic struct data *read_stats(char *names[], int name_count) {
219376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    static int bad[4096];  // Cache pids known not to match patterns
220376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    static size_t bad_count = 0;
221376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
222b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int pids[4096];
223b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    size_t pid_count = 0;
224b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
225b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    DIR *proc_dir = opendir("/proc");
226b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (proc_dir == NULL) {
227b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        perror("Can't scan /proc");
228b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        exit(1);
229b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
230b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
231376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    size_t bad_pos = 0;
232b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    char filename[1024];
233b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    struct dirent *proc_entry;
234b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    while ((proc_entry = readdir(proc_dir))) {
235b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        int pid = atoi(proc_entry->d_name);
236376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if (pid <= 0) continue;
237376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
238376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if (name_count > 0) {
239376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            while (bad_pos < bad_count && bad[bad_pos] < pid) ++bad_pos;
240376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if (bad_pos < bad_count && bad[bad_pos] == pid) continue;
241376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
242376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            char cmdline[4096];
243376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            sprintf(filename, "/proc/%d/cmdline", pid);
244376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            int fd = open(filename, O_RDONLY);
245376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if (fd < 0) {
246376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                perror(filename);
247376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                continue;
248376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            }
249376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
250376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            int len = read(fd, cmdline, sizeof(cmdline) - 1);
251376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if (len < 0) {
252376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                perror(filename);
253376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                close(fd);
254376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                continue;
255376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            }
256376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
257376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            close(fd);
258376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            cmdline[len] = '\0';
259376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            int n;
260376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            for (n = 0; n < name_count && !strstr(cmdline, names[n]); ++n);
261376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
262376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            if (n == name_count) {
263376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                // Insertion sort -- pids mostly increase so this makes sense
264376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                if (bad_count < sizeof(bad) / sizeof(bad[0])) {
265376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                    int pos = bad_count++;
266376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                    while (pos > 0 && bad[pos - 1] > pid) {
267376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                        bad[pos] = bad[pos - 1];
268376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                        --pos;
269376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                    }
270376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                    bad[pos] = pid;
271376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                }
272376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                continue;
273b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            }
274376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        }
275376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor
276376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        if (pid_count >= sizeof(pids) / sizeof(pids[0])) {
277376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            fprintf(stderr, "warning: >%d processes\n", pid_count);
278376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        } else {
279b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            pids[pid_count++] = pid;
280b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        }
281b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
282b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    closedir(proc_dir);
283b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
284376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    size_t i, stats_count = pid_count * 2 + 200;  // 200 for stat, yaffs, etc.
285b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    struct data *stats = malloc((stats_count + 1) * sizeof(struct data));
286b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    struct data *next = stats;
287b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    for (i = 0; i < pid_count; i++) {
288376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        assert(pids[i] > 0);
289b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        sprintf(filename, "/proc/%d/stat", pids[i]);
290b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        read_data(next++, filename);
291b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        sprintf(filename, "/proc/%d/wchan", pids[i]);
292b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        read_data(next++, filename);
293b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
294b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
295376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    struct data *end = stats + stats_count;
296b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    next += read_proc_yaffs(next, stats + stats_count - next);
297b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    next += read_lines("/proc/net/dev", ':', NULL, 0, next, end - next);
298b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    next += read_lines("/proc/stat", ' ', NULL, 0, next, end - next);
299b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    next += read_lines("/proc/binder/stats", ':', "\nproc ", 0, next, end - next);
300b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor    next += read_lines("/proc/diskstats", ' ', NULL, 2, next, end - next);
301376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    next += read_lines(
302376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor            "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state",
303b478656a6fbd88c7e1f3682d899126807ab8c1b3Dan Egnor            ' ', NULL, 0, next, end - next);
304b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
305b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    assert(next < stats + stats_count);
306b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    next->name = NULL;
307b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    next->value = NULL;
308b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    qsort(stats, next - stats, sizeof(struct data), compare_data);
309b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    return stats;
310b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
311b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
312b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Print stats which have changed from one sorted array to the next.
313b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorstatic void diff_stats(struct data *old_stats, struct data *new_stats) {
314b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    while (old_stats->name != NULL || new_stats->name != NULL) {
315b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        int compare;
316b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (old_stats->name == NULL) {
317b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            compare = 1;
318b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        } else if (new_stats->name == NULL) {
319b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            compare = -1;
320b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        } else {
321b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            compare = compare_data(old_stats, new_stats);
322b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        }
323b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
324b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (compare < 0) {
325b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            // old_stats no longer present
326b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            if (old_stats->value != NULL) {
327b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                printf("%s -\n", old_stats->name);
328b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            }
329b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            ++old_stats;
330b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        } else if (compare > 0) {
331b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            // new_stats is new
332b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            if (new_stats->value != NULL) {
333b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                printf("%s + %s\n", new_stats->name, new_stats->value);
334b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            }
335b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            ++new_stats;
336b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        } else {
337b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            // changed
338b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            if (new_stats->value == NULL) {
339b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                if (old_stats->value != NULL) {
340b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                    printf("%s -\n", old_stats->name);
341b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                }
342b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            } else if (old_stats->value == NULL) {
343b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                printf("%s + %s\n", new_stats->name, new_stats->value);
344b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            } else if (strcmp(old_stats->value, new_stats->value)) {
345b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                printf("%s = %s\n", new_stats->name, new_stats->value);
346b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            }
347b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            ++old_stats;
348b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor            ++new_stats;
349b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        }
350b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
351b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
352b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
353b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor// Free a "struct data" array and all the strings within it.
354b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorstatic void free_stats(struct data *stats) {
355b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    int i;
356b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    for (i = 0; stats[i].name != NULL; ++i) {
357b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        free(stats[i].name);
358b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        free(stats[i].value);
359b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
360b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    free(stats);
361b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
362b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
363b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnorint main(int argc, char *argv[]) {
364376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor    if (argc < 2) {
365b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        fprintf(stderr,
366376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "usage: procstatlog poll_interval [procname ...] > procstat.log\n\n"
367376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "\n"
368b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                "Scans process status every poll_interval seconds (e.g. 0.1)\n"
369b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                "and writes data from /proc/stat, /proc/*/stat files, and\n"
370b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                "other /proc status files every time something changes.\n"
371376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "\n"
372376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "Scans all processes by default.  Listing some process name\n"
373376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "substrings will limit scanning and reduce overhead.\n"
374376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor                "\n"
375b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor                "Data is logged continuously until the program is killed.\n");
376b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        return 2;
377b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
378b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
379b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    long poll_usec = (long) (atof(argv[1]) * 1000000l);
380b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    if (poll_usec <= 0) {
381b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        fprintf(stderr, "illegal poll interval: %s\n", argv[1]);
382b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        return 2;
383b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
384b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
385b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    struct data *old_stats = malloc(sizeof(struct data));
386b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    old_stats->name = NULL;
387b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    old_stats->value = NULL;
388b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    while (1) {
389b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        struct timeval before, after;
390b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        gettimeofday(&before, NULL);
391b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        printf("T + %ld.%06ld\n", before.tv_sec, before.tv_usec);
392b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
393376b3009a0d438a16fb7d6c27e19839cd1bb3f1fDan Egnor        struct data *new_stats = read_stats(argv + 2, argc - 2);
394b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        diff_stats(old_stats, new_stats);
395b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        free_stats(old_stats);
396b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        old_stats = new_stats;
397b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        gettimeofday(&after, NULL);
398b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        printf("T - %ld.%06ld\n", after.tv_sec, after.tv_usec);
399b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
400b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        long elapsed_usec = (long) after.tv_usec - before.tv_usec;
401b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        elapsed_usec += 1000000l * (after.tv_sec - before.tv_sec);
402b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor        if (poll_usec > elapsed_usec) usleep(poll_usec - elapsed_usec);
403b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    }
404b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor
405b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor    return 0;
406b897e9f6cdd0b229bddb5eb9439f87ac744bae42Dan Egnor}
407