Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf powerpc: Process auxtrace events and display in 'perf report -D'

Add VPA DTL PMU auxtrace process function for "perf report -D".
The auxtrace event processing functions are defined in file
"util/powerpc-vpadtl.c".

Data structures used includes "struct powerpc_vpadtl_queue", "struct
powerpc_vpadtl" to store the auxtrace buffers in queue. Different
PERF_RECORD_XXX are generated during recording.

PERF_RECORD_AUXTRACE_INFO is processed first since it is of type
perf_user_event_type and perf session event delivers
perf_session__process_user_event() first.

Define function powerpc_vpadtl_process_auxtrace_info() to handle the
processing of PERF_RECORD_AUXTRACE_INFO records.

In this function, initialize the aux buffer queues using
auxtrace_queues__init().

Setup the required infrastructure for aux data processing.

The data is collected per CPU and auxtrace_queue is created for each
CPU.

Define powerpc_vpadtl_process_event() function to process
PERF_RECORD_AUXTRACE records.

In this, add the event to queue using auxtrace_queues__add_event() and
process the buffer in powerpc_vpadtl_dump_event().

The first entry in the buffer with timebase as zero has boot timebase
and frequency.

Remaining data is of format for "struct powerpc_vpadtl_entry".

Define the translation for dispatch_reasons and preempt_reasons, report
this when dump trace is invoked via powerpc_vpadtl_dump()

Sample output:

./perf record -a -e sched:*,vpa_dtl/dtl_all/ -c 1000000000 sleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.300 MB perf.data ]

./perf report -D

0 0 0x39b10 [0x30]: PERF_RECORD_AUXTRACE size: 0x690 offset: 0 ref: 0 idx: 0 tid: -1 cpu: 0
.
. ... VPA DTL PMU data: size 1680 bytes, entries is 35
. 00000000: boot_tb: 21349649546353231, tb_freq: 512000000
. 00000030: dispatch_reason:decrementer interrupt, preempt_reason:H_CEDE, enqueue_to_dispatch_time:7064, ready_to_enqueue_time:187, waiting_to_ready_time:6611773
. 00000060: dispatch_reason:priv doorbell, preempt_reason:H_CEDE, enqueue_to_dispatch_time:146, ready_to_enqueue_time:0, waiting_to_ready_time:15359437
. 00000090: dispatch_reason:decrementer interrupt, preempt_reason:H_CEDE, enqueue_to_dispatch_time:4868, ready_to_enqueue_time:232, waiting_to_ready_time:5100709
. 000000c0: dispatch_reason:priv doorbell, preempt_reason:H_CEDE, enqueue_to_dispatch_time:179, ready_to_enqueue_time:0, waiting_to_ready_time:30714243
. 000000f0: dispatch_reason:priv doorbell, preempt_reason:H_CEDE, enqueue_to_dispatch_time:197, ready_to_enqueue_time:0, waiting_to_ready_time:15350648
. 00000120: dispatch_reason:priv doorbell, preempt_reason:H_CEDE, enqueue_to_dispatch_time:213, ready_to_enqueue_time:0, waiting_to_ready_time:15353446
. 00000150: dispatch_reason:priv doorbell, preempt_reason:H_CEDE, enqueue_to_dispatch_time:212, ready_to_enqueue_time:0, waiting_to_ready_time:15355126
. 00000180: dispatch_reason:decrementer interrupt, preempt_reason:H_CEDE, enqueue_to_dispatch_time:6368, ready_to_enqueue_time:164, waiting_to_ready_time:5104665

Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Athira Rajeev <atrajeev@linux.ibm.com>
Tested-by: Tejas Manhas <tejas05@linux.ibm.com>
Tested-by: Venkat Rao Bagalkote <venkat88@linux.ibm.com>
Cc: Aboorva Devarajan <aboorvad@linux.ibm.com>
Cc: Aditya Bodkhe <Aditya.Bodkhe1@ibm.com>
Cc: Hari Bathini <hbathini@linux.vnet.ibm.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Shrikanth Hegde <sshegde@linux.ibm.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Athira Rajeev and committed by
Arnaldo Carvalho de Melo
c4bbd4ec 1dbfaf94

