// SPDX-License-Identifier: GPL-2.0-only
/*
* intel-bts.c: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
*/
#include <endian.h>
#include <errno.h>
#include <byteswap.h>
#include <inttypes.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/zalloc.h>
#include "color.h"
#include "evsel.h"
#include "evlist.h"
#include "machine.h"
#include "symbol.h"
#include "session.h"
#include "tool.h"
#include "thread.h"
#include "thread-stack.h"
#include "debug.h"
#include "tsc.h"
#include "auxtrace.h"
#include "intel-pt-decoder/intel-pt-insn-decoder.h"
#include "intel-bts.h"
#include "util/synthetic-events.h"
#define MAX_TIMESTAMP (~0ULL)
#define INTEL_BTS_ERR_NOINSN 5
#define INTEL_BTS_ERR_LOST 9
#if __BYTE_ORDER == __BIG_ENDIAN
#define le64_to_cpu bswap_64
#else
#define le64_to_cpu
#endif
struct intel_bts {
struct auxtrace auxtrace;
struct auxtrace_queues queues;
struct auxtrace_heap heap;
u32 auxtrace_type;
struct perf_session *session;
struct machine *machine;
bool sampling_mode;
bool snapshot_mode;
bool data_queued;
u32 pmu_type;
struct perf_tsc_conversion tc;
bool cap_user_time_zero;
struct itrace_synth_opts synth_opts;
bool sample_branches;
u32 branches_filter;
u64 branches_sample_type;
u64 branches_id;
size_t branches_event_size;
unsigned long num_events;
};
struct intel_bts_queue {
struct intel_bts *bts;
unsigned int queue_nr;
struct auxtrace_buffer *buffer;
bool on_heap;
bool done;
pid_t pid;
pid_t tid;
int cpu;
u64 time;
struct intel_pt_insn intel_pt_insn;
u32 sample_flags;
};
struct branch {
u64 from;
u64 to;
u64 misc;
};
static void intel_bts_dump(struct intel_bts *bts __maybe_unused,
unsigned char *buf, size_t len)
{
struct branch *branch;
size_t i, pos = 0, br_sz = sizeof(struct branch), sz;
const char *color = PERF_COLOR_BLUE;
color_fprintf(stdout, color,
". ... Intel BTS data: size %zu bytes\n",
len);
while (len) {
if (len >= br_sz)
sz = br_sz;
else
sz = len;
printf(".");
color_fprintf(stdout, color, " %08x: ", pos);
for (i = 0; i < sz; i++)
color_fprintf(stdout, color, " %02x", buf[i]);
for (; i < br_sz; i++)
color_fprintf(stdout, color, " ");
if (len >= br_sz) {
branch = (struct branch *)buf;
color_fprintf(stdout, color, " %"PRIx64" -> %"PRIx64" %s\n",
le64_to_cpu(branch->from),
le64_to_cpu(branch->to),
le64_to_cpu(branch->misc) & 0x10 ?
"pred" : "miss");
} else {
color_fprintf(stdout, color, " Bad record!\n");
}
pos += sz;
buf += sz;
len -= sz;
}
}
static void intel_bts_dump_event(struct intel_bts *bts, unsigned char *buf,
size_t len)
{
printf(".\n");
intel_bts_dump(bts, buf, len);
}
static int intel_bts_lost(struct intel_bts *bts, struct perf_sample *sample)
{
union perf_event event;
int err;
auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE,
INTEL_BTS_ERR_LOST, sample->cpu, sample->pid,
sample->tid, 0, "Lost trace data", sample->time);
err = perf_session__deliver_synth_event(bts->session, &event, NULL);
if (err)
pr_err("Intel BTS: failed to deliver error event, error %d\n",
err);
return err;
}
static struct intel_bts_queue *intel_bts_alloc_queue(struct intel_bts *bts,
unsigned int queue_nr)
{
struct intel_bts_queue *btsq;
btsq = zalloc(sizeof(struct intel_bts_queue));
if (!btsq)
return NULL;
btsq->bts = bts;
btsq->queue_nr = queue_nr;
btsq->pid = -1;
btsq->tid = -1;
btsq->cpu = -1;
return btsq;
}
static int intel_bts_setup_queue(struct intel_bts *bts,
struct auxtrace_queue *queue,
unsigned int queue_nr)
{
struct intel_bts_queue *btsq = queue->priv;
if (list_empty(&queue->head))
return 0;
if (!btsq) {
btsq = intel_bts_alloc_queue(bts, queue_nr);
if (!btsq)
return -ENOMEM;
queue->priv = btsq;
if (queue->cpu != -1)
btsq->cpu = queue->cpu;
btsq->tid = queue->tid;
}
if (bts->sampling_mode)
return 0;
if (!btsq->on_heap && !btsq->buffer) {
int ret;
btsq->buffer = auxtrace_buffer__next(queue, NULL);
if (!btsq->buffer)
return 0;
ret = auxtrace_heap__add(&bts->heap, queue_nr,
btsq->buffer->reference);
if (ret)
return ret;
btsq->on_heap = true;
}
return 0;
}
static int intel_bts_setup_queues(struct intel_bts *bts)
{
unsigned int i;
int ret;
for (i = 0; i < bts->queues.nr_queues; i++) {
ret = intel_bts_setup_queue(bts, &bts->queues.queue_array[i],
i);