brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · c875567 Raw
704 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>4 */5#include <dirent.h>6#include <mntent.h>7#include <stdio.h>8#include <stdlib.h>9#include <string.h>10#include <stdarg.h>11#include <sys/types.h>12#include <sys/stat.h>13#include <sys/wait.h>14#include <fcntl.h>15#include <unistd.h>16#include <errno.h>17#include <stdbool.h>18#include <linux/list.h>19#include <linux/kernel.h>20#include <linux/zalloc.h>21#include <internal/lib.h> // page_size22#include <sys/param.h>23 24#include "trace-event.h"25#include "tracepoint.h"26#include <api/fs/tracing_path.h>27#include "evsel.h"28#include "debug.h"29#include "util.h"30 31#define VERSION "0.6"32#define MAX_EVENT_LENGTH 51233 34static int output_fd;35 36struct tracepoint_path {37	char *system;38	char *name;39	struct tracepoint_path *next;40};41 42/* unfortunately, you can not stat debugfs or proc files for size */43static int record_file(const char *file, ssize_t hdr_sz)44{45	unsigned long long size = 0;46	char buf[BUFSIZ], *sizep;47	off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);48	int r, fd;49	int err = -EIO;50 51	fd = open(file, O_RDONLY);52	if (fd < 0) {53		pr_debug("Can't read '%s'", file);54		return -errno;55	}56 57	/* put in zeros for file size, then fill true size later */58	if (hdr_sz) {59		if (write(output_fd, &size, hdr_sz) != hdr_sz)60			goto out;61	}62 63	do {64		r = read(fd, buf, BUFSIZ);65		if (r > 0) {66			size += r;67			if (write(output_fd, buf, r) != r)68				goto out;69		}70	} while (r > 0);71 72	/* ugh, handle big-endian hdr_size == 4 */73	sizep = (char*)&size;74	if (host_is_bigendian())75		sizep += sizeof(u64) - hdr_sz;76 77	if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) {78		pr_debug("writing file size failed\n");79		goto out;80	}81 82	err = 0;83out:84	close(fd);85	return err;86}87 88static int record_header_files(void)89{90	char *path = get_events_file("header_page");91	struct stat st;92	int err = -EIO;93 94	if (!path) {95		pr_debug("can't get tracing/events/header_page");96		return -ENOMEM;97	}98 99	if (stat(path, &st) < 0) {100		pr_debug("can't read '%s'", path);101		goto out;102	}103 104	if (write(output_fd, "header_page", 12) != 12) {105		pr_debug("can't write header_page\n");106		goto out;107	}108 109	if (record_file(path, 8) < 0) {110		pr_debug("can't record header_page file\n");111		goto out;112	}113 114	put_events_file(path);115 116	path = get_events_file("header_event");117	if (!path) {118		pr_debug("can't get tracing/events/header_event");119		err = -ENOMEM;120		goto out;121	}122 123	if (stat(path, &st) < 0) {124		pr_debug("can't read '%s'", path);125		goto out;126	}127 128	if (write(output_fd, "header_event", 13) != 13) {129		pr_debug("can't write header_event\n");130		goto out;131	}132 133	if (record_file(path, 8) < 0) {134		pr_debug("can't record header_event file\n");135		goto out;136	}137 138	err = 0;139out:140	put_events_file(path);141	return err;142}143 144static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)145{146	while (tps) {147		if (!strcmp(sys, tps->name))148			return true;149		tps = tps->next;150	}151 152	return false;153}154 155#define for_each_event_tps(dir, dent, tps)			\156	while ((dent = readdir(dir)))				\157		if (dent->d_type == DT_DIR &&			\158		    (strcmp(dent->d_name, ".")) &&		\159		    (strcmp(dent->d_name, "..")))		\160 161static int copy_event_system(const char *sys, struct tracepoint_path *tps)162{163	struct dirent *dent;164	struct stat st;165	char *format;166	DIR *dir;167	int count = 0;168	int ret;169	int err;170 171	dir = opendir(sys);172	if (!dir) {173		pr_debug("can't read directory '%s'", sys);174		return -errno;175	}176 177	for_each_event_tps(dir, dent, tps) {178		if (!name_in_tp_list(dent->d_name, tps))179			continue;180 181		if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {182			err = -ENOMEM;183			goto out;184		}185		ret = stat(format, &st);186		free(format);187		if (ret < 0)188			continue;189		count++;190	}191 192	if (write(output_fd, &count, 4) != 4) {193		err = -EIO;194		pr_debug("can't write count\n");195		goto out;196	}197 198	rewinddir(dir);199	for_each_event_tps(dir, dent, tps) {200		if (!name_in_tp_list(dent->d_name, tps))201			continue;202 203		if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {204			err = -ENOMEM;205			goto out;206		}207		ret = stat(format, &st);208 209		if (ret >= 0) {210			err = record_file(format, 8);211			if (err) {212				free(format);213				goto out;214			}215		}216		free(format);217	}218	err = 0;219out:220	closedir(dir);221	return err;222}223 224static int record_ftrace_files(struct tracepoint_path *tps)225{226	char *path;227	int ret;228 229	path = get_events_file("ftrace");230	if (!path) {231		pr_debug("can't get tracing/events/ftrace");232		return -ENOMEM;233	}234 235	ret = copy_event_system(path, tps);236 237	put_tracing_file(path);238 239	return ret;240}241 242static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)243{244	while (tps) {245		if (!strcmp(sys, tps->system))246			return true;247		tps = tps->next;248	}249 250	return false;251}252 253static int record_event_files(struct tracepoint_path *tps)254{255	struct dirent *dent;256	struct stat st;257	char *path;258	char *sys;259	DIR *dir;260	int count = 0;261	int ret;262	int err;263 264	path = get_tracing_file("events");265	if (!path) {266		pr_debug("can't get tracing/events");267		return -ENOMEM;268	}269 270	dir = opendir(path);271	if (!dir) {272		err = -errno;273		pr_debug("can't read directory '%s'", path);274		goto out;275	}276 277	for_each_event_tps(dir, dent, tps) {278		if (strcmp(dent->d_name, "ftrace") == 0 ||279		    !system_in_tp_list(dent->d_name, tps))280			continue;281 282		count++;283	}284 285	if (write(output_fd, &count, 4) != 4) {286		err = -EIO;287		pr_debug("can't write count\n");288		goto out;289	}290 291	rewinddir(dir);292	for_each_event_tps(dir, dent, tps) {293		if (strcmp(dent->d_name, "ftrace") == 0 ||294		    !system_in_tp_list(dent->d_name, tps))295			continue;296 297		if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) {298			err = -ENOMEM;299			goto out;300		}301		ret = stat(sys, &st);302		if (ret >= 0) {303			ssize_t size = strlen(dent->d_name) + 1;304 305			if (write(output_fd, dent->d_name, size) != size ||306			    copy_event_system(sys, tps) < 0) {307				err = -EIO;308				free(sys);309				goto out;310			}311		}312		free(sys);313	}314	err = 0;315out:316	if (dir)317		closedir(dir);318	put_tracing_file(path);319 320	return err;321}322 323static int record_proc_kallsyms(void)324{325	unsigned long long size = 0;326	/*327	 * Just to keep older perf.data file parsers happy, record a zero328	 * sized kallsyms file, i.e. do the same thing that was done when329	 * /proc/kallsyms (or something specified via --kallsyms, in a330	 * different path) couldn't be read.331	 */332	return write(output_fd, &size, 4) != 4 ? -EIO : 0;333}334 335static int record_ftrace_printk(void)336{337	unsigned int size;338	char *path;339	struct stat st;340	int ret, err = 0;341 342	path = get_tracing_file("printk_formats");343	if (!path) {344		pr_debug("can't get tracing/printk_formats");345		return -ENOMEM;346	}347 348	ret = stat(path, &st);349	if (ret < 0) {350		/* not found */351		size = 0;352		if (write(output_fd, &size, 4) != 4)353			err = -EIO;354		goto out;355	}356	err = record_file(path, 4);357 358out:359	put_tracing_file(path);360	return err;361}362 363static int record_saved_cmdline(void)364{365	unsigned long long size;366	char *path;367	struct stat st;368	int ret, err = 0;369 370	path = get_tracing_file("saved_cmdlines");371	if (!path) {372		pr_debug("can't get tracing/saved_cmdline");373		return -ENOMEM;374	}375 376	ret = stat(path, &st);377	if (ret < 0) {378		/* not found */379		size = 0;380		if (write(output_fd, &size, 8) != 8)381			err = -EIO;382		goto out;383	}384	err = record_file(path, 8);385 386out:387	put_tracing_file(path);388	return err;389}390 391static void392put_tracepoints_path(struct tracepoint_path *tps)393{394	while (tps) {395		struct tracepoint_path *t = tps;396 397		tps = tps->next;398		zfree(&t->name);399		zfree(&t->system);400		free(t);401	}402}403 404static struct tracepoint_path *tracepoint_id_to_path(u64 config)405{406	struct tracepoint_path *path = NULL;407	DIR *sys_dir, *evt_dir;408	struct dirent *sys_dirent, *evt_dirent;409	char id_buf[24];410	int fd;411	u64 id;412	char evt_path[MAXPATHLEN];413	char *dir_path;414 415	sys_dir = tracing_events__opendir();416	if (!sys_dir)417		return NULL;418 419	for_each_subsystem(sys_dir, sys_dirent) {420		dir_path = get_events_file(sys_dirent->d_name);421		if (!dir_path)422			continue;423		evt_dir = opendir(dir_path);424		if (!evt_dir)425			goto next;426 427		for_each_event(dir_path, evt_dir, evt_dirent) {428 429			scnprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,430				  evt_dirent->d_name);431			fd = open(evt_path, O_RDONLY);432			if (fd < 0)433				continue;434			if (read(fd, id_buf, sizeof(id_buf)) < 0) {435				close(fd);436				continue;437			}438			close(fd);439			id = atoll(id_buf);440			if (id == config) {441				put_events_file(dir_path);442				closedir(evt_dir);443				closedir(sys_dir);444				path = zalloc(sizeof(*path));445				if (!path)446					return NULL;447				if (asprintf(&path->system, "%.*s",448					     MAX_EVENT_LENGTH, sys_dirent->d_name) < 0) {449					free(path);450					return NULL;451				}452				if (asprintf(&path->name, "%.*s",453					     MAX_EVENT_LENGTH, evt_dirent->d_name) < 0) {454					zfree(&path->system);455					free(path);456					return NULL;457				}458				return path;459			}460		}461		closedir(evt_dir);462next:463		put_events_file(dir_path);464	}465 466	closedir(sys_dir);467	return NULL;468}469 470char *tracepoint_id_to_name(u64 config)471{472	struct tracepoint_path *path = tracepoint_id_to_path(config);473	char *buf = NULL;474 475	if (path && asprintf(&buf, "%s:%s", path->system, path->name) < 0)476		buf = NULL;477 478	put_tracepoints_path(path);479	return buf;480}481 482static struct tracepoint_path *tracepoint_name_to_path(const char *name)483{484	struct tracepoint_path *path = zalloc(sizeof(*path));485	char *str = strchr(name, ':');486 487	if (path == NULL || str == NULL) {488		free(path);489		return NULL;490	}491 492	path->system = strndup(name, str - name);493	path->name = strdup(str+1);494 495	if (path->system == NULL || path->name == NULL) {496		zfree(&path->system);497		zfree(&path->name);498		zfree(&path);499	}500 501	return path;502}503 504static struct tracepoint_path *505get_tracepoints_path(struct list_head *pattrs)506{507	struct tracepoint_path path, *ppath = &path;508	struct evsel *pos;509	int nr_tracepoints = 0;510 511	list_for_each_entry(pos, pattrs, core.node) {512		if (pos->core.attr.type != PERF_TYPE_TRACEPOINT)513			continue;514		++nr_tracepoints;515 516		if (pos->name) {517			ppath->next = tracepoint_name_to_path(pos->name);518			if (ppath->next)519				goto next;520 521			if (strchr(pos->name, ':') == NULL)522				goto try_id;523 524			goto error;525		}526 527try_id:528		ppath->next = tracepoint_id_to_path(pos->core.attr.config);529		if (!ppath->next) {530error:531			pr_debug("No memory to alloc tracepoints list\n");532			put_tracepoints_path(path.next);533			return NULL;534		}535next:536		ppath = ppath->next;537	}538 539	return nr_tracepoints > 0 ? path.next : NULL;540}541 542bool have_tracepoints(struct list_head *pattrs)543{544	struct evsel *pos;545 546	list_for_each_entry(pos, pattrs, core.node)547		if (pos->core.attr.type == PERF_TYPE_TRACEPOINT)548			return true;549 550	return false;551}552 553static int tracing_data_header(void)554{555	char buf[20];556	ssize_t size;557 558	/* just guessing this is someone's birthday.. ;) */559	buf[0] = 23;560	buf[1] = 8;561	buf[2] = 68;562	memcpy(buf + 3, "tracing", 7);563 564	if (write(output_fd, buf, 10) != 10)565		return -1;566 567	size = strlen(VERSION) + 1;568	if (write(output_fd, VERSION, size) != size)569		return -1;570 571	/* save endian */572	if (host_is_bigendian())573		buf[0] = 1;574	else575		buf[0] = 0;576 577	if (write(output_fd, buf, 1) != 1)578		return -1;579 580	/* save size of long */581	buf[0] = sizeof(long);582	if (write(output_fd, buf, 1) != 1)583		return -1;584 585	/* save page_size */586	if (write(output_fd, &page_size, 4) != 4)587		return -1;588 589	return 0;590}591 592struct tracing_data *tracing_data_get(struct list_head *pattrs,593				      int fd, bool temp)594{595	struct tracepoint_path *tps;596	struct tracing_data *tdata;597	int err;598 599	output_fd = fd;600 601	tps = get_tracepoints_path(pattrs);602	if (!tps)603		return NULL;604 605	tdata = malloc(sizeof(*tdata));606	if (!tdata)607		return NULL;608 609	tdata->temp = temp;610	tdata->size = 0;611 612	if (temp) {613		int temp_fd;614 615		snprintf(tdata->temp_file, sizeof(tdata->temp_file),616			 "/tmp/perf-XXXXXX");617		if (!mkstemp(tdata->temp_file)) {618			pr_debug("Can't make temp file");619			free(tdata);620			return NULL;621		}622 623		temp_fd = open(tdata->temp_file, O_RDWR);624		if (temp_fd < 0) {625			pr_debug("Can't read '%s'", tdata->temp_file);626			free(tdata);627			return NULL;628		}629 630		/*631		 * Set the temp file the default output, so all the632		 * tracing data are stored into it.633		 */634		output_fd = temp_fd;635	}636 637	err = tracing_data_header();638	if (err)639		goto out;640	err = record_header_files();641	if (err)642		goto out;643	err = record_ftrace_files(tps);644	if (err)645		goto out;646	err = record_event_files(tps);647	if (err)648		goto out;649	err = record_proc_kallsyms();650	if (err)651		goto out;652	err = record_ftrace_printk();653	if (err)654		goto out;655	err = record_saved_cmdline();656 657out:658	/*659	 * All tracing data are stored by now, we can restore660	 * the default output file in case we used temp file.661	 */662	if (temp) {663		tdata->size = lseek(output_fd, 0, SEEK_CUR);664		close(output_fd);665		output_fd = fd;666	}667 668	if (err)669		zfree(&tdata);670 671	put_tracepoints_path(tps);672	return tdata;673}674 675int tracing_data_put(struct tracing_data *tdata)676{677	int err = 0;678 679	if (tdata->temp) {680		err = record_file(tdata->temp_file, 0);681		unlink(tdata->temp_file);682	}683 684	free(tdata);685	return err;686}687 688int read_tracing_data(int fd, struct list_head *pattrs)689{690	int err;691	struct tracing_data *tdata;692 693	/*694	 * We work over the real file, so we can write data695	 * directly, no temp file is needed.696	 */697	tdata = tracing_data_get(pattrs, fd, false);698	if (!tdata)699		return -ENOMEM;700 701	err = tracing_data_put(tdata);702	return err;703}704