1#include <stdlib.h>
2#include <linux/types.h>
3#include <sys/stat.h>
4#include <fcntl.h>
5#include <string.h>
6#include <sys/time.h>
7#include <sys/resource.h>
8#include <api/fs/fs.h>
9#include "util.h"
10#include "machine.h"
11#include "symbol.h"
12#include "tests.h"
13#include "debug.h"
14
15static char *test_file(int size)
16{
17#define TEMPL "/tmp/perf-test-XXXXXX"
18	static char buf_templ[sizeof(TEMPL)];
19	char *templ = buf_templ;
20	int fd, i;
21	unsigned char *buf;
22
23	strcpy(buf_templ, TEMPL);
24#undef TEMPL
25
26	fd = mkstemp(templ);
27	if (fd < 0) {
28		perror("mkstemp failed");
29		return NULL;
30	}
31
32	buf = malloc(size);
33	if (!buf) {
34		close(fd);
35		return NULL;
36	}
37
38	for (i = 0; i < size; i++)
39		buf[i] = (unsigned char) ((int) i % 10);
40
41	if (size != write(fd, buf, size))
42		templ = NULL;
43
44	free(buf);
45	close(fd);
46	return templ;
47}
48
49#define TEST_FILE_SIZE (DSO__DATA_CACHE_SIZE * 20)
50
51struct test_data_offset {
52	off_t offset;
53	u8 data[10];
54	int size;
55};
56
57struct test_data_offset offsets[] = {
58	/* Fill first cache page. */
59	{
60		.offset = 10,
61		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
62		.size   = 10,
63	},
64	/* Read first cache page. */
65	{
66		.offset = 10,
67		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
68		.size   = 10,
69	},
70	/* Fill cache boundary pages. */
71	{
72		.offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
73		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
74		.size   = 10,
75	},
76	/* Read cache boundary pages. */
77	{
78		.offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
79		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
80		.size   = 10,
81	},
82	/* Fill final cache page. */
83	{
84		.offset = TEST_FILE_SIZE - 10,
85		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
86		.size   = 10,
87	},
88	/* Read final cache page. */
89	{
90		.offset = TEST_FILE_SIZE - 10,
91		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
92		.size   = 10,
93	},
94	/* Read final cache page. */
95	{
96		.offset = TEST_FILE_SIZE - 3,
97		.data   = { 7, 8, 9, 0, 0, 0, 0, 0, 0, 0 },
98		.size   = 3,
99	},
100};
101
102int test__dso_data(void)
103{
104	struct machine machine;
105	struct dso *dso;
106	char *file = test_file(TEST_FILE_SIZE);
107	size_t i;
108
109	TEST_ASSERT_VAL("No test file", file);
110
111	memset(&machine, 0, sizeof(machine));
112
113	dso = dso__new((const char *)file);
114
115	TEST_ASSERT_VAL("Failed to access to dso",
116			dso__data_fd(dso, &machine) >= 0);
117
118	/* Basic 10 bytes tests. */
119	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
120		struct test_data_offset *data = &offsets[i];
121		ssize_t size;
122		u8 buf[10];
123
124		memset(buf, 0, 10);
125		size = dso__data_read_offset(dso, &machine, data->offset,
126				     buf, 10);
127
128		TEST_ASSERT_VAL("Wrong size", size == data->size);
129		TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10));
130	}
131
132	/* Read cross multiple cache pages. */
133	{
134		ssize_t size;
135		int c;
136		u8 *buf;
137
138		buf = malloc(TEST_FILE_SIZE);
139		TEST_ASSERT_VAL("ENOMEM\n", buf);
140
141		/* First iteration to fill caches, second one to read them. */
142		for (c = 0; c < 2; c++) {
143			memset(buf, 0, TEST_FILE_SIZE);
144			size = dso__data_read_offset(dso, &machine, 10,
145						     buf, TEST_FILE_SIZE);
146
147			TEST_ASSERT_VAL("Wrong size",
148				size == (TEST_FILE_SIZE - 10));
149
150			for (i = 0; i < (size_t)size; i++)
151				TEST_ASSERT_VAL("Wrong data",
152					buf[i] == (i % 10));
153		}
154
155		free(buf);
156	}
157
158	dso__delete(dso);
159	unlink(file);
160	return 0;
161}
162
163static long open_files_cnt(void)
164{
165	char path[PATH_MAX];
166	struct dirent *dent;
167	DIR *dir;
168	long nr = 0;
169
170	scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint());
171	pr_debug("fd path: %s\n", path);
172
173	dir = opendir(path);
174	TEST_ASSERT_VAL("failed to open fd directory", dir);
175
176	while ((dent = readdir(dir)) != NULL) {
177		if (!strcmp(dent->d_name, ".") ||
178		    !strcmp(dent->d_name, ".."))
179			continue;
180
181		nr++;
182	}
183
184	closedir(dir);
185	return nr - 1;
186}
187
188static struct dso **dsos;
189
190static int dsos__create(int cnt, int size)
191{
192	int i;
193
194	dsos = malloc(sizeof(dsos) * cnt);
195	TEST_ASSERT_VAL("failed to alloc dsos array", dsos);
196
197	for (i = 0; i < cnt; i++) {
198		char *file;
199
200		file = test_file(size);
201		TEST_ASSERT_VAL("failed to get dso file", file);
202
203		dsos[i] = dso__new(file);
204		TEST_ASSERT_VAL("failed to get dso", dsos[i]);
205	}
206
207	return 0;
208}
209
210static void dsos__delete(int cnt)
211{
212	int i;
213
214	for (i = 0; i < cnt; i++) {
215		struct dso *dso = dsos[i];
216
217		unlink(dso->name);
218		dso__delete(dso);
219	}
220
221	free(dsos);
222}
223
224static int set_fd_limit(int n)
225{
226	struct rlimit rlim;
227
228	if (getrlimit(RLIMIT_NOFILE, &rlim))
229		return -1;
230
231	pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n);
232
233	rlim.rlim_cur = n;
234	return setrlimit(RLIMIT_NOFILE, &rlim);
235}
236
237int test__dso_data_cache(void)
238{
239	struct machine machine;
240	long nr_end, nr = open_files_cnt();
241	int dso_cnt, limit, i, fd;
242
243	memset(&machine, 0, sizeof(machine));
244
245	/* set as system limit */
246	limit = nr * 4;
247	TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit));
248
249	/* and this is now our dso open FDs limit */
250	dso_cnt = limit / 2;
251	TEST_ASSERT_VAL("failed to create dsos\n",
252		!dsos__create(dso_cnt, TEST_FILE_SIZE));
253
254	for (i = 0; i < (dso_cnt - 1); i++) {
255		struct dso *dso = dsos[i];
256
257		/*
258		 * Open dsos via dso__data_fd(), it opens the data
259		 * file and keep it open (unless open file limit).
260		 */
261		fd = dso__data_fd(dso, &machine);
262		TEST_ASSERT_VAL("failed to get fd", fd > 0);
263
264		if (i % 2) {
265			#define BUFSIZE 10
266			u8 buf[BUFSIZE];
267			ssize_t n;
268
269			n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE);
270			TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE);
271		}
272	}
273
274	/* verify the first one is already open */
275	TEST_ASSERT_VAL("dsos[0] is not open", dsos[0]->data.fd != -1);
276
277	/* open +1 dso to reach the allowed limit */
278	fd = dso__data_fd(dsos[i], &machine);
279	TEST_ASSERT_VAL("failed to get fd", fd > 0);
280
281	/* should force the first one to be closed */
282	TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1);
283
284	/* cleanup everything */
285	dsos__delete(dso_cnt);
286
287	/* Make sure we did not leak any file descriptor. */
288	nr_end = open_files_cnt();
289	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
290	TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
291	return 0;
292}
293
294int test__dso_data_reopen(void)
295{
296	struct machine machine;
297	long nr_end, nr = open_files_cnt();
298	int fd, fd_extra;
299
300#define dso_0 (dsos[0])
301#define dso_1 (dsos[1])
302#define dso_2 (dsos[2])
303
304	memset(&machine, 0, sizeof(machine));
305
306	/*
307	 * Test scenario:
308	 * - create 3 dso objects
309	 * - set process file descriptor limit to current
310	 *   files count + 3
311	 * - test that the first dso gets closed when we
312	 *   reach the files count limit
313	 */
314
315	/* Make sure we are able to open 3 fds anyway */
316	TEST_ASSERT_VAL("failed to set file limit",
317			!set_fd_limit((nr + 3)));
318
319	TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE));
320
321	/* open dso_0 */
322	fd = dso__data_fd(dso_0, &machine);
323	TEST_ASSERT_VAL("failed to get fd", fd > 0);
324
325	/* open dso_1 */
326	fd = dso__data_fd(dso_1, &machine);
327	TEST_ASSERT_VAL("failed to get fd", fd > 0);
328
329	/*
330	 * open extra file descriptor and we just
331	 * reached the files count limit
332	 */
333	fd_extra = open("/dev/null", O_RDONLY);
334	TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0);
335
336	/* open dso_2 */
337	fd = dso__data_fd(dso_2, &machine);
338	TEST_ASSERT_VAL("failed to get fd", fd > 0);
339
340	/*
341	 * dso_0 should get closed, because we reached
342	 * the file descriptor limit
343	 */
344	TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1);
345
346	/* open dso_0 */
347	fd = dso__data_fd(dso_0, &machine);
348	TEST_ASSERT_VAL("failed to get fd", fd > 0);
349
350	/*
351	 * dso_1 should get closed, because we reached
352	 * the file descriptor limit
353	 */
354	TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1);
355
356	/* cleanup everything */
357	close(fd_extra);
358	dsos__delete(3);
359
360	/* Make sure we did not leak any file descriptor. */
361	nr_end = open_files_cnt();
362	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
363	TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
364	return 0;
365}
366