summaryrefslogtreecommitdiff
path: root/procps/smemcap.c
blob: 196c91f54bd6334b69c437303a737d3571bc4dbc (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
/*
 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");
		}
	}

	if (ENABLE_FEATURE_CLEAN_UP)
		closedir(d);

	return EXIT_SUCCESS;
}