• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4  *
5  * Updates:
6  * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
7  *
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <dirent.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 
18 #include <kbuffer.h>
19 
20 #include "tracefs.h"
21 #include "tracefs-local.h"
22 
23 static struct follow_event *root_followers;
24 static int nr_root_followers;
25 
26 static struct follow_event *root_missed_followers;
27 static int nr_root_missed_followers;
28 
29 struct cpu_iterate {
30 	struct tracefs_cpu *tcpu;
31 	struct tep_record record;
32 	struct tep_event *event;
33 	struct kbuffer *kbuf;
34 	void *page;
35 	int psize;
36 	int cpu;
37 };
38 
read_kbuf_record(struct cpu_iterate * cpu)39 static int read_kbuf_record(struct cpu_iterate *cpu)
40 {
41 	unsigned long long ts;
42 	void *ptr;
43 
44 	if (!cpu || !cpu->kbuf)
45 		return -1;
46 	ptr = kbuffer_read_event(cpu->kbuf, &ts);
47 	if (!ptr)
48 		return -1;
49 
50 	memset(&cpu->record, 0, sizeof(cpu->record));
51 	cpu->record.ts = ts;
52 	cpu->record.size = kbuffer_event_size(cpu->kbuf);
53 	cpu->record.record_size = kbuffer_curr_size(cpu->kbuf);
54 	cpu->record.missed_events = kbuffer_missed_events(cpu->kbuf);
55 	cpu->record.cpu = cpu->cpu;
56 	cpu->record.data = ptr;
57 	cpu->record.ref_count = 1;
58 
59 	kbuffer_next_event(cpu->kbuf, NULL);
60 
61 	return 0;
62 }
63 
read_next_page(struct tep_handle * tep,struct cpu_iterate * cpu)64 int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu)
65 {
66 	enum kbuffer_long_size long_size;
67 	enum kbuffer_endian endian;
68 	int r;
69 
70 	if (!cpu->tcpu)
71 		return -1;
72 
73 	r = tracefs_cpu_buffered_read(cpu->tcpu, cpu->page, true);
74 	/*
75 	 * tracefs_cpu_buffered_read() only reads in full subbuffer size,
76 	 * but this wants partial buffers as well. If the function returns
77 	 * empty (-1 for EAGAIN), try tracefs_cpu_read() next, as that can
78 	 * read partially filled buffers too, but isn't as efficient.
79 	 */
80 	if (r <= 0)
81 		r = tracefs_cpu_read(cpu->tcpu, cpu->page, true);
82 	if (r <= 0)
83 		return -1;
84 
85 	if (!cpu->kbuf) {
86 		if (tep_is_file_bigendian(tep))
87 			endian = KBUFFER_ENDIAN_BIG;
88 		else
89 			endian = KBUFFER_ENDIAN_LITTLE;
90 
91 		if (tep_get_header_page_size(tep) == 8)
92 			long_size = KBUFFER_LSIZE_8;
93 		else
94 			long_size = KBUFFER_LSIZE_4;
95 
96 		cpu->kbuf = kbuffer_alloc(long_size, endian);
97 		if (!cpu->kbuf)
98 			return -1;
99 	}
100 
101 	kbuffer_load_subbuffer(cpu->kbuf, cpu->page);
102 	if (kbuffer_subbuffer_size(cpu->kbuf) > r) {
103 		tracefs_warning("%s: page_size > %d", __func__, r);
104 		return -1;
105 	}
106 
107 	return 0;
108 }
109 
read_next_record(struct tep_handle * tep,struct cpu_iterate * cpu)110 int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu)
111 {
112 	int id;
113 
114 	do {
115 		while (!read_kbuf_record(cpu)) {
116 			id = tep_data_type(tep, &(cpu->record));
117 			cpu->event = tep_find_event(tep, id);
118 			if (cpu->event)
119 				return 0;
120 		}
121 	} while (!read_next_page(tep, cpu));
122 
123 	return -1;
124 }
125 
126 /**
127  * tracefs_follow_missed_events - Add callback for missed events for iterators
128  * @instance: The instance to follow
129  * @callback: The function to call when missed events is detected
130  * @callback_data: The data to pass to @callback
131  *
132  * This attaches a callback to an @instance or the root instance if @instance
133  * is NULL, where if tracefs_iterate_raw_events() is called, that if missed
134  * events are detected, it will call @callback, with the following parameters:
135  *  @event: The event pointer of the record with the missing events
136  *  @record; The event instance of @event.
137  *  @cpu: The cpu that the event happened on.
138  *  @callback_data: The same as @callback_data passed to the function.
139  *
140  * If the count of missing events is available, @record->missed_events
141  * will have a positive number holding the number of missed events since
142  * the last event on the same CPU, or just -1 if that number is unknown
143  * but missed events did happen.
144  *
145  * Returns 0 on success and -1 on error.
146  */
tracefs_follow_missed_events(struct tracefs_instance * instance,int (* callback)(struct tep_event *,struct tep_record *,int,void *),void * callback_data)147 int tracefs_follow_missed_events(struct tracefs_instance *instance,
148 				 int (*callback)(struct tep_event *,
149 						 struct tep_record *,
150 						 int, void *),
151 				 void *callback_data)
152 {
153 	struct follow_event **followers;
154 	struct follow_event *follower;
155 	struct follow_event follow;
156 	int *nr_followers;
157 
158 	follow.event = NULL;
159 	follow.callback = callback;
160 	follow.callback_data = callback_data;
161 
162 	if (instance) {
163 		followers = &instance->missed_followers;
164 		nr_followers = &instance->nr_missed_followers;
165 	} else {
166 		followers = &root_missed_followers;
167 		nr_followers = &nr_root_missed_followers;
168 	}
169 	follower = realloc(*followers, sizeof(*follower) *
170 			    ((*nr_followers) + 1));
171 	if (!follower)
172 		return -1;
173 
174 	*followers = follower;
175 	follower[(*nr_followers)++] = follow;
176 
177 	return 0;
178 }
179 
call_missed_events(struct tracefs_instance * instance,struct tep_event * event,struct tep_record * record,int cpu)180 static int call_missed_events(struct tracefs_instance *instance,
181 			      struct tep_event *event, struct tep_record *record, int cpu)
182 {
183 	struct follow_event *followers;
184 	int nr_followers;
185 	int ret = 0;
186 	int i;
187 
188 	if (instance) {
189 		followers = instance->missed_followers;
190 		nr_followers = instance->nr_missed_followers;
191 	} else {
192 		followers = root_missed_followers;
193 		nr_followers = nr_root_missed_followers;
194 	}
195 
196 	if (!followers)
197 		return 0;
198 
199 	for (i = 0; i < nr_followers; i++) {
200 		ret |= followers[i].callback(event, record,
201 					     cpu, followers[i].callback_data);
202 	}
203 
204 	return ret;
205 }
206 
call_followers(struct tracefs_instance * instance,struct tep_event * event,struct tep_record * record,int cpu)207 static int call_followers(struct tracefs_instance *instance,
208 			  struct tep_event *event, struct tep_record *record, int cpu)
209 {
210 	struct follow_event *followers;
211 	int nr_followers;
212 	int ret = 0;
213 	int i;
214 
215 	if (record->missed_events)
216 		ret = call_missed_events(instance, event, record, cpu);
217 	if (ret)
218 		return ret;
219 
220 	if (instance) {
221 		followers = instance->followers;
222 		nr_followers = instance->nr_followers;
223 	} else {
224 		followers = root_followers;
225 		nr_followers = nr_root_followers;
226 	}
227 
228 	if (!followers)
229 		return 0;
230 
231 	for (i = 0; i < nr_followers; i++) {
232 		if (followers[i].event == event)
233 			ret |= followers[i].callback(event, record,
234 						     cpu, followers[i].callback_data);
235 	}
236 
237 	return ret;
238 }
239 
read_cpu_pages(struct tep_handle * tep,struct tracefs_instance * instance,struct cpu_iterate * cpus,int count,int (* callback)(struct tep_event *,struct tep_record *,int,void *),void * callback_context,bool * keep_going)240 static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *instance,
241 			  struct cpu_iterate *cpus, int count,
242 			  int (*callback)(struct tep_event *,
243 					  struct tep_record *,
244 					  int, void *),
245 			  void *callback_context,
246 			  bool *keep_going)
247 {
248 	bool has_data = false;
249 	int ret;
250 	int i, j;
251 
252 	for (i = 0; i < count; i++) {
253 		ret = read_next_record(tep, cpus + i);
254 		if (!ret)
255 			has_data = true;
256 	}
257 
258 	while (has_data && *(volatile bool *)keep_going) {
259 		j = count;
260 		for (i = 0; i < count; i++) {
261 			if (!cpus[i].event)
262 				continue;
263 			if (j == count || cpus[j].record.ts > cpus[i].record.ts)
264 				j = i;
265 		}
266 		if (j < count) {
267 			if (call_followers(instance, cpus[j].event, &cpus[j].record, cpus[j].cpu))
268 				break;
269 			if (callback &&
270 			    callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
271 				break;
272 			cpus[j].event = NULL;
273 			read_next_record(tep, cpus + j);
274 		} else {
275 			has_data = false;
276 		}
277 	}
278 
279 	return 0;
280 }
281 
open_cpu_files(struct tracefs_instance * instance,cpu_set_t * cpus,int cpu_size,struct cpu_iterate ** all_cpus,int * count)282 static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus,
283 			  int cpu_size, struct cpu_iterate **all_cpus, int *count)
284 {
285 	struct tracefs_cpu *tcpu;
286 	struct cpu_iterate *tmp;
287 	int nr_cpus;
288 	int cpu;
289 	int i = 0;
290 
291 	*all_cpus = NULL;
292 
293 	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
294 	for (cpu = 0; cpu < nr_cpus; cpu++) {
295 		if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
296 			continue;
297 		tcpu = tracefs_cpu_open(instance, cpu, true);
298 		tmp = realloc(*all_cpus, (i + 1) * sizeof(*tmp));
299 		if (!tmp) {
300 			i--;
301 			goto error;
302 		}
303 
304 		*all_cpus = tmp;
305 
306 		memset(tmp + i, 0, sizeof(*tmp));
307 
308 		if (!tcpu)
309 			goto error;
310 
311 		tmp[i].tcpu = tcpu;
312 		tmp[i].cpu = cpu;
313 		tmp[i].psize = tracefs_cpu_read_size(tcpu);
314 		tmp[i].page =  malloc(tmp[i].psize);
315 
316 		if (!tmp[i++].page)
317 			goto error;
318 	}
319 	*count = i;
320 	return 0;
321  error:
322 	tmp = *all_cpus;
323 	for (; i >= 0; i--) {
324 		tracefs_cpu_close(tmp[i].tcpu);
325 		free(tmp[i].page);
326 	}
327 	free(tmp);
328 	*all_cpus = NULL;
329 	return -1;
330 }
331 
332 /**
333  * tracefs_follow_event - Add callback for specific events for iterators
334  * @tep: a handle to the trace event parser context
335  * @instance: The instance to follow
336  * @system: The system of the event to track
337  * @event_name: The name of the event to track
338  * @callback: The function to call when the event is hit in an iterator
339  * @callback_data: The data to pass to @callback
340  *
341  * This attaches a callback to an @instance or the root instance if @instance
342  * is NULL, where if tracefs_iterate_raw_events() is called, that if the specified
343  * event is hit, it will call @callback, with the following parameters:
344  *  @event: The event pointer that was found by @system and @event_name.
345  *  @record; The event instance of @event.
346  *  @cpu: The cpu that the event happened on.
347  *  @callback_data: The same as @callback_data passed to the function.
348  *
349  * Returns 0 on success and -1 on error.
350  */
tracefs_follow_event(struct tep_handle * tep,struct tracefs_instance * instance,const char * system,const char * event_name,int (* callback)(struct tep_event *,struct tep_record *,int,void *),void * callback_data)351 int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
352 			  const char *system, const char *event_name,
353 			  int (*callback)(struct tep_event *,
354 					  struct tep_record *,
355 					  int, void *),
356 			  void *callback_data)
357 {
358 	struct follow_event **followers;
359 	struct follow_event *follower;
360 	struct follow_event follow;
361 	int *nr_followers;
362 
363 	if (!tep) {
364 		errno = EINVAL;
365 		return -1;
366 	}
367 
368 	follow.event = tep_find_event_by_name(tep, system, event_name);
369 	if (!follow.event) {
370 		errno = ENOENT;
371 		return -1;
372 	}
373 
374 	follow.callback = callback;
375 	follow.callback_data = callback_data;
376 
377 	if (instance) {
378 		followers = &instance->followers;
379 		nr_followers = &instance->nr_followers;
380 	} else {
381 		followers = &root_followers;
382 		nr_followers = &nr_root_followers;
383 	}
384 	follower = realloc(*followers, sizeof(*follower) *
385 			    ((*nr_followers) + 1));
386 	if (!follower)
387 		return -1;
388 
389 	*followers = follower;
390 	follower[(*nr_followers)++] = follow;
391 
392 	return 0;
393 }
394 
395 static bool top_iterate_keep_going;
396 
397 /*
398  * tracefs_iterate_raw_events - Iterate through events in trace_pipe_raw,
399  *				per CPU trace buffers
400  * @tep: a handle to the trace event parser context
401  * @instance: ftrace instance, can be NULL for the top instance
402  * @cpus: Iterate only through the buffers of CPUs, set in the mask.
403  *	  If NULL, iterate through all CPUs.
404  * @cpu_size: size of @cpus set
405  * @callback: A user function, called for each record from the file
406  * @callback_context: A custom context, passed to the user callback function
407  *
408  * If the @callback returns non-zero, the iteration stops - in that case all
409  * records from the current page will be lost from future reads
410  * The events are iterated in sorted order, oldest first.
411  *
412  * Returns -1 in case of an error, or 0 otherwise
413  */
tracefs_iterate_raw_events(struct tep_handle * tep,struct tracefs_instance * instance,cpu_set_t * cpus,int cpu_size,int (* callback)(struct tep_event *,struct tep_record *,int,void *),void * callback_context)414 int tracefs_iterate_raw_events(struct tep_handle *tep,
415 				struct tracefs_instance *instance,
416 				cpu_set_t *cpus, int cpu_size,
417 				int (*callback)(struct tep_event *,
418 						struct tep_record *,
419 						int, void *),
420 				void *callback_context)
421 {
422 	bool *keep_going = instance ? &instance->iterate_keep_going :
423 				      &top_iterate_keep_going;
424 	struct follow_event *followers;
425 	struct cpu_iterate *all_cpus;
426 	int count = 0;
427 	int ret;
428 	int i;
429 
430 	(*(volatile bool *)keep_going) = true;
431 
432 	if (!tep)
433 		return -1;
434 
435 	if (instance)
436 		followers = instance->followers;
437 	else
438 		followers = root_followers;
439 	if (!callback && !followers)
440 		return -1;
441 
442 	ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count);
443 	if (ret < 0)
444 		goto out;
445 	ret = read_cpu_pages(tep, instance, all_cpus, count,
446 			     callback, callback_context,
447 			     keep_going);
448 
449 out:
450 	if (all_cpus) {
451 		for (i = 0; i < count; i++) {
452 			kbuffer_free(all_cpus[i].kbuf);
453 			tracefs_cpu_close(all_cpus[i].tcpu);
454 			free(all_cpus[i].page);
455 		}
456 		free(all_cpus);
457 	}
458 
459 	return ret;
460 }
461 
462 /**
463  * tracefs_iterate_stop - stop the iteration over the raw events.
464  * @instance: ftrace instance, can be NULL for top tracing instance.
465  */
tracefs_iterate_stop(struct tracefs_instance * instance)466 void tracefs_iterate_stop(struct tracefs_instance *instance)
467 {
468 	if (instance)
469 		instance->iterate_keep_going = false;
470 	else
471 		top_iterate_keep_going = false;
472 }
473 
add_list_string(char *** list,const char * name)474 static int add_list_string(char ***list, const char *name)
475 {
476 	char **tmp;
477 
478 	tmp = tracefs_list_add(*list, name);
479 	if (!tmp) {
480 		tracefs_list_free(*list);
481 		*list = NULL;
482 		return -1;
483 	}
484 
485 	*list = tmp;
486 	return 0;
487 }
488 
trace_append_file(const char * dir,const char * name)489 __hidden char *trace_append_file(const char *dir, const char *name)
490 {
491 	char *file;
492 	int ret;
493 
494 	ret = asprintf(&file, "%s/%s", dir, name);
495 
496 	return ret < 0 ? NULL : file;
497 }
498 
event_file(char ** path,const char * system,const char * event,const char * file)499 static int event_file(char **path, const char *system,
500 		      const char *event, const char *file)
501 {
502 	if (!system || !event || !file)
503 		return -1;
504 
505 	return asprintf(path, "events/%s/%s/%s",
506 			system, event, file);
507 }
508 
509 /**
510  * tracefs_event_get_file - return a file in an event directory
511  * @instance: The instance the event is in (NULL for top level)
512  * @system: The system name that the event file is in
513  * @event: The event name of the event
514  * @file: The name of the file in the event directory.
515  *
516  * Returns a path to a file in the event director.
517  * or NULL on error. The path returned must be freed with
518  * tracefs_put_tracing_file().
519  */
tracefs_event_get_file(struct tracefs_instance * instance,const char * system,const char * event,const char * file)520 char *tracefs_event_get_file(struct tracefs_instance *instance,
521 			     const char *system, const char *event,
522 			     const char *file)
523 {
524 	char *instance_path;
525 	char *path;
526 	int ret;
527 
528 	ret = event_file(&path, system, event, file);
529 	if (ret < 0)
530 		return NULL;
531 
532 	instance_path = tracefs_instance_get_file(instance, path);
533 	free(path);
534 
535 	return instance_path;
536 }
537 
538 /**
539  * tracefs_event_file_read - read the content from an event file
540  * @instance: The instance the event is in (NULL for top level)
541  * @system: The system name that the event file is in
542  * @event: The event name of the event
543  * @file: The name of the file in the event directory.
544  * @psize: the size of the content read.
545  *
546  * Reads the content of the event file that is passed via the
547  * arguments and returns the content.
548  *
549  * Return a string containing the content of the file or NULL
550  * on error. The string returned must be freed with free().
551  */
tracefs_event_file_read(struct tracefs_instance * instance,const char * system,const char * event,const char * file,int * psize)552 char *tracefs_event_file_read(struct tracefs_instance *instance,
553 			      const char *system, const char *event,
554 			      const char *file, int *psize)
555 {
556 	char *content;
557 	char *path;
558 	int ret;
559 
560 	ret = event_file(&path, system, event, file);
561 	if (ret < 0)
562 		return NULL;
563 
564 	content = tracefs_instance_file_read(instance, path, psize);
565 	free(path);
566 	return content;
567 }
568 
569 /**
570  * tracefs_event_file_write - write to an event file
571  * @instance: The instance the event is in (NULL for top level)
572  * @system: The system name that the event file is in
573  * @event: The event name of the event
574  * @file: The name of the file in the event directory.
575  * @str: The string to write into the file
576  *
577  * Writes the content of @str to a file in the instance directory.
578  * The content of the file will be overwritten by @str.
579  *
580  * Return 0 on success, and -1 on error.
581  */
tracefs_event_file_write(struct tracefs_instance * instance,const char * system,const char * event,const char * file,const char * str)582 int tracefs_event_file_write(struct tracefs_instance *instance,
583 			     const char *system, const char *event,
584 			     const char *file, const char *str)
585 {
586 	char *path;
587 	int ret;
588 
589 	ret = event_file(&path, system, event, file);
590 	if (ret < 0)
591 		return -1;
592 
593 	ret = tracefs_instance_file_write(instance, path, str);
594 	free(path);
595 	return ret;
596 }
597 
598 /**
599  * tracefs_event_file_append - write to an event file
600  * @instance: The instance the event is in (NULL for top level)
601  * @system: The system name that the event file is in
602  * @event: The event name of the event
603  * @file: The name of the file in the event directory.
604  * @str: The string to write into the file
605  *
606  * Writes the content of @str to a file in the instance directory.
607  * The content of @str will be appended to the content of the file.
608  * The current content should not be lost.
609  *
610  * Return 0 on success, and -1 on error.
611  */
tracefs_event_file_append(struct tracefs_instance * instance,const char * system,const char * event,const char * file,const char * str)612 int tracefs_event_file_append(struct tracefs_instance *instance,
613 			      const char *system, const char *event,
614 			      const char *file, const char *str)
615 {
616 	char *path;
617 	int ret;
618 
619 	ret = event_file(&path, system, event, file);
620 	if (ret < 0)
621 		return -1;
622 
623 	ret = tracefs_instance_file_append(instance, path, str);
624 	free(path);
625 	return ret;
626 }
627 
628 /**
629  * tracefs_event_file_clear - clear an event file
630  * @instance: The instance the event is in (NULL for top level)
631  * @system: The system name that the event file is in
632  * @event: The event name of the event
633  * @file: The name of the file in the event directory.
634  *
635  * Clears the content of the event file. That is, it is opened
636  * with O_TRUNC and then closed.
637  *
638  * Return 0 on success, and -1 on error.
639  */
tracefs_event_file_clear(struct tracefs_instance * instance,const char * system,const char * event,const char * file)640 int tracefs_event_file_clear(struct tracefs_instance *instance,
641 			     const char *system, const char *event,
642 			     const char *file)
643 {
644 	char *path;
645 	int ret;
646 
647 	ret = event_file(&path, system, event, file);
648 	if (ret < 0)
649 		return -1;
650 
651 	ret = tracefs_instance_file_clear(instance, path);
652 	free(path);
653 	return ret;
654 }
655 
656 /**
657  * tracefs_event_file_exits - test if a file exists
658  * @instance: The instance the event is in (NULL for top level)
659  * @system: The system name that the event file is in
660  * @event: The event name of the event
661  * @file: The name of the file in the event directory.
662  *
663  * Return true if the file exists, false if it odes not or
664  * an error occurred.
665  */
tracefs_event_file_exists(struct tracefs_instance * instance,const char * system,const char * event,const char * file)666 bool tracefs_event_file_exists(struct tracefs_instance *instance,
667 			       const char *system, const char *event,
668 			       const char *file)
669 {
670 	char *path;
671 	bool ret;
672 
673 	if (event_file(&path, system, event, file) < 0)
674 		return false;
675 
676 	ret = tracefs_file_exists(instance, path);
677 	free(path);
678 	return ret;
679 }
680 
681 /**
682  * tracefs_event_systems - return list of systems for tracing
683  * @tracing_dir: directory holding the "events" directory
684  *		 if NULL, top tracing directory is used
685  *
686  * Returns an allocated list of system names. Both the names and
687  * the list must be freed with tracefs_list_free()
688  * The list returned ends with a "NULL" pointer
689  */
tracefs_event_systems(const char * tracing_dir)690 char **tracefs_event_systems(const char *tracing_dir)
691 {
692 	struct dirent *dent;
693 	char **systems = NULL;
694 	char *events_dir;
695 	struct stat st;
696 	DIR *dir;
697 	int ret;
698 
699 	if (!tracing_dir)
700 		tracing_dir = tracefs_tracing_dir();
701 
702 	if (!tracing_dir)
703 		return NULL;
704 
705 	events_dir = trace_append_file(tracing_dir, "events");
706 	if (!events_dir)
707 		return NULL;
708 
709 	/*
710 	 * Search all the directories in the events directory,
711 	 * and collect the ones that have the "enable" file.
712 	 */
713 	ret = stat(events_dir, &st);
714 	if (ret < 0 || !S_ISDIR(st.st_mode))
715 		goto out_free;
716 
717 	dir = opendir(events_dir);
718 	if (!dir)
719 		goto out_free;
720 
721 	while ((dent = readdir(dir))) {
722 		const char *name = dent->d_name;
723 		char *enable;
724 		char *sys;
725 
726 		if (strcmp(name, ".") == 0 ||
727 		    strcmp(name, "..") == 0)
728 			continue;
729 
730 		sys = trace_append_file(events_dir, name);
731 		ret = stat(sys, &st);
732 		if (ret < 0 || !S_ISDIR(st.st_mode)) {
733 			free(sys);
734 			continue;
735 		}
736 
737 		enable = trace_append_file(sys, "enable");
738 
739 		ret = stat(enable, &st);
740 		if (ret >= 0) {
741 			if (add_list_string(&systems, name) < 0)
742 				goto out_free;
743 		}
744 		free(enable);
745 		free(sys);
746 	}
747 
748 	closedir(dir);
749 
750  out_free:
751 	free(events_dir);
752 	return systems;
753 }
754 
755 /**
756  * tracefs_system_events - return list of events for system
757  * @tracing_dir: directory holding the "events" directory
758  * @system: the system to return the events for
759  *
760  * Returns an allocated list of event names. Both the names and
761  * the list must be freed with tracefs_list_free()
762  * The list returned ends with a "NULL" pointer
763  */
tracefs_system_events(const char * tracing_dir,const char * system)764 char **tracefs_system_events(const char *tracing_dir, const char *system)
765 {
766 	struct dirent *dent;
767 	char **events = NULL;
768 	char *system_dir = NULL;
769 	struct stat st;
770 	DIR *dir;
771 	int ret;
772 
773 	if (!tracing_dir)
774 		tracing_dir = tracefs_tracing_dir();
775 
776 	if (!tracing_dir || !system)
777 		return NULL;
778 
779 	asprintf(&system_dir, "%s/events/%s", tracing_dir, system);
780 	if (!system_dir)
781 		return NULL;
782 
783 	ret = stat(system_dir, &st);
784 	if (ret < 0 || !S_ISDIR(st.st_mode))
785 		goto out_free;
786 
787 	dir = opendir(system_dir);
788 	if (!dir)
789 		goto out_free;
790 
791 	while ((dent = readdir(dir))) {
792 		const char *name = dent->d_name;
793 		char *event;
794 
795 		if (strcmp(name, ".") == 0 ||
796 		    strcmp(name, "..") == 0)
797 			continue;
798 
799 		event = trace_append_file(system_dir, name);
800 		ret = stat(event, &st);
801 		if (ret < 0 || !S_ISDIR(st.st_mode)) {
802 			free(event);
803 			continue;
804 		}
805 
806 		if (add_list_string(&events, name) < 0)
807 			goto out_free;
808 
809 		free(event);
810 	}
811 
812 	closedir(dir);
813 
814  out_free:
815 	free(system_dir);
816 
817 	return events;
818 }
819 
820 /**
821  * tracefs_tracers - returns an array of available tracers
822  * @tracing_dir: The directory that contains the tracing directory
823  *
824  * Returns an allocate list of plugins. The array ends with NULL
825  * Both the plugin names and array must be freed with tracefs_list_free()
826  */
tracefs_tracers(const char * tracing_dir)827 char **tracefs_tracers(const char *tracing_dir)
828 {
829 	char *available_tracers;
830 	struct stat st;
831 	char **plugins = NULL;
832 	char *buf;
833 	char *str, *saveptr;
834 	char *plugin;
835 	int slen;
836 	int len;
837 	int ret;
838 
839 	if (!tracing_dir)
840 		tracing_dir = tracefs_tracing_dir();
841 
842 	if (!tracing_dir)
843 		return NULL;
844 
845 	available_tracers = trace_append_file(tracing_dir, "available_tracers");
846 	if (!available_tracers)
847 		return NULL;
848 
849 	ret = stat(available_tracers, &st);
850 	if (ret < 0)
851 		goto out_free;
852 
853 	len = str_read_file(available_tracers, &buf, true);
854 	if (len <= 0)
855 		goto out_free;
856 
857 	for (str = buf; ; str = NULL) {
858 		plugin = strtok_r(str, " ", &saveptr);
859 		if (!plugin)
860 			break;
861 		slen = strlen(plugin);
862 		if (!slen)
863 			continue;
864 
865 		/* chop off any newlines */
866 		if (plugin[slen - 1] == '\n')
867 			plugin[slen - 1] = '\0';
868 
869 		/* Skip the non tracers */
870 		if (strcmp(plugin, "nop") == 0 ||
871 		    strcmp(plugin, "none") == 0)
872 			continue;
873 
874 		if (add_list_string(&plugins, plugin) < 0)
875 			break;
876 	}
877 	free(buf);
878 
879  out_free:
880 	free(available_tracers);
881 
882 	return plugins;
883 }
884 
load_events(struct tep_handle * tep,const char * tracing_dir,const char * system,bool check)885 static int load_events(struct tep_handle *tep,
886 		       const char *tracing_dir, const char *system, bool check)
887 {
888 	int ret = 0, failure = 0;
889 	char **events = NULL;
890 	struct stat st;
891 	int len = 0;
892 	int i;
893 
894 	if (!tracing_dir)
895 		tracing_dir = tracefs_tracing_dir();
896 
897 	events = tracefs_system_events(tracing_dir, system);
898 	if (!events)
899 		return -ENOENT;
900 
901 	for (i = 0; events[i]; i++) {
902 		char *format;
903 		char *buf;
904 
905 		ret = asprintf(&format, "%s/events/%s/%s/format",
906 			       tracing_dir, system, events[i]);
907 		if (ret < 0) {
908 			failure = -ENOMEM;
909 			break;
910 		}
911 
912 		ret = stat(format, &st);
913 		if (ret < 0)
914 			goto next_event;
915 
916 		/* check if event is already added, to avoid duplicates */
917 		if (check && tep_find_event_by_name(tep, system, events[i]))
918 			goto next_event;
919 
920 		len = str_read_file(format, &buf, true);
921 		if (len <= 0)
922 			goto next_event;
923 
924 		ret = tep_parse_event(tep, buf, len, system);
925 		free(buf);
926 next_event:
927 		free(format);
928 		if (ret)
929 			failure = ret;
930 	}
931 
932 	tracefs_list_free(events);
933 	return failure;
934 }
935 
trace_rescan_events(struct tep_handle * tep,const char * tracing_dir,const char * system)936 __hidden int trace_rescan_events(struct tep_handle *tep,
937 				const char *tracing_dir, const char *system)
938 {
939 	/* ToDo: add here logic for deleting removed events from tep handle */
940 	return load_events(tep, tracing_dir, system, true);
941 }
942 
trace_load_events(struct tep_handle * tep,const char * tracing_dir,const char * system)943 __hidden int trace_load_events(struct tep_handle *tep,
944 			       const char *tracing_dir, const char *system)
945 {
946 	return load_events(tep, tracing_dir, system, false);
947 }
948 
get_tep_event(struct tep_handle * tep,const char * system,const char * name)949 __hidden struct tep_event *get_tep_event(struct tep_handle *tep,
950 					 const char *system, const char *name)
951 {
952 	struct tep_event *event;
953 
954 	/* Check if event exists in the system */
955 	if (!tracefs_event_file_exists(NULL, system, name, "format"))
956 		return NULL;
957 
958 	/* If the event is already loaded in the tep, return it */
959 	event = tep_find_event_by_name(tep, system, name);
960 	if (event)
961 		return event;
962 
963 	/* Try to load any new events from the given system */
964 	if (trace_rescan_events(tep, NULL, system))
965 		return NULL;
966 
967 	return tep_find_event_by_name(tep, system, name);
968 }
969 
read_header(struct tep_handle * tep,const char * tracing_dir)970 static int read_header(struct tep_handle *tep, const char *tracing_dir)
971 {
972 	struct stat st;
973 	char *header;
974 	char *buf;
975 	int len;
976 	int ret = -1;
977 
978 	header = trace_append_file(tracing_dir, "events/header_page");
979 
980 	ret = stat(header, &st);
981 	if (ret < 0)
982 		goto out;
983 
984 	len = str_read_file(header, &buf, true);
985 	if (len <= 0)
986 		goto out;
987 
988 	tep_parse_header_page(tep, buf, len, sizeof(long));
989 
990 	free(buf);
991 
992 	ret = 0;
993  out:
994 	free(header);
995 	return ret;
996 }
997 
contains(const char * name,const char * const * names)998 static bool contains(const char *name, const char * const *names)
999 {
1000 	if (!names)
1001 		return false;
1002 	for (; *names; names++)
1003 		if (strcmp(name, *names) == 0)
1004 			return true;
1005 	return false;
1006 }
1007 
load_kallsyms(struct tep_handle * tep)1008 static void load_kallsyms(struct tep_handle *tep)
1009 {
1010 	char *buf;
1011 
1012 	if (str_read_file("/proc/kallsyms", &buf, false) <= 0)
1013 		return;
1014 
1015 	tep_parse_kallsyms(tep, buf);
1016 	free(buf);
1017 }
1018 
load_saved_cmdlines(const char * tracing_dir,struct tep_handle * tep,bool warn)1019 static int load_saved_cmdlines(const char *tracing_dir,
1020 			       struct tep_handle *tep, bool warn)
1021 {
1022 	char *path;
1023 	char *buf;
1024 	int ret;
1025 
1026 	path = trace_append_file(tracing_dir, "saved_cmdlines");
1027 	if (!path)
1028 		return -1;
1029 
1030 	ret = str_read_file(path, &buf, false);
1031 	free(path);
1032 	if (ret <= 0)
1033 		return -1;
1034 
1035 	ret = tep_parse_saved_cmdlines(tep, buf);
1036 	free(buf);
1037 
1038 	return ret;
1039 }
1040 
load_printk_formats(const char * tracing_dir,struct tep_handle * tep)1041 static void load_printk_formats(const char *tracing_dir,
1042 				struct tep_handle *tep)
1043 {
1044 	char *path;
1045 	char *buf;
1046 	int ret;
1047 
1048 	path = trace_append_file(tracing_dir, "printk_formats");
1049 	if (!path)
1050 		return;
1051 
1052 	ret = str_read_file(path, &buf, false);
1053 	free(path);
1054 	if (ret <= 0)
1055 		return;
1056 
1057 	tep_parse_printk_formats(tep, buf);
1058 	free(buf);
1059 }
1060 
1061 /*
1062  * Do a best effort attempt to load kallsyms, saved_cmdlines and
1063  * printk_formats. If they can not be loaded, then this will not
1064  * do the mappings. But this does not fail the loading of events.
1065  */
load_mappings(const char * tracing_dir,struct tep_handle * tep)1066 static void load_mappings(const char *tracing_dir,
1067 			  struct tep_handle *tep)
1068 {
1069 	load_kallsyms(tep);
1070 
1071 	/* If there's no tracing_dir no reason to go further */
1072 	if (!tracing_dir)
1073 		tracing_dir = tracefs_tracing_dir();
1074 
1075 	if (!tracing_dir)
1076 		return;
1077 
1078 	load_saved_cmdlines(tracing_dir, tep, false);
1079 	load_printk_formats(tracing_dir, tep);
1080 }
1081 
tracefs_load_cmdlines(const char * tracing_dir,struct tep_handle * tep)1082 int tracefs_load_cmdlines(const char *tracing_dir, struct tep_handle *tep)
1083 {
1084 
1085 	if (!tracing_dir)
1086 		tracing_dir = tracefs_tracing_dir();
1087 
1088 	if (!tracing_dir)
1089 		return -1;
1090 
1091 	return load_saved_cmdlines(tracing_dir, tep, true);
1092 }
1093 
fill_local_events_system(const char * tracing_dir,struct tep_handle * tep,const char * const * sys_names,int * parsing_failures)1094 static int fill_local_events_system(const char *tracing_dir,
1095 				    struct tep_handle *tep,
1096 				    const char * const *sys_names,
1097 				    int *parsing_failures)
1098 {
1099 	char **systems = NULL;
1100 	int ret;
1101 	int i;
1102 
1103 	if (!tracing_dir)
1104 		tracing_dir = tracefs_tracing_dir();
1105 	if (!tracing_dir)
1106 		return -1;
1107 
1108 	systems = tracefs_event_systems(tracing_dir);
1109 	if (!systems)
1110 		return -1;
1111 
1112 	ret = read_header(tep, tracing_dir);
1113 	if (ret < 0) {
1114 		ret = -1;
1115 		goto out;
1116 	}
1117 
1118 	if (parsing_failures)
1119 		*parsing_failures = 0;
1120 
1121 	for (i = 0; systems[i]; i++) {
1122 		if (sys_names && !contains(systems[i], sys_names))
1123 			continue;
1124 		ret = trace_load_events(tep, tracing_dir, systems[i]);
1125 		if (ret && parsing_failures)
1126 			(*parsing_failures)++;
1127 	}
1128 
1129 	/* Include ftrace, as it is excluded for not having "enable" file */
1130 	if (!sys_names || contains("ftrace", sys_names))
1131 		trace_load_events(tep, tracing_dir, "ftrace");
1132 
1133 	load_mappings(tracing_dir, tep);
1134 
1135 	/* always succeed because parsing failures are not critical */
1136 	ret = 0;
1137 out:
1138 	tracefs_list_free(systems);
1139 	return ret;
1140 }
1141 
set_tep_cpus(const char * tracing_dir,struct tep_handle * tep)1142 static void set_tep_cpus(const char *tracing_dir, struct tep_handle *tep)
1143 {
1144 	struct stat st;
1145 	char path[PATH_MAX];
1146 	int cpus = sysconf(_SC_NPROCESSORS_CONF);
1147 	int max_cpu = 0;
1148 	int ret;
1149 	int i;
1150 
1151 	if (!tracing_dir)
1152 		tracing_dir = tracefs_tracing_dir();
1153 
1154 	/*
1155 	 * Paranoid: in case sysconf() above does not work.
1156 	 * And we also only care about the number of tracing
1157 	 * buffers that exist. If cpus is 32, but the top half
1158 	 * is offline, there may only be 16 tracing buffers.
1159 	 * That's what we want to know.
1160 	 */
1161 	for (i = 0; !cpus || i < cpus; i++) {
1162 		snprintf(path, PATH_MAX, "%s/per_cpu/cpu%d", tracing_dir, i);
1163 		ret = stat(path, &st);
1164 		if (!ret && S_ISDIR(st.st_mode))
1165 			max_cpu = i + 1;
1166 		else if (i >= cpus)
1167 			break;
1168 	}
1169 
1170 	if (!max_cpu)
1171 		max_cpu = cpus;
1172 
1173 	tep_set_cpus(tep, max_cpu);
1174 }
1175 
1176 /**
1177  * tracefs_local_events_system - create a tep from the events of the specified subsystem.
1178  *
1179  * @tracing_dir: The directory that contains the events.
1180  * @sys_name: Array of system names, to load the events from.
1181  * The last element from the array must be NULL
1182  *
1183  * Returns a tep structure that contains the tep local to
1184  * the system.
1185  */
tracefs_local_events_system(const char * tracing_dir,const char * const * sys_names)1186 struct tep_handle *tracefs_local_events_system(const char *tracing_dir,
1187 					       const char * const *sys_names)
1188 {
1189 	struct tep_handle *tep = NULL;
1190 
1191 	tep = tep_alloc();
1192 	if (!tep)
1193 		return NULL;
1194 
1195 	if (fill_local_events_system(tracing_dir, tep, sys_names, NULL)) {
1196 		tep_free(tep);
1197 		tep = NULL;
1198 	}
1199 
1200 	set_tep_cpus(tracing_dir, tep);
1201 
1202 	/* Set the long size for this tep handle */
1203 	tep_set_long_size(tep, tep_get_header_page_size(tep));
1204 
1205 	return tep;
1206 }
1207 
1208 /**
1209  * tracefs_local_events - create a tep from the events on system
1210  * @tracing_dir: The directory that contains the events.
1211  *
1212  * Returns a tep structure that contains the teps local to
1213  * the system.
1214  */
tracefs_local_events(const char * tracing_dir)1215 struct tep_handle *tracefs_local_events(const char *tracing_dir)
1216 {
1217 	return tracefs_local_events_system(tracing_dir, NULL);
1218 }
1219 
1220 /**
1221  * tracefs_fill_local_events - Fill a tep with the events on system
1222  * @tracing_dir: The directory that contains the events.
1223  * @tep: Allocated tep handler which will be filled
1224  * @parsing_failures: return number of failures while parsing the event files
1225  *
1226  * Returns whether the operation succeeded
1227  */
tracefs_fill_local_events(const char * tracing_dir,struct tep_handle * tep,int * parsing_failures)1228 int tracefs_fill_local_events(const char *tracing_dir,
1229 			       struct tep_handle *tep, int *parsing_failures)
1230 {
1231 	return fill_local_events_system(tracing_dir, tep,
1232 					NULL, parsing_failures);
1233 }
1234 
match(const char * str,regex_t * re)1235 static bool match(const char *str, regex_t *re)
1236 {
1237 	return regexec(re, str, 0, NULL, 0) == 0;
1238 }
1239 
1240 enum event_state {
1241 	STATE_INIT,
1242 	STATE_ENABLED,
1243 	STATE_DISABLED,
1244 	STATE_MIXED,
1245 	STATE_ERROR,
1246 };
1247 
read_event_state(struct tracefs_instance * instance,const char * file,enum event_state * state)1248 static int read_event_state(struct tracefs_instance *instance, const char *file,
1249 			    enum event_state *state)
1250 {
1251 	char *val;
1252 	int ret = 0;
1253 
1254 	if (*state == STATE_ERROR)
1255 		return -1;
1256 
1257 	val = tracefs_instance_file_read(instance, file, NULL);
1258 	if (!val)
1259 		return -1;
1260 
1261 	switch (val[0]) {
1262 	case '0':
1263 		switch (*state) {
1264 		case STATE_INIT:
1265 			*state = STATE_DISABLED;
1266 			break;
1267 		case STATE_ENABLED:
1268 			*state = STATE_MIXED;
1269 			break;
1270 		default:
1271 			break;
1272 		}
1273 		break;
1274 	case '1':
1275 		switch (*state) {
1276 		case STATE_INIT:
1277 			*state = STATE_ENABLED;
1278 			break;
1279 		case STATE_DISABLED:
1280 			*state = STATE_MIXED;
1281 			break;
1282 		default:
1283 			break;
1284 		}
1285 		break;
1286 	case 'X':
1287 		*state = STATE_MIXED;
1288 		break;
1289 	default:
1290 		*state = TRACEFS_ERROR;
1291 		ret = -1;
1292 		break;
1293 	}
1294 	free(val);
1295 
1296 	return ret;
1297 }
1298 
enable_disable_event(struct tracefs_instance * instance,const char * system,const char * event,bool enable,enum event_state * state)1299 static int enable_disable_event(struct tracefs_instance *instance,
1300 				const char *system, const char *event,
1301 				bool enable, enum event_state *state)
1302 {
1303 	const char *str = enable ? "1" : "0";
1304 	char *system_event;
1305 	int ret;
1306 
1307 	ret = asprintf(&system_event, "events/%s/%s/enable", system, event);
1308 	if (ret < 0)
1309 		return ret;
1310 
1311 	if (state)
1312 		ret = read_event_state(instance, system_event, state);
1313 	else
1314 		ret = tracefs_instance_file_write(instance, system_event, str);
1315 	free(system_event);
1316 
1317 	return ret;
1318 }
1319 
enable_disable_system(struct tracefs_instance * instance,const char * system,bool enable,enum event_state * state)1320 static int enable_disable_system(struct tracefs_instance *instance,
1321 				 const char *system, bool enable,
1322 				 enum event_state *state)
1323 {
1324 	const char *str = enable ? "1" : "0";
1325 	char *system_path;
1326 	int ret;
1327 
1328 	ret = asprintf(&system_path, "events/%s/enable", system);
1329 	if (ret < 0)
1330 		return ret;
1331 
1332 	if (state)
1333 		ret = read_event_state(instance, system_path, state);
1334 	else
1335 		ret = tracefs_instance_file_write(instance, system_path, str);
1336 	free(system_path);
1337 
1338 	return ret;
1339 }
1340 
enable_disable_all(struct tracefs_instance * instance,bool enable)1341 static int enable_disable_all(struct tracefs_instance *instance,
1342 			      bool enable)
1343 {
1344 	const char *str = enable ? "1" : "0";
1345 	int ret;
1346 
1347 	ret = tracefs_instance_file_write(instance, "events/enable", str);
1348 	return ret < 0 ? ret : 0;
1349 }
1350 
make_regex(regex_t * re,const char * match)1351 static int make_regex(regex_t *re, const char *match)
1352 {
1353 	int len = strlen(match);
1354 	char str[len + 3];
1355 	char *p = &str[0];
1356 
1357 	if (!len || match[0] != '^')
1358 		*(p++) = '^';
1359 
1360 	strcpy(p, match);
1361 	p += len;
1362 
1363 	if (!len || match[len-1] != '$')
1364 		*(p++) = '$';
1365 
1366 	*p = '\0';
1367 
1368 	return regcomp(re, str, REG_ICASE|REG_NOSUB);
1369 }
1370 
event_enable_disable(struct tracefs_instance * instance,const char * system,const char * event,bool enable,enum event_state * state)1371 static int event_enable_disable(struct tracefs_instance *instance,
1372 				const char *system, const char *event,
1373 				bool enable, enum event_state *state)
1374 {
1375 	regex_t system_re, event_re;
1376 	char **systems;
1377 	char **events = NULL;
1378 	int ret = -1;
1379 	int s, e;
1380 
1381 	/* Handle all events first */
1382 	if (!system && !event)
1383 		return enable_disable_all(instance, enable);
1384 
1385 	systems = tracefs_event_systems(NULL);
1386 	if (!systems)
1387 		goto out_free;
1388 
1389 	if (system) {
1390 		ret = make_regex(&system_re, system);
1391 		if (ret < 0)
1392 			goto out_free;
1393 	}
1394 	if (event) {
1395 		ret = make_regex(&event_re, event);
1396 		if (ret < 0) {
1397 			if (system)
1398 				regfree(&system_re);
1399 			goto out_free;
1400 		}
1401 	}
1402 
1403 	ret = -1;
1404 	for (s = 0; systems[s]; s++) {
1405 		if (system && !match(systems[s], &system_re))
1406 			continue;
1407 
1408 		/* Check for the short cut first */
1409 		if (!event) {
1410 			ret = enable_disable_system(instance, systems[s], enable, state);
1411 			if (ret < 0)
1412 				break;
1413 			ret = 0;
1414 			continue;
1415 		}
1416 
1417 		events = tracefs_system_events(NULL, systems[s]);
1418 		if (!events)
1419 			continue; /* Error? */
1420 
1421 		for (e = 0; events[e]; e++) {
1422 			if (!match(events[e], &event_re))
1423 				continue;
1424 			ret = enable_disable_event(instance, systems[s],
1425 						   events[e], enable, state);
1426 			if (ret < 0)
1427 				break;
1428 			ret = 0;
1429 		}
1430 		tracefs_list_free(events);
1431 		events = NULL;
1432 	}
1433 	if (system)
1434 		regfree(&system_re);
1435 	if (event)
1436 		regfree(&event_re);
1437 
1438  out_free:
1439 	tracefs_list_free(systems);
1440 	tracefs_list_free(events);
1441 	return ret;
1442 }
1443 
1444 /**
1445  * tracefs_event_enable - enable specified events
1446  * @instance: ftrace instance, can be NULL for the top instance
1447  * @system: A regex of a system (NULL to match all systems)
1448  * @event: A regex of the event in the system (NULL to match all events)
1449  *
1450  * This will enable events that match the @system and @event.
1451  * If both @system and @event are NULL, then it will enable all events.
1452  * If @system is NULL, it will look at all systems for matching events
1453  * to @event.
1454  * If @event is NULL, then it will enable all events in the systems
1455  * that match @system.
1456  *
1457  * Returns 0 on success, and -1 if it encountered an error,
1458  * or if no events matched. If no events matched, then -1 is set
1459  * but errno will not be.
1460  */
tracefs_event_enable(struct tracefs_instance * instance,const char * system,const char * event)1461 int tracefs_event_enable(struct tracefs_instance *instance,
1462 			 const char *system, const char *event)
1463 {
1464 	return event_enable_disable(instance, system, event, true, NULL);
1465 }
1466 
tracefs_event_disable(struct tracefs_instance * instance,const char * system,const char * event)1467 int tracefs_event_disable(struct tracefs_instance *instance,
1468 			  const char *system, const char *event)
1469 {
1470 	return event_enable_disable(instance, system, event, false, NULL);
1471 }
1472 
1473 /**
1474  * tracefs_event_is_enabled - return if the event is enabled or not
1475  * @instance: ftrace instance, can be NULL for the top instance
1476  * @system: The name of the system to check
1477  * @event: The name of the event to check
1478  *
1479  * Checks is an event or multiple events are enabled.
1480  *
1481  * If @system is NULL, then it will check all the systems where @event is
1482  * a match.
1483  *
1484  * If @event is NULL, then it will check all events where @system is a match.
1485  *
1486  * If both @system and @event are NULL, then it will check all events
1487  *
1488  * Returns TRACEFS_ALL_ENABLED if all matching are enabled.
1489  * Returns TRACEFS_SOME_ENABLED if some are enabled and some are not
1490  * Returns TRACEFS_ALL_DISABLED if none of the events are enabled.
1491  * Returns TRACEFS_ERROR if there is an error reading the events.
1492  */
1493 enum tracefs_enable_state
tracefs_event_is_enabled(struct tracefs_instance * instance,const char * system,const char * event)1494 tracefs_event_is_enabled(struct tracefs_instance *instance,
1495 			 const char *system, const char *event)
1496 {
1497 	enum event_state state = STATE_INIT;
1498 	int ret;
1499 
1500 	ret = event_enable_disable(instance, system, event, false, &state);
1501 
1502 	if (ret < 0)
1503 		return TRACEFS_ERROR;
1504 
1505 	switch (state) {
1506 	case STATE_ENABLED:
1507 		return TRACEFS_ALL_ENABLED;
1508 	case STATE_DISABLED:
1509 		return TRACEFS_ALL_DISABLED;
1510 	case STATE_MIXED:
1511 		return TRACEFS_SOME_ENABLED;
1512 	default:
1513 		return TRACEFS_ERROR;
1514 	}
1515 }
1516