summaryrefslogtreecommitdiffhomepage
path: root/procps/smemcap.c
blob: f951a5fb6a1be594689982c8aca48cd807163a80 (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
/*
 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"
#include "unarchive.h"

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

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

	memset(&header, 0, TAR_BLOCK_SIZE);
	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  "); /* like GNU tar */

	/* 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 < TAR_BLOCK_SIZE; i++)
		sum += ((unsigned char*)&header)[i];
	sprintf(header.chksum, "%06o", sum);

	xwrite(STDOUT_FILENO, &header, TAR_BLOCK_SIZE);
}

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, TAR_BLOCK_SIZE);
		if (r > 0)
			size += r;
	} while (r == TAR_BLOCK_SIZE);

	/* 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 -= TAR_BLOCK_SIZE) {
		xwrite(STDOUT_FILENO, cur->data, TAR_BLOCK_SIZE);
		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;
}