• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 #include "../perf.h"
3 #include "util.h"
4 #include "debug.h"
5 #include <api/fs/fs.h>
6 #include <sys/mman.h>
7 #include <sys/stat.h>
8 #include <sys/utsname.h>
9 #include <dirent.h>
10 #include <inttypes.h>
11 #include <signal.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <limits.h>
17 #include <linux/kernel.h>
18 #include <linux/log2.h>
19 #include <linux/time64.h>
20 #include <unistd.h>
21 #include "strlist.h"
22 
23 /*
24  * XXX We need to find a better place for these things...
25  */
26 unsigned int page_size;
27 int cacheline_size;
28 
29 int sysctl_perf_event_max_stack = PERF_MAX_STACK_DEPTH;
30 int sysctl_perf_event_max_contexts_per_stack = PERF_MAX_CONTEXTS_PER_STACK;
31 
32 bool test_attr__enabled;
33 
34 bool perf_host  = true;
35 bool perf_guest = false;
36 
event_attr_init(struct perf_event_attr * attr)37 void event_attr_init(struct perf_event_attr *attr)
38 {
39 	if (!perf_host)
40 		attr->exclude_host  = 1;
41 	if (!perf_guest)
42 		attr->exclude_guest = 1;
43 	/* to capture ABI version */
44 	attr->size = sizeof(*attr);
45 }
46 
mkdir_p(char * path,mode_t mode)47 int mkdir_p(char *path, mode_t mode)
48 {
49 	struct stat st;
50 	int err;
51 	char *d = path;
52 
53 	if (*d != '/')
54 		return -1;
55 
56 	if (stat(path, &st) == 0)
57 		return 0;
58 
59 	while (*++d == '/');
60 
61 	while ((d = strchr(d, '/'))) {
62 		*d = '\0';
63 		err = stat(path, &st) && mkdir(path, mode);
64 		*d++ = '/';
65 		if (err)
66 			return -1;
67 		while (*d == '/')
68 			++d;
69 	}
70 	return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
71 }
72 
rm_rf(const char * path)73 int rm_rf(const char *path)
74 {
75 	DIR *dir;
76 	int ret = 0;
77 	struct dirent *d;
78 	char namebuf[PATH_MAX];
79 
80 	dir = opendir(path);
81 	if (dir == NULL)
82 		return 0;
83 
84 	while ((d = readdir(dir)) != NULL && !ret) {
85 		struct stat statbuf;
86 
87 		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
88 			continue;
89 
90 		scnprintf(namebuf, sizeof(namebuf), "%s/%s",
91 			  path, d->d_name);
92 
93 		/* We have to check symbolic link itself */
94 		ret = lstat(namebuf, &statbuf);
95 		if (ret < 0) {
96 			pr_debug("stat failed: %s\n", namebuf);
97 			break;
98 		}
99 
100 		if (S_ISDIR(statbuf.st_mode))
101 			ret = rm_rf(namebuf);
102 		else
103 			ret = unlink(namebuf);
104 	}
105 	closedir(dir);
106 
107 	if (ret < 0)
108 		return ret;
109 
110 	return rmdir(path);
111 }
112 
113 /* A filter which removes dot files */
lsdir_no_dot_filter(const char * name __maybe_unused,struct dirent * d)114 bool lsdir_no_dot_filter(const char *name __maybe_unused, struct dirent *d)
115 {
116 	return d->d_name[0] != '.';
117 }
118 
119 /* lsdir reads a directory and store it in strlist */
lsdir(const char * name,bool (* filter)(const char *,struct dirent *))120 struct strlist *lsdir(const char *name,
121 		      bool (*filter)(const char *, struct dirent *))
122 {
123 	struct strlist *list = NULL;
124 	DIR *dir;
125 	struct dirent *d;
126 
127 	dir = opendir(name);
128 	if (!dir)
129 		return NULL;
130 
131 	list = strlist__new(NULL, NULL);
132 	if (!list) {
133 		errno = ENOMEM;
134 		goto out;
135 	}
136 
137 	while ((d = readdir(dir)) != NULL) {
138 		if (!filter || filter(name, d))
139 			strlist__add(list, d->d_name);
140 	}
141 
142 out:
143 	closedir(dir);
144 	return list;
145 }
146 
slow_copyfile(const char * from,const char * to,struct nsinfo * nsi)147 static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
148 {
149 	int err = -1;
150 	char *line = NULL;
151 	size_t n;
152 	FILE *from_fp, *to_fp;
153 	struct nscookie nsc;
154 
155 	nsinfo__mountns_enter(nsi, &nsc);
156 	from_fp = fopen(from, "r");
157 	nsinfo__mountns_exit(&nsc);
158 	if (from_fp == NULL)
159 		goto out;
160 
161 	to_fp = fopen(to, "w");
162 	if (to_fp == NULL)
163 		goto out_fclose_from;
164 
165 	while (getline(&line, &n, from_fp) > 0)
166 		if (fputs(line, to_fp) == EOF)
167 			goto out_fclose_to;
168 	err = 0;
169 out_fclose_to:
170 	fclose(to_fp);
171 	free(line);
172 out_fclose_from:
173 	fclose(from_fp);
174 out:
175 	return err;
176 }
177 
copyfile_offset(int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size)178 int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
179 {
180 	void *ptr;
181 	loff_t pgoff;
182 
183 	pgoff = off_in & ~(page_size - 1);
184 	off_in -= pgoff;
185 
186 	ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff);
187 	if (ptr == MAP_FAILED)
188 		return -1;
189 
190 	while (size) {
191 		ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out);
192 		if (ret < 0 && errno == EINTR)
193 			continue;
194 		if (ret <= 0)
195 			break;
196 
197 		size -= ret;
198 		off_in += ret;
199 		off_out += ret;
200 	}
201 	munmap(ptr, off_in + size);
202 
203 	return size ? -1 : 0;
204 }
205 
copyfile_mode_ns(const char * from,const char * to,mode_t mode,struct nsinfo * nsi)206 static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
207 			    struct nsinfo *nsi)
208 {
209 	int fromfd, tofd;
210 	struct stat st;
211 	int err;
212 	char *tmp = NULL, *ptr = NULL;
213 	struct nscookie nsc;
214 
215 	nsinfo__mountns_enter(nsi, &nsc);
216 	err = stat(from, &st);
217 	nsinfo__mountns_exit(&nsc);
218 	if (err)
219 		goto out;
220 	err = -1;
221 
222 	/* extra 'x' at the end is to reserve space for '.' */
223 	if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
224 		tmp = NULL;
225 		goto out;
226 	}
227 	ptr = strrchr(tmp, '/');
228 	if (!ptr)
229 		goto out;
230 	ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1);
231 	*ptr = '.';
232 
233 	tofd = mkstemp(tmp);
234 	if (tofd < 0)
235 		goto out;
236 
237 	if (fchmod(tofd, mode))
238 		goto out_close_to;
239 
240 	if (st.st_size == 0) { /* /proc? do it slowly... */
241 		err = slow_copyfile(from, tmp, nsi);
242 		goto out_close_to;
243 	}
244 
245 	nsinfo__mountns_enter(nsi, &nsc);
246 	fromfd = open(from, O_RDONLY);
247 	nsinfo__mountns_exit(&nsc);
248 	if (fromfd < 0)
249 		goto out_close_to;
250 
251 	err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size);
252 
253 	close(fromfd);
254 out_close_to:
255 	close(tofd);
256 	if (!err)
257 		err = link(tmp, to);
258 	unlink(tmp);
259 out:
260 	free(tmp);
261 	return err;
262 }
263 
copyfile_ns(const char * from,const char * to,struct nsinfo * nsi)264 int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
265 {
266 	return copyfile_mode_ns(from, to, 0755, nsi);
267 }
268 
copyfile_mode(const char * from,const char * to,mode_t mode)269 int copyfile_mode(const char *from, const char *to, mode_t mode)
270 {
271 	return copyfile_mode_ns(from, to, mode, NULL);
272 }
273 
copyfile(const char * from,const char * to)274 int copyfile(const char *from, const char *to)
275 {
276 	return copyfile_mode(from, to, 0755);
277 }
278 
ion(bool is_read,int fd,void * buf,size_t n)279 static ssize_t ion(bool is_read, int fd, void *buf, size_t n)
280 {
281 	void *buf_start = buf;
282 	size_t left = n;
283 
284 	while (left) {
285 		/* buf must be treated as const if !is_read. */
286 		ssize_t ret = is_read ? read(fd, buf, left) :
287 					write(fd, buf, left);
288 
289 		if (ret < 0 && errno == EINTR)
290 			continue;
291 		if (ret <= 0)
292 			return ret;
293 
294 		left -= ret;
295 		buf  += ret;
296 	}
297 
298 	BUG_ON((size_t)(buf - buf_start) != n);
299 	return n;
300 }
301 
302 /*
303  * Read exactly 'n' bytes or return an error.
304  */
readn(int fd,void * buf,size_t n)305 ssize_t readn(int fd, void *buf, size_t n)
306 {
307 	return ion(true, fd, buf, n);
308 }
309 
310 /*
311  * Write exactly 'n' bytes or return an error.
312  */
writen(int fd,const void * buf,size_t n)313 ssize_t writen(int fd, const void *buf, size_t n)
314 {
315 	/* ion does not modify buf. */
316 	return ion(false, fd, (void *)buf, n);
317 }
318 
hex_width(u64 v)319 size_t hex_width(u64 v)
320 {
321 	size_t n = 1;
322 
323 	while ((v >>= 4))
324 		++n;
325 
326 	return n;
327 }
328 
hex(char ch)329 static int hex(char ch)
330 {
331 	if ((ch >= '0') && (ch <= '9'))
332 		return ch - '0';
333 	if ((ch >= 'a') && (ch <= 'f'))
334 		return ch - 'a' + 10;
335 	if ((ch >= 'A') && (ch <= 'F'))
336 		return ch - 'A' + 10;
337 	return -1;
338 }
339 
340 /*
341  * While we find nice hex chars, build a long_val.
342  * Return number of chars processed.
343  */
hex2u64(const char * ptr,u64 * long_val)344 int hex2u64(const char *ptr, u64 *long_val)
345 {
346 	const char *p = ptr;
347 	*long_val = 0;
348 
349 	while (*p) {
350 		const int hex_val = hex(*p);
351 
352 		if (hex_val < 0)
353 			break;
354 
355 		*long_val = (*long_val << 4) | hex_val;
356 		p++;
357 	}
358 
359 	return p - ptr;
360 }
361 
perf_event_paranoid(void)362 int perf_event_paranoid(void)
363 {
364 	int value;
365 
366 	if (sysctl__read_int("kernel/perf_event_paranoid", &value))
367 		return INT_MAX;
368 
369 	return value;
370 }
371 static int
fetch_ubuntu_kernel_version(unsigned int * puint)372 fetch_ubuntu_kernel_version(unsigned int *puint)
373 {
374 	ssize_t len;
375 	size_t line_len = 0;
376 	char *ptr, *line = NULL;
377 	int version, patchlevel, sublevel, err;
378 	FILE *vsig;
379 
380 	if (!puint)
381 		return 0;
382 
383 	vsig = fopen("/proc/version_signature", "r");
384 	if (!vsig) {
385 		pr_debug("Open /proc/version_signature failed: %s\n",
386 			 strerror(errno));
387 		return -1;
388 	}
389 
390 	len = getline(&line, &line_len, vsig);
391 	fclose(vsig);
392 	err = -1;
393 	if (len <= 0) {
394 		pr_debug("Reading from /proc/version_signature failed: %s\n",
395 			 strerror(errno));
396 		goto errout;
397 	}
398 
399 	ptr = strrchr(line, ' ');
400 	if (!ptr) {
401 		pr_debug("Parsing /proc/version_signature failed: %s\n", line);
402 		goto errout;
403 	}
404 
405 	err = sscanf(ptr + 1, "%d.%d.%d",
406 		     &version, &patchlevel, &sublevel);
407 	if (err != 3) {
408 		pr_debug("Unable to get kernel version from /proc/version_signature '%s'\n",
409 			 line);
410 		goto errout;
411 	}
412 
413 	*puint = (version << 16) + (patchlevel << 8) + sublevel;
414 	err = 0;
415 errout:
416 	free(line);
417 	return err;
418 }
419 
420 int
fetch_kernel_version(unsigned int * puint,char * str,size_t str_size)421 fetch_kernel_version(unsigned int *puint, char *str,
422 		     size_t str_size)
423 {
424 	struct utsname utsname;
425 	int version, patchlevel, sublevel, err;
426 	bool int_ver_ready = false;
427 
428 	if (access("/proc/version_signature", R_OK) == 0)
429 		if (!fetch_ubuntu_kernel_version(puint))
430 			int_ver_ready = true;
431 
432 	if (uname(&utsname))
433 		return -1;
434 
435 	if (str && str_size) {
436 		strncpy(str, utsname.release, str_size);
437 		str[str_size - 1] = '\0';
438 	}
439 
440 	if (!puint || int_ver_ready)
441 		return 0;
442 
443 	err = sscanf(utsname.release, "%d.%d.%d",
444 		     &version, &patchlevel, &sublevel);
445 
446 	if (err != 3) {
447 		pr_debug("Unable to get kernel version from uname '%s'\n",
448 			 utsname.release);
449 		return -1;
450 	}
451 
452 	*puint = (version << 16) + (patchlevel << 8) + sublevel;
453 	return 0;
454 }
455 
perf_tip(const char * dirpath)456 const char *perf_tip(const char *dirpath)
457 {
458 	struct strlist *tips;
459 	struct str_node *node;
460 	char *tip = NULL;
461 	struct strlist_config conf = {
462 		.dirname = dirpath,
463 		.file_only = true,
464 	};
465 
466 	tips = strlist__new("tips.txt", &conf);
467 	if (tips == NULL)
468 		return errno == ENOENT ? NULL :
469 			"Tip: check path of tips.txt or get more memory! ;-p";
470 
471 	if (strlist__nr_entries(tips) == 0)
472 		goto out;
473 
474 	node = strlist__entry(tips, random() % strlist__nr_entries(tips));
475 	if (asprintf(&tip, "Tip: %s", node->s) < 0)
476 		tip = (char *)"Tip: get more memory! ;-)";
477 
478 out:
479 	strlist__delete(tips);
480 
481 	return tip;
482 }
483