mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 16:54:21 +00:00 
			
		
		
		
	perf kwork: New tool to trace time properties of kernel work (such as softirq, and workqueue)
The 'perf kwork' tool is used to trace time properties of kernel work
(such as irq, softirq, and workqueue), including runtime, latency, and
timehist, using the infrastructure in the perf tools to allow tracing
extra targets.
This is the first commit to reuse the 'perf record' framework code to
implement a simple record function, kwork is not supported currently.
Test cases:
  # perf
   usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS]
   The most commonly used perf commands are:
  <SNIP>
     iostat          Show I/O performance metrics
     kallsyms        Searches running kernel for symbols
     kmem            Tool to trace/measure kernel memory properties
     kvm             Tool to trace/measure kvm guest os
     kwork           Tool to trace/measure kernel work properties (latencies)
     list            List all symbolic event types
     lock            Analyze lock events
     mem             Profile memory accesses
     record          Run a command and record its profile into perf.data
  <SNIP>
   See 'perf help COMMAND' for more information on a specific command.
  # perf kwork
   Usage: perf kwork [<options>] {record}
      -D, --dump-raw-trace  dump raw trace in ASCII
      -f, --force           don't complain, do it
      -k, --kwork <kwork>   list of kwork to profile
      -v, --verbose         be more verbose (show symbol address, etc)
  # perf kwork record -- sleep 1
  [ perf record: Woken up 0 times to write data ]
  [ perf record: Captured and wrote 1.787 MB perf.data ]
