aboutsummaryrefslogtreecommitdiff
path: root/procps/smemcap.c
blob: cdcc891a1225b8779482eb6979d47efcf5a5b674 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 smemcap - a tool for meaningful memory reporting

 Copyright 2008-2009 Matt Mackall <mpm@selenic.com>

 This software may be used and distributed according to the terms of
 the GNU General Public License version 2 or later, incorporated
 herein by reference.
*/

//applet:IF_SMEMCAP(APPLET(smemcap, _BB_DIR_USR_BIN, _BB_SUID_DROP))

//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o

//config:config SMEMCAP
//config:	bool "smemcap"
//config:	default y
//config:	help
//config:	  smemcap is a tool for capturing process data for smem,
//config:	  a memory usage statistic tool.

#include "libbb.h"

struct tar_header {
	char name[100];           /*   0-99 */
	char mode[8];             /* 100-107 */
	char uid[8];              /* 108-115 */
	char gid[8];              /* 116-123 */
	char size[12];            /* 124-135 */
	char mtime[12];           /* 136-147 */
	char chksum[8];           /* 148-155 */
	char typeflag;            /* 156-156 */
	char linkname[100];       /* 157-256 */
	/* POSIX:   "ustar" NUL "00" */
	/* GNU tar: "ustar  " NUL */
	/* Normally it's defined as magic[6] followed by
	 * version[2], but we put them together to save code.
	 */
	char magic[8];            /* 257-264 */
	char uname[32];           /* 265-296 */
	char gname[32];           /* 297-328 */
	char devmajor[8];         /* 329-336 */
	char devminor[8];         /* 337-344 */
	char prefix[155];         /* 345-499 */
	char padding[12];         /* 500-512 (pad to exactly TAR_512) */
};

struct fileblock {
	struct fileblock *next;
	char data[512];
};

static void writeheader(const char *path, struct stat *sb, int type)
{
	struct tar_header header;
	int i, sum;

	memset(&header, 0, 512);
	strcpy(header.name, path);
	sprintf(header.mode, "%o", sb->st_mode & 0777);
	/* careful to not overflow fields! */
	sprintf(header.uid, "%o", sb->st_uid & 07777777);
	sprintf(header.gid, "%o", sb->st_gid & 07777777);
	sprintf(header.size, "%o", (unsigned)sb->st_size);
	sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
	header.typeflag = type;
	//strcpy(header.magic, "ustar  "); - do we want to be standard-compliant?

	/* Calculate and store the checksum (the sum of all of the bytes of
	 * the header). The checksum field must be filled with blanks for the
	 * calculation. The checksum field is formatted differently from the
	 * other fields: it has 6 digits, a NUL, then a space -- rather than
	 * digits, followed by a NUL like the other fields... */
	header.chksum[7] = ' ';
	sum = ' ' * 7;
	for (i = 0; i < 512; i++)
		sum += ((unsigned char*)&header)[i];
	sprintf(header.chksum, "%06o", sum);

	xwrite(STDOUT_FILENO, &header, 512);
}

static void archivefile(const char *path)
{
	struct fileblock *start, *cur;
	struct fileblock **prev = &start;
	int fd, r;
	unsigned size = 0;
	struct stat s;

	/* buffer the file */
	fd = xopen(path, O_RDONLY);
	do {
		cur = xzalloc(sizeof(*cur));
		*prev = cur;
		prev = &cur->next;
		r = full_read(fd, cur->data, 512);
		if (r > 0)
			size += r;
	} while (r == 512);

	/* write archive header */
	fstat(fd, &s);
	close(fd);
	s.st_size = size;
	writeheader(path, &s, '0');

	/* dump file contents */
	for (cur = start; (int)size > 0; size -= 512) {
		xwrite(STDOUT_FILENO, cur->data, 512);
		start = cur;
		cur = cur->next;
		free(start);
	}
}

static void archivejoin(const char *sub, const char *name)
{
	char path[sizeof(long long)*3 + sizeof("/cmdline")];
	sprintf(path, "%s/%s", sub, name);
	archivefile(path);
}

//usage:#define smemcap_trivial_usage ">SMEMDATA.TAR"
//usage:#define smemcap_full_usage "\n\n"
//usage:       "Collect memory usage data in /proc and write it to stdout"

int smemcap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int smemcap_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
	DIR *d;
	struct dirent *de;

	xchdir("/proc");
	d = xopendir(".");

	archivefile("meminfo");
	archivefile("version");
	while ((de = readdir(d)) != NULL) {
		if (isdigit(de->d_name[0])) {
			struct stat s;
			memset(&s, 0, sizeof(s));
			s.st_mode = 0555;
			writeheader(de->d_name, &s, '5');
			archivejoin(de->d_name, "smaps");
			archivejoin(de->d_name, "cmdline");
			archivejoin(de->d_name, "stat");
		}
	}

	return EXIT_SUCCESS;
}