19ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor/*
29ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * Copyright (C) 2010 The Android Open Source Project
39ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor *
49ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * Licensed under the Apache License, Version 2.0 (the "License");
59ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * you may not use this file except in compliance with the License.
69ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * You may obtain a copy of the License at
79ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor *
89ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor *      http://www.apache.org/licenses/LICENSE-2.0
99ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor *
109ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * Unless required by applicable law or agreed to in writing, software
119ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * distributed under the License is distributed on an "AS IS" BASIS,
129ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * See the License for the specific language governing permissions and
149ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor * limitations under the License.
159ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor */
169ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
179ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <assert.h>
189ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <ctype.h>
199ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <dirent.h>
209ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <fcntl.h>
219ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <stdio.h>
229ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <stdlib.h>
239ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <string.h>
249ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <sys/stat.h>
259ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <sys/time.h>
269ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <sys/types.h>
279ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <time.h>
289ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor#include <unistd.h>
299ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
309ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// This program is as dumb as possible -- it reads a whole bunch of data
319ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// from /proc and reports when it changes.  It's up to analysis tools to
329ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// actually parse the data.  This program only does enough parsing to split
339ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// large files (/proc/stat, /proc/yaffs) into individual values.
349ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//
359ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// The output format is a repeating series of observed differences:
369ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//
379ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//   T + <beforetime.stamp>
389ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//   /proc/<new_filename> + <contents of newly discovered file>
399ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//   /proc/<changed_filename> = <contents of changed file>
409ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//   /proc/<deleted_filename> -
419ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//   /proc/<filename>:<label> = <part of a multiline file>
429ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//   T - <aftertime.stamp>
439ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//
449ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//
459ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Files read:
469ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor//
47bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/*/stat       - for all running/selected processes
48bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/*/wchan      - for all running/selected processes
49bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/binder/stats - per line: "/proc/binder/stats:BC_REPLY"
50bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/diskstats    - per device: "/proc/diskstats:mmcblk0"
51bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/net/dev      - per interface: "/proc/net/dev:rmnet0"
52bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/stat         - per line: "/proc/stat:intr"
53bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /proc/yaffs        - per device/line: "/proc/yaffs:userdata:nBlockErasures"
54bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
55bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor//                    - per line: "/sys/.../time_in_state:245000"
569ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
579ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorstruct data {
589ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char *name;            // filename, plus ":var" for many-valued files
599ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char *value;           // text to be reported when it changes
609ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor};
619ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
62c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor// Like memcpy, but replaces spaces and unprintables with '_'.
63c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnorstatic void unspace(char *dest, const char *src, int len) {
64c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    while (len-- > 0) {
65c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        char ch = *src++;
66c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        *dest++ = isgraph(ch) ? ch : '_';
67c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    }
68c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor}
69c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
709ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Set data->name and data->value to malloc'd strings with the
719ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// filename and contents of the file.  Trims trailing whitespace.
729ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorstatic void read_data(struct data *data, const char *filename) {
739ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char buf[4096];
749ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    data->name = strdup(filename);
759ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int fd = open(filename, O_RDONLY);
769ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (fd < 0) {
779ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        data->value = NULL;
789ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        return;
799ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
809ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
819ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int len = read(fd, buf, sizeof(buf));
829ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (len < 0) {
839ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        perror(filename);
849ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        close(fd);
859ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        data->value = NULL;
869ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        return;
879ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
889ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
899ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    close(fd);
909ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    while (len > 0 && isspace(buf[len - 1])) --len;
919ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    data->value = malloc(len + 1);
929ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    memcpy(data->value, buf, len);
939ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    data->value[len] = '\0';
949ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
959ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
96c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor// Read a name/value file and write data entries for each line.
979ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Returns the number of entries written (always <= stats_count).
98bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor//
99bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// delimiter: used to split each line into name and value
100bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// terminator: if non-NULL, processing stops after this string
101bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor// skip_words: skip this many words at the start of each line
102c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnorstatic int read_lines(
103bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor        const char *filename,
104bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor        char delimiter, const char *terminator, int skip_words,
105c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        struct data *stats, int stats_count) {
106c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    char buf[8192];
107c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    int fd = open(filename, O_RDONLY);
1089ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (fd < 0) return 0;
1099ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1109ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int len = read(fd, buf, sizeof(buf) - 1);
1119ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (len < 0) {
112c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        perror(filename);
1139ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        close(fd);
1149ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        return 0;
1159ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
1169ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    buf[len] = '\0';
1179ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    close(fd);
1189ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
119c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    if (terminator != NULL) {
120c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        char *end = strstr(buf, terminator);
121c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        if (end != NULL) *end = '\0';
122c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    }
123c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
124c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    int filename_len = strlen(filename);
1259ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int num = 0;
1269ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char *line;
1279ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    for (line = strtok(buf, "\n");
1289ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor         line != NULL && num < stats_count;
1299ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor         line = strtok(NULL, "\n")) {
130c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        // Line format: <sp>name<delim><sp>value
131c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
132bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor        int i;
133c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        while (isspace(*line)) ++line;
134bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor        for (i = 0; i < skip_words; ++i) {
135bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor            while (isgraph(*line)) ++line;
136bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor            while (isspace(*line)) ++line;
137bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor        }
138bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor
139c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        char *name_end = strchr(line, delimiter);
1409ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (name_end == NULL) continue;
1419ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
142c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        // Key format: <filename>:<name>
1439ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        struct data *data = &stats[num++];
144c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        data->name = malloc(filename_len + 1 + (name_end - line) + 1);
145c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        unspace(data->name, filename, filename_len);
146c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        data->name[filename_len] = ':';
147c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        unspace(data->name + filename_len + 1, line, name_end - line);
148c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        data->name[filename_len + 1 + (name_end - line)] = '\0';
1499ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
150c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        char *value = name_end + 1;
1519ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        while (isspace(*value)) ++value;
1529ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        data->value = strdup(value);
1539ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
1549ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1559ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    return num;
1569ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
1579ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1589ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Read /proc/yaffs and write data entries for each line.
1599ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Returns the number of entries written (always <= stats_count).
1609ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorstatic int read_proc_yaffs(struct data *stats, int stats_count) {
1619ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char buf[8192];
1629ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int fd = open("/proc/yaffs", O_RDONLY);
1639ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (fd < 0) return 0;
1649ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1659ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int len = read(fd, buf, sizeof(buf) - 1);
1669ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (len < 0) {
1679ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        perror("/proc/yaffs");
1689ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        close(fd);
1699ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        return 0;
1709ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
1719ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    buf[len] = '\0';
1729ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    close(fd);
1739ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1749ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int num = 0, device_len = 0;
1759ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char *line, *device = NULL;
1769ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    for (line = strtok(buf, "\n");
1779ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor         line != NULL && num < stats_count;
1789ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor         line = strtok(NULL, "\n")) {
1799ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (strncmp(line, "Device ", 7) == 0) {
1809ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            device = strchr(line, '"');
1819ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            if (device != NULL) {
1829ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                char *end = strchr(++device, '"');
1839ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                if (end != NULL) *end = '\0';
1849ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                device_len = strlen(device);
1859ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            }
1869ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            continue;
1879ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        }
1889ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (device == NULL) continue;
1899ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1909ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        char *name_end = line + strcspn(line, " .");
1919ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (name_end == line || *name_end == '\0') continue;
1929ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
1939ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        struct data *data = &stats[num++];
1949ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        data->name = malloc(12 + device_len + 1 + (name_end - line) + 1);
195c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        memcpy(data->name, "/proc/yaffs:", 12);
196c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        unspace(data->name + 12, device, device_len);
197c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        data->name[12 + device_len] = ':';
198c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        unspace(data->name + 12 + device_len + 1, line, name_end - line);
1999ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        data->name[12 + device_len + 1 + (name_end - line)] = '\0';
2009ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
2019ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        char *value = name_end;
2029ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        while (*value == '.' || isspace(*value)) ++value;
2039ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        data->value = strdup(value);
2049ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
2059ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
2069ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    return num;
2079ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
2089ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
2099ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Compare two "struct data" records by their name.
2109ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorstatic int compare_data(const void *a, const void *b) {
2119ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    const struct data *data_a = (const struct data *) a;
2129ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    const struct data *data_b = (const struct data *) b;
2139ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    return strcmp(data_a->name, data_b->name);
2149ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
2159ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
2169ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Return a malloc'd array of "struct data" read from all over /proc.
2179ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// The array is sorted by name and terminated by a record with name == NULL.
218c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnorstatic struct data *read_stats(char *names[], int name_count) {
219c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    static int bad[4096];  // Cache pids known not to match patterns
220c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    static size_t bad_count = 0;
221c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
2229ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int pids[4096];
2239ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    size_t pid_count = 0;
2249ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
2259ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    DIR *proc_dir = opendir("/proc");
2269ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (proc_dir == NULL) {
2279ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        perror("Can't scan /proc");
2289ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        exit(1);
2299ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
2309ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
231c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    size_t bad_pos = 0;
2329ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    char filename[1024];
2339ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    struct dirent *proc_entry;
2349ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    while ((proc_entry = readdir(proc_dir))) {
2359ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        int pid = atoi(proc_entry->d_name);
236c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        if (pid <= 0) continue;
237c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
238c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        if (name_count > 0) {
239c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            while (bad_pos < bad_count && bad[bad_pos] < pid) ++bad_pos;
240c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            if (bad_pos < bad_count && bad[bad_pos] == pid) continue;
241c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
242c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            char cmdline[4096];
243c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            sprintf(filename, "/proc/%d/cmdline", pid);
244c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            int fd = open(filename, O_RDONLY);
245c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            if (fd < 0) {
246c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                perror(filename);
247c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                continue;
248c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            }
249c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
250c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            int len = read(fd, cmdline, sizeof(cmdline) - 1);
251c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            if (len < 0) {
252c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                perror(filename);
253c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                close(fd);
254c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                continue;
255c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            }
256c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
257c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            close(fd);
258c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            cmdline[len] = '\0';
259c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            int n;
260c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            for (n = 0; n < name_count && !strstr(cmdline, names[n]); ++n);
261c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
262c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            if (n == name_count) {
263c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                // Insertion sort -- pids mostly increase so this makes sense
264c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                if (bad_count < sizeof(bad) / sizeof(bad[0])) {
265c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                    int pos = bad_count++;
266c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                    while (pos > 0 && bad[pos - 1] > pid) {
267c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                        bad[pos] = bad[pos - 1];
268c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                        --pos;
269c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                    }
270c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                    bad[pos] = pid;
271c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                }
272c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                continue;
2739ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            }
274c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        }
275c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor
276c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        if (pid_count >= sizeof(pids) / sizeof(pids[0])) {
2779295b85ec3b4eb30e8fdd524a810033ae2aec633Marcus Oakland            fprintf(stderr, "warning: >%zu processes\n", pid_count);
278c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        } else {
2799ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            pids[pid_count++] = pid;
2809ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        }
2819ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
2829ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    closedir(proc_dir);
2839ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
284c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    size_t i, stats_count = pid_count * 2 + 200;  // 200 for stat, yaffs, etc.
2859ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    struct data *stats = malloc((stats_count + 1) * sizeof(struct data));
2869ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    struct data *next = stats;
2879ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    for (i = 0; i < pid_count; i++) {
288c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        assert(pids[i] > 0);
2899ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        sprintf(filename, "/proc/%d/stat", pids[i]);
2909ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        read_data(next++, filename);
2919ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        sprintf(filename, "/proc/%d/wchan", pids[i]);
2929ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        read_data(next++, filename);
2939ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
2949ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
295c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    struct data *end = stats + stats_count;
2969ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    next += read_proc_yaffs(next, stats + stats_count - next);
297bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor    next += read_lines("/proc/net/dev", ':', NULL, 0, next, end - next);
298bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor    next += read_lines("/proc/stat", ' ', NULL, 0, next, end - next);
299bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor    next += read_lines("/proc/binder/stats", ':', "\nproc ", 0, next, end - next);
300bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor    next += read_lines("/proc/diskstats", ' ', NULL, 2, next, end - next);
301c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    next += read_lines(
302c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor            "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state",
303bbb8304e20da95f79b76df1a5b905503c8988457Dan Egnor            ' ', NULL, 0, next, end - next);
3049ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3059ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    assert(next < stats + stats_count);
3069ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    next->name = NULL;
3079ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    next->value = NULL;
3089ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    qsort(stats, next - stats, sizeof(struct data), compare_data);
3099ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    return stats;
3109ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
3119ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3129ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Print stats which have changed from one sorted array to the next.
3139ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorstatic void diff_stats(struct data *old_stats, struct data *new_stats) {
3149ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    while (old_stats->name != NULL || new_stats->name != NULL) {
3159ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        int compare;
3169ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (old_stats->name == NULL) {
3179ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            compare = 1;
3189ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        } else if (new_stats->name == NULL) {
3199ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            compare = -1;
3209ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        } else {
3219ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            compare = compare_data(old_stats, new_stats);
3229ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        }
3239ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3249ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (compare < 0) {
3259ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            // old_stats no longer present
3269ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            if (old_stats->value != NULL) {
3279ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                printf("%s -\n", old_stats->name);
3289ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            }
3299ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            ++old_stats;
3309ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        } else if (compare > 0) {
3319ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            // new_stats is new
3329ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            if (new_stats->value != NULL) {
3339ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                printf("%s + %s\n", new_stats->name, new_stats->value);
3349ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            }
3359ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            ++new_stats;
3369ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        } else {
3379ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            // changed
3389ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            if (new_stats->value == NULL) {
3399ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                if (old_stats->value != NULL) {
3409ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                    printf("%s -\n", old_stats->name);
3419ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                }
3429ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            } else if (old_stats->value == NULL) {
3439ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                printf("%s + %s\n", new_stats->name, new_stats->value);
3449ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            } else if (strcmp(old_stats->value, new_stats->value)) {
3459ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                printf("%s = %s\n", new_stats->name, new_stats->value);
3469ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            }
3479ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            ++old_stats;
3489ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor            ++new_stats;
3499ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        }
3509ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
3519ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
3529ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3539ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor// Free a "struct data" array and all the strings within it.
3549ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorstatic void free_stats(struct data *stats) {
3559ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    int i;
3569ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    for (i = 0; stats[i].name != NULL; ++i) {
3579ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        free(stats[i].name);
3589ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        free(stats[i].value);
3599ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
3609ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    free(stats);
3619ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
3629ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3639ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnorint main(int argc, char *argv[]) {
364c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor    if (argc < 2) {
3659ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        fprintf(stderr,
366c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                "usage: procstatlog poll_interval [procname ...] > procstat.log\n\n"
367c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                "\n"
3689ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                "Scans process status every poll_interval seconds (e.g. 0.1)\n"
3699ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                "and writes data from /proc/stat, /proc/*/stat files, and\n"
3709ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                "other /proc status files every time something changes.\n"
371c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                "\n"
372c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                "Scans all processes by default.  Listing some process name\n"
373c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                "substrings will limit scanning and reduce overhead.\n"
374c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor                "\n"
3759ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor                "Data is logged continuously until the program is killed.\n");
3769ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        return 2;
3779ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
3789ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3799ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    long poll_usec = (long) (atof(argv[1]) * 1000000l);
3809ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    if (poll_usec <= 0) {
3819ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        fprintf(stderr, "illegal poll interval: %s\n", argv[1]);
3829ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        return 2;
3839ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
3849ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
3859ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    struct data *old_stats = malloc(sizeof(struct data));
3869ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    old_stats->name = NULL;
3879ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    old_stats->value = NULL;
3889ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    while (1) {
3899ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        struct timeval before, after;
3909ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        gettimeofday(&before, NULL);
3919ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        printf("T + %ld.%06ld\n", before.tv_sec, before.tv_usec);
3929ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
393c58e5db92595ceb241125a84c4aee02b33c521ffDan Egnor        struct data *new_stats = read_stats(argv + 2, argc - 2);
3949ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        diff_stats(old_stats, new_stats);
3959ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        free_stats(old_stats);
3969ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        old_stats = new_stats;
3979ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        gettimeofday(&after, NULL);
3989ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        printf("T - %ld.%06ld\n", after.tv_sec, after.tv_usec);
3999ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
4009ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        long elapsed_usec = (long) after.tv_usec - before.tv_usec;
4019ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        elapsed_usec += 1000000l * (after.tv_sec - before.tv_sec);
4029ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor        if (poll_usec > elapsed_usec) usleep(poll_usec - elapsed_usec);
4039ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    }
4049ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor
4059ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor    return 0;
4069ba812360baf977f9fa02f8e031f6f8fba6a4380Dan Egnor}
407