+291
+1
tools/perf/util/Build
··· 136 136 perf-util-$(CONFIG_AUXTRACE) += hisi-ptt.o 137 137 perf-util-$(CONFIG_AUXTRACE) += hisi-ptt-decoder/ 138 138 perf-util-$(CONFIG_AUXTRACE) += s390-cpumsf.o 139 + perf-util-$(CONFIG_AUXTRACE) += powerpc-vpadtl.o 139 140 140 141 ifdef CONFIG_LIBOPENCSD 141 142 perf-util-$(CONFIG_AUXTRACE) += cs-etm.o
+3
tools/perf/util/auxtrace.c
··· 55 55 #include "hisi-ptt.h" 56 56 #include "s390-cpumsf.h" 57 57 #include "util/mmap.h" 58 + #include "powerpc-vpadtl.h" 58 59 59 60 #include <linux/ctype.h> 60 61 #include "symbol/kallsyms.h" ··· 1392 1391 err = hisi_ptt_process_auxtrace_info(event, session); 1393 1392 break; 1394 1393 case PERF_AUXTRACE_VPA_DTL: 1394 + err = powerpc_vpadtl_process_auxtrace_info(event, session); 1395 + break; 1395 1396 case PERF_AUXTRACE_UNKNOWN: 1396 1397 default: 1397 1398 return -EINVAL;
+16
tools/perf/util/event.h
··· 254 254 u64 branch_ip; /* If via_branch */ 255 255 }; 256 256 257 + /* 258 + * The powerpc VPA DTL entries are of below format 259 + */ 260 + struct powerpc_vpadtl_entry { 261 + u8 dispatch_reason; 262 + u8 preempt_reason; 263 + u16 processor_id; 264 + u32 enqueue_to_dispatch_time; 265 + u32 ready_to_enqueue_time; 266 + u32 waiting_to_ready_time; 267 + u64 timebase; 268 + u64 fault_addr; 269 + u64 srr0; 270 + u64 srr1; 271 + }; 272 + 257 273 static inline void *perf_synth__raw_data(void *p) 258 274 { 259 275 return p + 4;
+264
tools/perf/util/powerpc-vpadtl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * VPA DTL PMU support 4 + */ 5 + 6 + #include <inttypes.h> 7 + #include "color.h" 8 + #include "evlist.h" 9 + #include "session.h" 10 + #include "auxtrace.h" 11 + #include "data.h" 12 + #include "machine.h" 13 + #include "debug.h" 14 + #include "powerpc-vpadtl.h" 15 + 16 + /* 17 + * Structure to save the auxtrace queue 18 + */ 19 + struct powerpc_vpadtl { 20 + struct auxtrace auxtrace; 21 + struct auxtrace_queues queues; 22 + struct auxtrace_heap heap; 23 + u32 auxtrace_type; 24 + struct perf_session *session; 25 + struct machine *machine; 26 + u32 pmu_type; 27 + }; 28 + 29 + struct boottb_freq { 30 + u64 boot_tb; 31 + u64 tb_freq; 32 + u64 timebase; 33 + u64 padded[3]; 34 + }; 35 + 36 + struct powerpc_vpadtl_queue { 37 + struct powerpc_vpadtl *vpa; 38 + unsigned int queue_nr; 39 + struct auxtrace_buffer *buffer; 40 + struct thread *thread; 41 + bool on_heap; 42 + bool done; 43 + pid_t pid; 44 + pid_t tid; 45 + int cpu; 46 + }; 47 + 48 + const char *dispatch_reasons[11] = { 49 + "external_interrupt", 50 + "firmware_internal_event", 51 + "H_PROD", 52 + "decrementer_interrupt", 53 + "system_reset", 54 + "firmware_internal_event", 55 + "conferred_cycles", 56 + "time_slice", 57 + "virtual_memory_page_fault", 58 + "expropriated_adjunct", 59 + "priv_doorbell"}; 60 + 61 + const char *preempt_reasons[10] = { 62 + "unused", 63 + "firmware_internal_event", 64 + "H_CEDE", 65 + "H_CONFER", 66 + "time_slice", 67 + "migration_hibernation_page_fault", 68 + "virtual_memory_page_fault", 69 + "H_CONFER_ADJUNCT", 70 + "hcall_adjunct", 71 + "HDEC_adjunct"}; 72 + 73 + #define dtl_entry_size sizeof(struct powerpc_vpadtl_entry) 74 + 75 + /* 76 + * Function to dump the dispatch trace data when perf report 77 + * is invoked with -D 78 + */ 79 + static void powerpc_vpadtl_dump(struct powerpc_vpadtl *vpa __maybe_unused, 80 + unsigned char *buf, size_t len) 81 + { 82 + struct powerpc_vpadtl_entry *dtl; 83 + int pkt_len, pos = 0; 84 + const char *color = PERF_COLOR_BLUE; 85 + 86 + color_fprintf(stdout, color, 87 + ". ... VPA DTL PMU data: size %zu bytes, entries is %zu\n", 88 + len, len/dtl_entry_size); 89 + 90 + if (len % dtl_entry_size) 91 + len = len - (len % dtl_entry_size); 92 + 93 + while (len) { 94 + pkt_len = dtl_entry_size; 95 + printf("."); 96 + color_fprintf(stdout, color, " %08x: ", pos); 97 + dtl = (struct powerpc_vpadtl_entry *)buf; 98 + if (dtl->timebase != 0) { 99 + printf("dispatch_reason:%s, preempt_reason:%s, " 100 + "enqueue_to_dispatch_time:%d, ready_to_enqueue_time:%d, " 101 + "waiting_to_ready_time:%d\n", 102 + dispatch_reasons[dtl->dispatch_reason], 103 + preempt_reasons[dtl->preempt_reason], 104 + be32_to_cpu(dtl->enqueue_to_dispatch_time), 105 + be32_to_cpu(dtl->ready_to_enqueue_time), 106 + be32_to_cpu(dtl->waiting_to_ready_time)); 107 + } else { 108 + struct boottb_freq *boot_tb = (struct boottb_freq *)buf; 109 + 110 + printf("boot_tb: %" PRIu64 ", tb_freq: %" PRIu64 "\n", 111 + boot_tb->boot_tb, boot_tb->tb_freq); 112 + } 113 + 114 + pos += pkt_len; 115 + buf += pkt_len; 116 + len -= pkt_len; 117 + } 118 + } 119 + 120 + static struct powerpc_vpadtl *session_to_vpa(struct perf_session *session) 121 + { 122 + return container_of(session->auxtrace, struct powerpc_vpadtl, auxtrace); 123 + } 124 + 125 + static void powerpc_vpadtl_dump_event(struct powerpc_vpadtl *vpa, unsigned char *buf, 126 + size_t len) 127 + { 128 + printf(".\n"); 129 + powerpc_vpadtl_dump(vpa, buf, len); 130 + } 131 + 132 + static int powerpc_vpadtl_process_event(struct perf_session *session __maybe_unused, 133 + union perf_event *event __maybe_unused, 134 + struct perf_sample *sample __maybe_unused, 135 + const struct perf_tool *tool __maybe_unused) 136 + { 137 + return 0; 138 + } 139 + 140 + /* 141 + * Process PERF_RECORD_AUXTRACE records 142 + */ 143 + static int powerpc_vpadtl_process_auxtrace_event(struct perf_session *session, 144 + union perf_event *event, 145 + const struct perf_tool *tool __maybe_unused) 146 + { 147 + struct powerpc_vpadtl *vpa = session_to_vpa(session); 148 + struct auxtrace_buffer *buffer; 149 + int fd = perf_data__fd(session->data); 150 + off_t data_offset; 151 + int err; 152 + 153 + if (!dump_trace) 154 + return 0; 155 + 156 + if (perf_data__is_pipe(session->data)) { 157 + data_offset = 0; 158 + } else { 159 + data_offset = lseek(fd, 0, SEEK_CUR); 160 + if (data_offset == -1) 161 + return -errno; 162 + } 163 + 164 + err = auxtrace_queues__add_event(&vpa->queues, session, event, 165 + data_offset, &buffer); 166 + 167 + if (err) 168 + return err; 169 + 170 + /* Dump here now we have copied a piped trace out of the pipe */ 171 + if (auxtrace_buffer__get_data(buffer, fd)) { 172 + powerpc_vpadtl_dump_event(vpa, buffer->data, buffer->size); 173 + auxtrace_buffer__put_data(buffer); 174 + } 175 + 176 + return 0; 177 + } 178 + 179 + static int powerpc_vpadtl_flush(struct perf_session *session __maybe_unused, 180 + const struct perf_tool *tool __maybe_unused) 181 + { 182 + return 0; 183 + } 184 + 185 + static void powerpc_vpadtl_free_events(struct perf_session *session) 186 + { 187 + struct powerpc_vpadtl *vpa = session_to_vpa(session); 188 + struct auxtrace_queues *queues = &vpa->queues; 189 + 190 + for (unsigned int i = 0; i < queues->nr_queues; i++) 191 + zfree(&queues->queue_array[i].priv); 192 + 193 + auxtrace_queues__free(queues); 194 + } 195 + 196 + static void powerpc_vpadtl_free(struct perf_session *session) 197 + { 198 + struct powerpc_vpadtl *vpa = session_to_vpa(session); 199 + 200 + auxtrace_heap__free(&vpa->heap); 201 + powerpc_vpadtl_free_events(session); 202 + session->auxtrace = NULL; 203 + free(vpa); 204 + } 205 + 206 + static const char * const powerpc_vpadtl_info_fmts[] = { 207 + [POWERPC_VPADTL_TYPE] = " PMU Type %"PRId64"\n", 208 + }; 209 + 210 + static void powerpc_vpadtl_print_info(__u64 *arr) 211 + { 212 + if (!dump_trace) 213 + return; 214 + 215 + fprintf(stdout, powerpc_vpadtl_info_fmts[POWERPC_VPADTL_TYPE], arr[POWERPC_VPADTL_TYPE]); 216 + } 217 + 218 + /* 219 + * Process the PERF_RECORD_AUXTRACE_INFO records and setup 220 + * the infrastructure to process auxtrace events. PERF_RECORD_AUXTRACE_INFO 221 + * is processed first since it is of type perf_user_event_type. 222 + * Initialise the aux buffer queues using auxtrace_queues__init(). 223 + * auxtrace_queue is created for each CPU. 224 + */ 225 + int powerpc_vpadtl_process_auxtrace_info(union perf_event *event, 226 + struct perf_session *session) 227 + { 228 + struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; 229 + size_t min_sz = sizeof(u64) * POWERPC_VPADTL_TYPE; 230 + struct powerpc_vpadtl *vpa; 231 + int err; 232 + 233 + if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) + 234 + min_sz) 235 + return -EINVAL; 236 + 237 + vpa = zalloc(sizeof(struct powerpc_vpadtl)); 238 + if (!vpa) 239 + return -ENOMEM; 240 + 241 + err = auxtrace_queues__init(&vpa->queues); 242 + if (err) 243 + goto err_free; 244 + 245 + vpa->session = session; 246 + vpa->machine = &session->machines.host; /* No kvm support */ 247 + vpa->auxtrace_type = auxtrace_info->type; 248 + vpa->pmu_type = auxtrace_info->priv[POWERPC_VPADTL_TYPE]; 249 + 250 + vpa->auxtrace.process_event = powerpc_vpadtl_process_event; 251 + vpa->auxtrace.process_auxtrace_event = powerpc_vpadtl_process_auxtrace_event; 252 + vpa->auxtrace.flush_events = powerpc_vpadtl_flush; 253 + vpa->auxtrace.free_events = powerpc_vpadtl_free_events; 254 + vpa->auxtrace.free = powerpc_vpadtl_free; 255 + session->auxtrace = &vpa->auxtrace; 256 + 257 + powerpc_vpadtl_print_info(&auxtrace_info->priv[0]); 258 + 259 + return 0; 260 + 261 + err_free: 262 + free(vpa); 263 + return err; 264 + }
+7
tools/perf/util/powerpc-vpadtl.h
··· 13 13 14 14 #define VPADTL_AUXTRACE_PRIV_SIZE (VPADTL_AUXTRACE_PRIV_MAX * sizeof(u64)) 15 15 16 + union perf_event; 17 + struct perf_session; 18 + struct perf_pmu; 19 + 20 + int powerpc_vpadtl_process_auxtrace_info(union perf_event *event, 21 + struct perf_session *session); 22 + 16 23 #endif