nmeter: fix block i/o count on newer Linux kernels
[oweals/busybox.git] / procps / smemcap.c
1 /*
2  smemcap - a tool for meaningful memory reporting
3
4  Copyright 2008-2009 Matt Mackall <mpm@selenic.com>
5
6  This software may be used and distributed according to the terms of
7  the GNU General Public License version 2 or later, incorporated
8  herein by reference.
9 */
10
11 //applet:IF_SMEMCAP(APPLET(smemcap, BB_DIR_USR_BIN, BB_SUID_DROP))
12
13 //kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
14
15 //config:config SMEMCAP
16 //config:       bool "smemcap"
17 //config:       default y
18 //config:       help
19 //config:         smemcap is a tool for capturing process data for smem,
20 //config:         a memory usage statistic tool.
21
22 #include "libbb.h"
23 #include "bb_archive.h"
24
25 struct fileblock {
26         struct fileblock *next;
27         char data[TAR_BLOCK_SIZE];
28 };
29
30 static void writeheader(const char *path, struct stat *sb, int type)
31 {
32         struct tar_header_t header;
33         int i, sum;
34
35         memset(&header, 0, TAR_BLOCK_SIZE);
36         strcpy(header.name, path);
37         sprintf(header.mode, "%o", sb->st_mode & 0777);
38         /* careful to not overflow fields! */
39         sprintf(header.uid, "%o", sb->st_uid & 07777777);
40         sprintf(header.gid, "%o", sb->st_gid & 07777777);
41         sprintf(header.size, "%o", (unsigned)sb->st_size);
42         sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
43         header.typeflag = type;
44         strcpy(header.magic, "ustar  "); /* like GNU tar */
45
46         /* Calculate and store the checksum (the sum of all of the bytes of
47          * the header). The checksum field must be filled with blanks for the
48          * calculation. The checksum field is formatted differently from the
49          * other fields: it has 6 digits, a NUL, then a space -- rather than
50          * digits, followed by a NUL like the other fields... */
51         header.chksum[7] = ' ';
52         sum = ' ' * 7;
53         for (i = 0; i < TAR_BLOCK_SIZE; i++)
54                 sum += ((unsigned char*)&header)[i];
55         sprintf(header.chksum, "%06o", sum);
56
57         xwrite(STDOUT_FILENO, &header, TAR_BLOCK_SIZE);
58 }
59
60 static void archivefile(const char *path)
61 {
62         struct fileblock *start, *cur;
63         struct fileblock **prev = &start;
64         int fd, r;
65         unsigned size = 0;
66         struct stat s;
67
68         /* buffer the file */
69         fd = xopen(path, O_RDONLY);
70         do {
71                 cur = xzalloc(sizeof(*cur));
72                 *prev = cur;
73                 prev = &cur->next;
74                 r = full_read(fd, cur->data, TAR_BLOCK_SIZE);
75                 if (r > 0)
76                         size += r;
77         } while (r == TAR_BLOCK_SIZE);
78
79         /* write archive header */
80         fstat(fd, &s);
81         close(fd);
82         s.st_size = size;
83         writeheader(path, &s, '0');
84
85         /* dump file contents */
86         for (cur = start; (int)size > 0; size -= TAR_BLOCK_SIZE) {
87                 xwrite(STDOUT_FILENO, cur->data, TAR_BLOCK_SIZE);
88                 start = cur;
89                 cur = cur->next;
90                 free(start);
91         }
92 }
93
94 static void archivejoin(const char *sub, const char *name)
95 {
96         char path[sizeof(long long)*3 + sizeof("/cmdline")];
97         sprintf(path, "%s/%s", sub, name);
98         archivefile(path);
99 }
100
101 //usage:#define smemcap_trivial_usage ">SMEMDATA.TAR"
102 //usage:#define smemcap_full_usage "\n\n"
103 //usage:       "Collect memory usage data in /proc and write it to stdout"
104
105 int smemcap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
106 int smemcap_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
107 {
108         DIR *d;
109         struct dirent *de;
110
111         xchdir("/proc");
112         d = xopendir(".");
113
114         archivefile("meminfo");
115         archivefile("version");
116         while ((de = readdir(d)) != NULL) {
117                 if (isdigit(de->d_name[0])) {
118                         struct stat s;
119                         memset(&s, 0, sizeof(s));
120                         s.st_mode = 0555;
121                         writeheader(de->d_name, &s, '5');
122                         archivejoin(de->d_name, "smaps");
123                         archivejoin(de->d_name, "cmdline");
124                         archivejoin(de->d_name, "stat");
125                 }
126         }
127
128         if (ENABLE_FEATURE_CLEAN_UP)
129                 closedir(d);
130
131         return EXIT_SUCCESS;
132 }