Signed-off-by: Yang Jihong <yangjihong1@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Clarke <pc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220709015033.38326-2-yangjihong1@huawei.com
[ Add {} for multiline if blocks ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									ade5353950
								
							
						
					
					
						commit
						0f70d8e9db
					
				
					 7 changed files with 250 additions and 0 deletions
				
			
		|  | @ -25,6 +25,7 @@ perf-y += builtin-data.o | |||
| perf-y += builtin-version.o | ||||
| perf-y += builtin-c2c.o | ||||
| perf-y += builtin-daemon.o | ||||
| perf-y += builtin-kwork.o | ||||
| 
 | ||||
| perf-$(CONFIG_TRACE) += builtin-trace.o | ||||
| perf-$(CONFIG_LIBELF) += builtin-probe.o | ||||
|  |  | |||
							
								
								
									
										43
									
								
								tools/perf/Documentation/perf-kwork.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tools/perf/Documentation/perf-kwork.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| perf-kowrk(1) | ||||
| ============= | ||||
| 
 | ||||
| NAME | ||||
| ---- | ||||
| perf-kwork - Tool to trace/measure kernel work properties (latencies) | ||||
| 
 | ||||
| SYNOPSIS | ||||
| -------- | ||||
| [verse] | ||||
| 'perf kwork' {record} | ||||
| 
 | ||||
| DESCRIPTION | ||||
| ----------- | ||||
| There are several variants of 'perf kwork': | ||||
| 
 | ||||
|   'perf kwork record <command>' to record the kernel work | ||||
|   of an arbitrary workload. | ||||
| 
 | ||||
|     Example usage: | ||||
|         perf kwork record -- sleep 1 | ||||
| 
 | ||||
| OPTIONS | ||||
| ------- | ||||
| -D:: | ||||
| --dump-raw-trace=:: | ||||
| 	Display verbose dump of the sched data. | ||||
| 
 | ||||
| -f:: | ||||
| --force:: | ||||
| 	Don't complain, do it. | ||||
| 
 | ||||
| -k:: | ||||
| --kwork:: | ||||
| 	List of kwork to profile | ||||
| 
 | ||||
| -v:: | ||||
| --verbose:: | ||||
| 	Be more verbose. (show symbol address, etc) | ||||
| 
 | ||||
| SEE ALSO | ||||
| -------- | ||||
| linkperf:perf-record[1] | ||||
							
								
								
									
										162
									
								
								tools/perf/builtin-kwork.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								tools/perf/builtin-kwork.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * builtin-kwork.c | ||||
|  * | ||||
|  * Copyright (c) 2022  Huawei Inc,  Yang Jihong <yangjihong1@huawei.com> | ||||
|  */ | ||||
| 
 | ||||
| #include "builtin.h" | ||||
| 
 | ||||
| #include "util/data.h" | ||||
| #include "util/kwork.h" | ||||
| #include "util/debug.h" | ||||
| #include "util/symbol.h" | ||||
| #include "util/thread.h" | ||||
| #include "util/string2.h" | ||||
| #include "util/callchain.h" | ||||
| #include "util/evsel_fprintf.h" | ||||
| 
 | ||||
| #include <subcmd/pager.h> | ||||
| #include <subcmd/parse-options.h> | ||||
| 
 | ||||
| #include <errno.h> | ||||
| #include <inttypes.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/time64.h> | ||||
| #include <linux/zalloc.h> | ||||
| 
 | ||||
| static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = { | ||||
| }; | ||||
| 
 | ||||
| static void setup_event_list(struct perf_kwork *kwork, | ||||
| 			     const struct option *options, | ||||
| 			     const char * const usage_msg[]) | ||||
| { | ||||
| 	int i; | ||||
| 	struct kwork_class *class; | ||||
| 	char *tmp, *tok, *str; | ||||
| 
 | ||||
| 	if (kwork->event_list_str == NULL) | ||||
| 		goto null_event_list_str; | ||||
| 
 | ||||
| 	str = strdup(kwork->event_list_str); | ||||
| 	for (tok = strtok_r(str, ", ", &tmp); | ||||
| 	     tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||||
| 		for (i = 0; i < KWORK_CLASS_MAX; i++) { | ||||
| 			class = kwork_class_supported_list[i]; | ||||
| 			if (strcmp(tok, class->name) == 0) { | ||||
| 				list_add_tail(&class->list, &kwork->class_list); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (i == KWORK_CLASS_MAX) { | ||||
| 			usage_with_options_msg(usage_msg, options, | ||||
| 					       "Unknown --event key: `%s'", tok); | ||||
| 		} | ||||
| 	} | ||||
| 	free(str); | ||||
| 
 | ||||
| null_event_list_str: | ||||
| 	/*
 | ||||
| 	 * config all kwork events if not specified | ||||
| 	 */ | ||||
| 	if (list_empty(&kwork->class_list)) { | ||||
| 		for (i = 0; i < KWORK_CLASS_MAX; i++) { | ||||
| 			list_add_tail(&kwork_class_supported_list[i]->list, | ||||
| 				      &kwork->class_list); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pr_debug("Config event list:"); | ||||
| 	list_for_each_entry(class, &kwork->class_list, list) | ||||
| 		pr_debug(" %s", class->name); | ||||
| 	pr_debug("\n"); | ||||
| } | ||||
| 
 | ||||
| static int perf_kwork__record(struct perf_kwork *kwork, | ||||
| 			      int argc, const char **argv) | ||||
| { | ||||
| 	const char **rec_argv; | ||||
| 	unsigned int rec_argc, i, j; | ||||
| 	struct kwork_class *class; | ||||
| 
 | ||||
| 	const char *const record_args[] = { | ||||
| 		"record", | ||||
| 		"-a", | ||||
| 		"-R", | ||||
| 		"-m", "1024", | ||||
| 		"-c", "1", | ||||
| 	}; | ||||
| 
 | ||||
| 	rec_argc = ARRAY_SIZE(record_args) + argc - 1; | ||||
| 
 | ||||
| 	list_for_each_entry(class, &kwork->class_list, list) | ||||
| 		rec_argc += 2 * class->nr_tracepoints; | ||||
| 
 | ||||
| 	rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||||
| 	if (rec_argv == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(record_args); i++) | ||||
| 		rec_argv[i] = strdup(record_args[i]); | ||||
| 
 | ||||
| 	list_for_each_entry(class, &kwork->class_list, list) { | ||||
| 		for (j = 0; j < class->nr_tracepoints; j++) { | ||||
| 			rec_argv[i++] = strdup("-e"); | ||||
| 			rec_argv[i++] = strdup(class->tp_handlers[j].name); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (j = 1; j < (unsigned int)argc; j++, i++) | ||||
| 		rec_argv[i] = argv[j]; | ||||
| 
 | ||||
| 	BUG_ON(i != rec_argc); | ||||
| 
 | ||||
| 	pr_debug("record comm: "); | ||||
| 	for (j = 0; j < rec_argc; j++) | ||||
| 		pr_debug("%s ", rec_argv[j]); | ||||
| 	pr_debug("\n"); | ||||
| 
 | ||||
| 	return cmd_record(i, rec_argv); | ||||
| } | ||||
| 
 | ||||
| int cmd_kwork(int argc, const char **argv) | ||||
| { | ||||
| 	static struct perf_kwork kwork = { | ||||
| 		.class_list          = LIST_HEAD_INIT(kwork.class_list), | ||||
| 		.force               = false, | ||||
| 		.event_list_str      = NULL, | ||||
| 	}; | ||||
| 	const struct option kwork_options[] = { | ||||
| 	OPT_INCR('v', "verbose", &verbose, | ||||
| 		 "be more verbose (show symbol address, etc)"), | ||||
| 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||||
| 		    "dump raw trace in ASCII"), | ||||
| 	OPT_STRING('k', "kwork", &kwork.event_list_str, "kwork", | ||||
| 		   "list of kwork to profile"), | ||||
| 	OPT_BOOLEAN('f', "force", &kwork.force, "don't complain, do it"), | ||||
| 	OPT_END() | ||||
| 	}; | ||||
| 	const char *kwork_usage[] = { | ||||
| 		NULL, | ||||
| 		NULL | ||||
| 	}; | ||||
| 	const char *const kwork_subcommands[] = { | ||||
| 		"record", NULL | ||||
| 	}; | ||||
| 
 | ||||
| 	argc = parse_options_subcommand(argc, argv, kwork_options, | ||||
| 					kwork_subcommands, kwork_usage, | ||||
| 					PARSE_OPT_STOP_AT_NON_OPTION); | ||||
| 	if (!argc) | ||||
| 		usage_with_options(kwork_usage, kwork_options); | ||||
| 
 | ||||
| 	setup_event_list(&kwork, kwork_options, kwork_usage); | ||||
| 
 | ||||
| 	if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) | ||||
| 		return perf_kwork__record(&kwork, argc, argv); | ||||
| 	else | ||||
| 		usage_with_options(kwork_usage, kwork_options); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -38,6 +38,7 @@ int cmd_mem(int argc, const char **argv); | |||
| int cmd_data(int argc, const char **argv); | ||||
| int cmd_ftrace(int argc, const char **argv); | ||||
| int cmd_daemon(int argc, const char **argv); | ||||
| int cmd_kwork(int argc, const char **argv); | ||||
| 
 | ||||
| int find_scripts(char **scripts_array, char **scripts_path_array, int num, | ||||
| 		 int pathlen); | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ perf-iostat			mainporcelain common | |||
| perf-kallsyms			mainporcelain common | ||||
| perf-kmem			mainporcelain common | ||||
| perf-kvm			mainporcelain common | ||||
| perf-kwork			mainporcelain common | ||||
| perf-list			mainporcelain common | ||||
| perf-lock			mainporcelain common | ||||
| perf-mem			mainporcelain common | ||||
|  |  | |||
|  | @ -91,6 +91,7 @@ static struct cmd_struct commands[] = { | |||
| 	{ "data",	cmd_data,	0 }, | ||||
| 	{ "ftrace",	cmd_ftrace,	0 }, | ||||
| 	{ "daemon",	cmd_daemon,	0 }, | ||||
| 	{ "kwork",	cmd_kwork,	0 }, | ||||
| }; | ||||
| 
 | ||||
| struct pager_config { | ||||
|  |  | |||
							
								
								
									
										41
									
								
								tools/perf/util/kwork.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tools/perf/util/kwork.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| #ifndef PERF_UTIL_KWORK_H | ||||
| #define PERF_UTIL_KWORK_H | ||||
| 
 | ||||
| #include "perf.h" | ||||
| 
 | ||||
| #include "util/tool.h" | ||||
| #include "util/event.h" | ||||
| #include "util/evlist.h" | ||||
| #include "util/session.h" | ||||
| #include "util/time-utils.h" | ||||
| 
 | ||||
| #include <linux/list.h> | ||||
| #include <linux/bitmap.h> | ||||
| 
 | ||||
| enum kwork_class_type { | ||||
| 	KWORK_CLASS_MAX, | ||||
| }; | ||||
| 
 | ||||
| struct kwork_class { | ||||
| 	struct list_head list; | ||||
| 	const char *name; | ||||
| 	enum kwork_class_type type; | ||||
| 
 | ||||
| 	unsigned int nr_tracepoints; | ||||
| 	const struct evsel_str_handler *tp_handlers; | ||||
| }; | ||||
| 
 | ||||
| struct perf_kwork { | ||||
| 	/*
 | ||||
| 	 * metadata | ||||
| 	 */ | ||||
| 	struct list_head class_list; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * options for command | ||||
| 	 */ | ||||
| 	bool force; | ||||
| 	const char *event_list_str; | ||||
| }; | ||||
| 
 | ||||
| #endif  /* PERF_UTIL_KWORK_H */ | ||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Yang Jihong
						Yang Jihong