[linux-yocto] [PATCH 13/17] opencsd: This is a Backport of the Following

Daniel Dragomir daniel.dragomir at windriver.com
Tue May 16 11:39:05 PDT 2017


From: John Jacques <john.jacques at intel.com>

repository: https://github.com/Linaro/OpenCSD.git
branch: perf-opencsd-4.9
tip:
commit e38cbdb2928a36c5a ("cs-etm: Update to perf cs-etm decoder for OpenCSD v0.5")

Signed-off-by: John Jacques <john.jacques at intel.com>
---
 Documentation/trace/coresight.txt                  |  138 +-
 drivers/hwtracing/coresight/coresight-etm-perf.c   |   31 +-
 drivers/hwtracing/coresight/coresight-etm.h        |    5 +
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    |   12 +-
 drivers/hwtracing/coresight/coresight-priv.h       |    4 +-
 drivers/hwtracing/coresight/coresight-stm.c        |    9 +-
 drivers/hwtracing/coresight/coresight-tmc-etf.c    |   48 +-
 drivers/hwtracing/coresight/coresight-tmc-etr.c    |  286 +++-
 drivers/hwtracing/coresight/coresight-tmc.h        |    2 +-
 drivers/hwtracing/coresight/coresight.c            |   62 +-
 tools/perf/Makefile.config                         |   18 +
 tools/perf/Makefile.perf                           |    3 +
 tools/perf/arch/arm/util/cs-etm.c                  |    2 -
 tools/perf/builtin-script.c                        |    3 +-
 tools/perf/scripts/python/cs-trace-disasm.py       |  134 ++
 tools/perf/scripts/python/cs-trace-ranges.py       |   44 +
 tools/perf/util/Build                              |    2 +
 tools/perf/util/auxtrace.c                         |    2 +
 tools/perf/util/cs-etm-decoder/Build               |    6 +
 .../perf/util/cs-etm-decoder/cs-etm-decoder-stub.c |   99 ++
 tools/perf/util/cs-etm-decoder/cs-etm-decoder.c    |  527 +++++++
 tools/perf/util/cs-etm-decoder/cs-etm-decoder.h    |  117 ++
 tools/perf/util/cs-etm.c                           | 1501 ++++++++++++++++++++
 tools/perf/util/cs-etm.h                           |   10 +
 tools/perf/util/machine.c                          |   46 +-
 .../util/scripting-engines/trace-event-python.c    |    2 +
 tools/perf/util/symbol-minimal.c                   |    3 +-
 27 files changed, 3001 insertions(+), 115 deletions(-)
 create mode 100644 tools/perf/scripts/python/cs-trace-disasm.py
 create mode 100644 tools/perf/scripts/python/cs-trace-ranges.py
 create mode 100644 tools/perf/util/cs-etm-decoder/Build
 create mode 100644 tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
 create mode 100644 tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
 create mode 100644 tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
 create mode 100644 tools/perf/util/cs-etm.c

diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt
index a33c88c..a2e7ccb 100644
--- a/Documentation/trace/coresight.txt
+++ b/Documentation/trace/coresight.txt
@@ -20,13 +20,13 @@ Components are generally categorised as source, link and sinks and are
 
 "Sources" generate a compressed stream representing the processor instruction
 path based on tracing scenarios as configured by users.  From there the stream
-flows through the coresight system (via ATB bus) using links that are connecting
-the emanating source to a sink(s).  Sinks serve as endpoints to the coresight
+flows through the Coresight system (via ATB bus) using links that are connecting
+the emanating source to a sink(s).  Sinks serve as endpoints to the Coresight
 implementation, either storing the compressed stream in a memory buffer or
 creating an interface to the outside world where data can be transferred to a
-host without fear of filling up the onboard coresight memory buffer.
+host without fear of filling up the onboard Coresight memory buffer.
 
-At typical coresight system would look like this:
+At typical Coresight system would look like this:
 
   *****************************************************************
  **************************** AMBA AXI  ****************************===||
@@ -83,8 +83,8 @@ While on target configuration of the components is done via the APB bus,
 all trace data are carried out-of-band on the ATB bus.  The CTM provides
 a way to aggregate and distribute signals between CoreSight components.
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  This first implementation centers on
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  This first implementation centers on
 the basic tracing functionality, enabling components such ETM/PTM, funnel,
 replicator, TMC, TPIU and ETB.  Future work will enable more
 intricate IP blocks such as STM and CTI.
@@ -129,11 +129,11 @@ expected to be added as the solution matures.
 Framework and implementation
 ----------------------------
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  Any coresight compliant device can
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  Any Coresight compliant device can
 register with the framework for as long as they use the right APIs:
 
-struct coresight_device *coresight_register(struct coresight_desc *desc);
+struct Coresight_device *coresight_register(struct coresight_desc *desc);
 void coresight_unregister(struct coresight_device *csdev);
 
 The registering function is taking a "struct coresight_device *csdev" and
@@ -193,10 +193,120 @@ the information carried in "THIS_MODULE".
 How to use the tracer modules
 -----------------------------
 
-Before trace collection can start, a coresight sink needs to be identify.
-There is no limit on the amount of sinks (nor sources) that can be enabled at
-any given moment.  As a generic operation, all device pertaining to the sink
-class will have an "active" entry in sysfs:
+There is two ways to use the Coresight framework: 1) using the perf cmd line
+tool and 2) interacting directly with the Coresight devices using the sysFS
+interface.  The latter will slowly be faded out as more functionality become
+available from the perf cmd line tool but for the time being both are still
+supported.  The following sections provide details on using both methods.
+
+1) Using perf framework:
+
+Coresight tracers like ETM and PTM are represented using the Perf framework's
+Performance Monitoring Unit (PMU).  As such the perf framework takes charge of
+controlling when tracing happens based on when the process(es) of interest are
+scheduled.  When configure in a system, Coresight PMUs will be listed when
+queried by the perf command line tool:
+
+linaro at linaro-nano:~$ ./perf list pmu
+
+List of pre-defined events (to be used in -e):
+
+  cs_etm//                                           [Kernel PMU event]
+
+linaro at linaro-nano:~$
+
+Regardless of the amount ETM/PTM IP block in a system (usually equal to the
+amount of processor core), the "cs_etm" PMU will be listed only once.
+
+Before a trace can be configured and started a Coresight sink needs to be
+selected using the sysFS method (see below).  This is only temporary until
+sink selection can be made from the command line tool.
+
+linaro at linaro-nano:~$ ls /sys/bus/coresight/devices
+20010000.etb  20030000.tpiu  20040000.funnel  2201c000.ptm
+2201d000.ptm  2203c000.etm  2203d000.etm  2203e000.etm  replicator
+
+linaro at linaro-nano:~$ echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink
+
+Once a sink has been selected configuring a Coresight PMU works the same way as
+any other PMU.  As such tracing can happen for a single CPU, a group of CPU, per
+thread or a combination of those:
+
+linaro at linaro-nano:~$ perf record -e cs_etm// --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm// <command>
+
+Tracing limited to user and kernel space can also be used to narrow the amount
+of collected traces:
+
+linaro at linaro-nano:~$ perf record -e cs_etm//u --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm//k <command>
+
+As of this writing two ETM/PTM specific options have are available: cycle
+accurate and timestamp (please refer to the Embedded Trace Macrocell reference
+manual for details on these options).  By default both are disabled but using
+the "cycacc" and "timestamp" mnemonic within the double '/' will see those
+options configure for the upcoming trace run:
+
+linaro at linaro-nano:~$ perf record -e cs_etm/cycacc/ --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm/cycacc,timestamp/ <command>
+
+The Coresight PMUs can be configured to work in "full trace" or "snapshot" mode.
+In full trace mode trace acquisition is enabled from beginning to end with trace
+data being recorded continuously:
+
+linaro at linaro-nano:~$ perf record -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Since this can lead to a significant amount of data and because some devices are
+limited in disk space snapshot mode can be used instead.  In snapshot mode
+traces are still collected in the ring buffer but not communicated to user
+space.  The ring buffer is allowed to wrap around, providing the latest
+information before an event of interest happens.  Significant events are
+communicated by sending a USR2 signal to the user space command line tool.
+From there the tool will stop trace collection and harvest data from the ring
+buffer before re-enabling traces.  Snapshot mode can be invoked using '-S' when
+launching a trace collection:
+
+linaro at linaro-nano:~$ perf record -S -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Trace data collected during trace runs ends up in the "perf.data" file.  Trace
+configuration information necessary for trace decoding is also embedded in the
+"perf.data" file.  Two new headers, 'PERF_RECORD_AUXTRACE_INFO' and
+'PERF_RECORD_AUXTRACE' have been added to the list of event types in order to
+find out where the different sections start.
+
+It is worth noting that a set of metadata information exists for each tracer
+that participated in a trace run.  As such if 5 processors have been engaged,
+5 sets of metadata will be found in the perf.data file.  This is to ensure that
+tracer decompression tools have all the information they need in order to
+process the trace data.
+
+Metadata information is collected directly from the ETM/PTM management registers
+using the sysFS interface.  Since there is no way for the perf command line
+tool to associate a CPU with a tracer, a symbolic link has been created between
+the cs_etm sysFS event directory and each Coresight tracer:
+
+linaro at linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm
+cpu0  cpu1  cpu2  cpu3  cpu4  format  perf_event_mux_interval_ms
+power  subsystem  type  uevent
+
+linaro at linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm/cpu0/mgmt/
+etmccer  etmccr  etmcr  etmidr  etmscr  etmtecr1  etmtecr2
+etmteevr  etmtraceidr  etmtssvr
+
+2) Using the sysFS interface:
+
+Most, if not all, configuration registers are made available to users via the
+sysFS interface.  Until all Coresight ETM drivers have been converted to perf,
+it will also be possible to start and stop traces from sysFS.
+
+As with the perf method described above, a Coresight sink needs to be identify
+before trace collection can commence.  Using the sysFS method _only_, there is
+no limit on the amount of sinks (nor sources) that can be enabled at
+any given moment.  As a generic operation, all devices pertaining to the sink
+class will have an "enable_sink" entry in sysfs:
 
 root:/sys/bus/coresight/devices# ls
 replicator  20030000.tpiu    2201c000.ptm  2203c000.etm  2203e000.etm
@@ -246,7 +356,7 @@ The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32.
 
 Following is a DS-5 output of an experimental loop that increments a variable up
 to a certain value.  The example is simple and yet provides a glimpse of the
-wealth of possibilities that coresight provides.
+wealth of possibilities that Coresight provides.
 
 Info                                    Tracing enabled
 Instruction     106378866       0x8026B53C      E52DE004        false   PUSH     {lr}
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 2cd7c71..1774196 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -202,6 +202,21 @@ static void *etm_setup_aux(int event_cpu, void **pages,
 	if (!event_data)
 		return NULL;
 
+	/*
+	 * In theory nothing prevent tracers in a trace session from being
+	 * associated with different sinks, nor having a sink per tracer.  But
+	 * until we have HW with this kind of topology we need to assume tracers
+	 * in a trace session are using the same sink.  Therefore go through
+	 * the coresight bus and pick the first enabled sink.
+	 *
+	 * When operated from sysFS users are responsible to enable the sink
+	 * while from perf, the perf tools will do it based on the choice made
+	 * on the cmd line.  As such the "enable_sink" flag in sysFS is reset.
+	 */
+	sink = coresight_get_enabled_sink(true);
+	if (!sink)
+		goto err;
+
 	INIT_WORK(&event_data->work, free_event_data);
 
 	mask = &event_data->mask;
@@ -219,25 +234,11 @@ static void *etm_setup_aux(int event_cpu, void **pages,
 		 * list of devices from source to sink that can be
 		 * referenced later when the path is actually needed.
 		 */
-		event_data->path[cpu] = coresight_build_path(csdev);
+		event_data->path[cpu] = coresight_build_path(csdev, sink);
 		if (IS_ERR(event_data->path[cpu]))
 			goto err;
 	}
 
-	/*
-	 * In theory nothing prevent tracers in a trace session from being
-	 * associated with different sinks, nor having a sink per tracer.  But
-	 * until we have HW with this kind of topology and a way to convey
-	 * sink assignement from the perf cmd line we need to assume tracers
-	 * in a trace session are using the same sink.  Therefore pick the sink
-	 * found at the end of the first available path.
-	 */
-	cpu = cpumask_first(mask);
-	/* Grab the sink at the end of the path */
-	sink = coresight_get_sink(event_data->path[cpu]);
-	if (!sink)
-		goto err;
-
 	if (!sink_ops(sink)->alloc_buffer)
 		goto err;
 
diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 4a18ee4..ad063d7 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -89,11 +89,13 @@
 /* ETMCR - 0x00 */
 #define ETMCR_PWD_DWN		BIT(0)
 #define ETMCR_STALL_MODE	BIT(7)
+#define ETMCR_BRANCH_BROADCAST	BIT(8)
 #define ETMCR_ETM_PRG		BIT(10)
 #define ETMCR_ETM_EN		BIT(11)
 #define ETMCR_CYC_ACC		BIT(12)
 #define ETMCR_CTXID_SIZE	(BIT(14)|BIT(15))
 #define ETMCR_TIMESTAMP_EN	BIT(28)
+#define ETMCR_RETURN_STACK	BIT(29)
 /* ETMCCR - 0x04 */
 #define ETMCCR_FIFOFULL		BIT(23)
 /* ETMPDCR - 0x310 */
@@ -110,8 +112,11 @@
 #define ETM_MODE_STALL		BIT(2)
 #define ETM_MODE_TIMESTAMP	BIT(3)
 #define ETM_MODE_CTXID		BIT(4)
+#define ETM_MODE_BBROAD		BIT(5)
+#define ETM_MODE_RET_STACK	BIT(6)
 #define ETM_MODE_ALL		(ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \
 				 ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \
+				 ETM_MODE_BBROAD | ETM_MODE_RET_STACK | \
 				 ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \
 				 ETM_MODE_EXCL_USER)
 
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index e9b0719..ca98ad1 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -146,7 +146,7 @@ static ssize_t mode_store(struct device *dev,
 			goto err_unlock;
 		}
 		config->ctrl |= ETMCR_STALL_MODE;
-	 } else
+	} else
 		config->ctrl &= ~ETMCR_STALL_MODE;
 
 	if (config->mode & ETM_MODE_TIMESTAMP) {
@@ -164,6 +164,16 @@ static ssize_t mode_store(struct device *dev,
 	else
 		config->ctrl &= ~ETMCR_CTXID_SIZE;
 
+	if (config->mode & ETM_MODE_BBROAD)
+		config->ctrl |= ETMCR_BRANCH_BROADCAST;
+	else
+		config->ctrl &= ~ETMCR_BRANCH_BROADCAST;
+
+	if (config->mode & ETM_MODE_RET_STACK)
+		config->ctrl |= ETMCR_RETURN_STACK;
+	else
+		config->ctrl &= ~ETMCR_RETURN_STACK;
+
 	if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
 		etm_config_trace_mode(config);
 
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 196a14b..ef9d8e9 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -111,7 +111,9 @@ static inline void CS_UNLOCK(void __iomem *addr)
 void coresight_disable_path(struct list_head *path);
 int coresight_enable_path(struct list_head *path, u32 mode);
 struct coresight_device *coresight_get_sink(struct list_head *path);
-struct list_head *coresight_build_path(struct coresight_device *csdev);
+struct coresight_device *coresight_get_enabled_sink(bool reset);
+struct list_head *coresight_build_path(struct coresight_device *csdev,
+				       struct coresight_device *sink);
 void coresight_release_path(struct list_head *path);
 
 #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 8e79056..2d16260 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -419,10 +419,10 @@ static ssize_t stm_generic_packet(struct stm_data *stm_data,
 						   struct stm_drvdata, stm);
 
 	if (!(drvdata && local_read(&drvdata->mode)))
-		return 0;
+		return -EACCES;
 
 	if (channel >= drvdata->numsp)
-		return 0;
+		return -EINVAL;
 
 	ch_addr = (unsigned long)stm_channel_addr(drvdata, channel);
 
@@ -920,6 +920,11 @@ static struct amba_id stm_ids[] = {
 		.mask   = 0x0003ffff,
 		.data	= "STM32",
 	},
+	{
+		.id	= 0x0003b963,
+		.mask	= 0x0003ffff,
+		.data	= "STM500",
+	},
 	{ 0, 0},
 };
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index d6941ea..1549436 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -70,7 +70,7 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
 	 * When operating in sysFS mode the content of the buffer needs to be
 	 * read before the TMC is disabled.
 	 */
-	if (local_read(&drvdata->mode) == CS_MODE_SYSFS)
+	if (drvdata->mode == CS_MODE_SYSFS)
 		tmc_etb_dump_hw(drvdata);
 	tmc_disable_hw(drvdata);
 
@@ -103,19 +103,14 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
 {
 	int ret = 0;
 	bool used = false;
 	char *buf = NULL;
-	long val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	 /* This shouldn't be happening */
-	if (WARN_ON(mode != CS_MODE_SYSFS))
-		return -EINVAL;
-
 	/*
 	 * If we don't have a buffer release the lock and allocate memory.
 	 * Otherwise keep the lock and move along.
@@ -138,13 +133,12 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
 		goto out;
 	}
 
-	val = local_xchg(&drvdata->mode, mode);
 	/*
 	 * In sysFS mode we can have multiple writers per sink.  Since this
 	 * sink is already enabled no memory is needed and the HW need not be
 	 * touched.
 	 */
-	if (val == CS_MODE_SYSFS)
+	if (drvdata->mode == CS_MODE_SYSFS)
 		goto out;
 
 	/*
@@ -163,6 +157,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
 		drvdata->buf = buf;
 	}
 
+	drvdata->mode = CS_MODE_SYSFS;
 	tmc_etb_enable_hw(drvdata);
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -177,34 +172,29 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
 	return ret;
 }
 
-static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etf_sink_perf(struct coresight_device *csdev)
 {
 	int ret = 0;
-	long val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	 /* This shouldn't be happening */
-	if (WARN_ON(mode != CS_MODE_PERF))
-		return -EINVAL;
-
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		ret = -EINVAL;
 		goto out;
 	}
 
-	val = local_xchg(&drvdata->mode, mode);
 	/*
 	 * In Perf mode there can be only one writer per sink.  There
 	 * is also no need to continue if the ETB/ETR is already operated
 	 * from sysFS.
 	 */
-	if (val != CS_MODE_DISABLED) {
+	if (drvdata->mode != CS_MODE_DISABLED) {
 		ret = -EINVAL;
 		goto out;
 	}
 
+	drvdata->mode = CS_MODE_PERF;
 	tmc_etb_enable_hw(drvdata);
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -216,9 +206,9 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 {
 	switch (mode) {
 	case CS_MODE_SYSFS:
-		return tmc_enable_etf_sink_sysfs(csdev, mode);
+		return tmc_enable_etf_sink_sysfs(csdev);
 	case CS_MODE_PERF:
-		return tmc_enable_etf_sink_perf(csdev, mode);
+		return tmc_enable_etf_sink_perf(csdev);
 	}
 
 	/* We shouldn't be here */
@@ -227,7 +217,6 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 
 static void tmc_disable_etf_sink(struct coresight_device *csdev)
 {
-	long val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -237,10 +226,11 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 		return;
 	}
 
-	val = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
 	/* Disable the TMC only if it needs to */
-	if (val != CS_MODE_DISABLED)
+	if (drvdata->mode != CS_MODE_DISABLED) {
 		tmc_etb_disable_hw(drvdata);
+		drvdata->mode = CS_MODE_DISABLED;
+	}
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
@@ -260,7 +250,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
 	}
 
 	tmc_etf_enable_hw(drvdata);
-	local_set(&drvdata->mode, CS_MODE_SYSFS);
+	drvdata->mode = CS_MODE_SYSFS;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETF enabled\n");
@@ -280,7 +270,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	}
 
 	tmc_etf_disable_hw(drvdata);
-	local_set(&drvdata->mode, CS_MODE_DISABLED);
+	drvdata->mode = CS_MODE_DISABLED;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC disabled\n");
@@ -383,7 +373,7 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev,
 		return;
 
 	/* This shouldn't happen */
-	if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF))
+	if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF))
 		return;
 
 	CS_UNLOCK(drvdata->base);
@@ -504,7 +494,6 @@ const struct coresight_ops tmc_etf_cs_ops = {
 
 int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
 {
-	long val;
 	enum tmc_mode mode;
 	int ret = 0;
 	unsigned long flags;
@@ -528,9 +517,8 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
 		goto out;
 	}
 
-	val = local_read(&drvdata->mode);
 	/* Don't interfere if operated from Perf */
-	if (val == CS_MODE_PERF) {
+	if (drvdata->mode == CS_MODE_PERF) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -542,7 +530,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
 	}
 
 	/* Disable the TMC if need be */
-	if (val == CS_MODE_SYSFS)
+	if (drvdata->mode == CS_MODE_SYSFS)
 		tmc_etb_disable_hw(drvdata);
 
 	drvdata->reading = true;
@@ -573,7 +561,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
 	}
 
 	/* Re-enable the TMC if need be */
-	if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
+	if (drvdata->mode == CS_MODE_SYSFS) {
 		/*
 		 * The trace run will continue with the same allocated trace
 		 * buffer. As such zero-out the buffer so that we don't end
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 886ea83..2db4857 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -15,11 +15,30 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/circ_buf.h>
 #include <linux/coresight.h>
 #include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
+/**
+ * struct cs_etr_buffer - keep track of a recording session' specifics
+ * @tmc:	generic portion of the TMC buffers
+ * @paddr:	the physical address of a DMA'able contiguous memory area
+ * @vaddr:	the virtual address associated to @paddr
+ * @size:	how much memory we have, starting at @paddr
+ * @dev:	the device @vaddr has been tied to
+ */
+struct cs_etr_buffers {
+	struct cs_buffers	tmc;
+	dma_addr_t		paddr;
+	void __iomem		*vaddr;
+	u32			size;
+	struct device		*dev;
+};
+
 static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
 {
 	u32 axictl;
@@ -86,26 +105,22 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	 * When operating in sysFS mode the content of the buffer needs to be
 	 * read before the TMC is disabled.
 	 */
-	if (local_read(&drvdata->mode) == CS_MODE_SYSFS)
+	if (drvdata->mode == CS_MODE_SYSFS)
 		tmc_etr_dump_hw(drvdata);
 	tmc_disable_hw(drvdata);
 
 	CS_LOCK(drvdata->base);
 }
 
-static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
 {
 	int ret = 0;
 	bool used = false;
-	long val;
 	unsigned long flags;
 	void __iomem *vaddr = NULL;
 	dma_addr_t paddr;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	 /* This shouldn't be happening */
-	if (WARN_ON(mode != CS_MODE_SYSFS))
-		return -EINVAL;
 
 	/*
 	 * If we don't have a buffer release the lock and allocate memory.
@@ -134,13 +149,12 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
 		goto out;
 	}
 
-	val = local_xchg(&drvdata->mode, mode);
 	/*
 	 * In sysFS mode we can have multiple writers per sink.  Since this
 	 * sink is already enabled no memory is needed and the HW need not be
 	 * touched.
 	 */
-	if (val == CS_MODE_SYSFS)
+	if (drvdata->mode == CS_MODE_SYSFS)
 		goto out;
 
 	/*
@@ -155,8 +169,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
 		drvdata->buf = drvdata->vaddr;
 	}
 
-	memset(drvdata->vaddr, 0, drvdata->size);
-
+	drvdata->mode = CS_MODE_SYSFS;
 	tmc_etr_enable_hw(drvdata);
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -171,34 +184,29 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
 	return ret;
 }
 
-static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etr_sink_perf(struct coresight_device *csdev)
 {
 	int ret = 0;
-	long val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
-	 /* This shouldn't be happening */
-	if (WARN_ON(mode != CS_MODE_PERF))
-		return -EINVAL;
-
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		ret = -EINVAL;
 		goto out;
 	}
 
-	val = local_xchg(&drvdata->mode, mode);
 	/*
 	 * In Perf mode there can be only one writer per sink.  There
 	 * is also no need to continue if the ETR is already operated
 	 * from sysFS.
 	 */
-	if (val != CS_MODE_DISABLED) {
+	if (drvdata->mode != CS_MODE_DISABLED) {
 		ret = -EINVAL;
 		goto out;
 	}
 
+	drvdata->mode = CS_MODE_PERF;
 	tmc_etr_enable_hw(drvdata);
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -210,9 +218,9 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 {
 	switch (mode) {
 	case CS_MODE_SYSFS:
-		return tmc_enable_etr_sink_sysfs(csdev, mode);
+		return tmc_enable_etr_sink_sysfs(csdev);
 	case CS_MODE_PERF:
-		return tmc_enable_etr_sink_perf(csdev, mode);
+		return tmc_enable_etr_sink_perf(csdev);
 	}
 
 	/* We shouldn't be here */
@@ -221,7 +229,6 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 
 static void tmc_disable_etr_sink(struct coresight_device *csdev)
 {
-	long val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -231,19 +238,244 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 		return;
 	}
 
-	val = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
 	/* Disable the TMC only if it needs to */
-	if (val != CS_MODE_DISABLED)
+	if (drvdata->mode != CS_MODE_DISABLED) {
 		tmc_etr_disable_hw(drvdata);
+		drvdata->mode = CS_MODE_DISABLED;
+	}
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETR disabled\n");
 }
 
+static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, int cpu,
+				  void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_etr_buffers *buf;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Allocate memory structure for interaction with Perf */
+	buf = kzalloc_node(sizeof(struct cs_etr_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->dev = drvdata->dev;
+	buf->size = drvdata->size;
+	buf->vaddr = dma_alloc_coherent(buf->dev, buf->size,
+					&buf->paddr, GFP_KERNEL);
+	if (!buf->vaddr) {
+		kfree(buf);
+		return NULL;
+	}
+
+	buf->tmc.snapshot = overwrite;
+	buf->tmc.nr_pages = nr_pages;
+	buf->tmc.data_pages = pages;
+
+	return buf;
+}
+
+static void tmc_free_etr_buffer(void *config)
+{
+	struct cs_etr_buffers *buf = config;
+
+	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->paddr);
+	kfree(buf);
+}
+
+static int tmc_set_etr_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->tmc.nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->tmc.cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->tmc.offset = head % PAGE_SIZE;
+
+	local_set(&buf->tmc.data_size, 0);
+
+	/* Tell the HW where to put the trace data */
+	drvdata->vaddr = buf->vaddr;
+	drvdata->paddr = buf->paddr;
+	memset(drvdata->vaddr, 0, drvdata->size);
+
+	return ret;
+}
+
+static unsigned long tmc_reset_etr_buffer(struct coresight_device *csdev,
+					  struct perf_output_handle *handle,
+					  void *sink_config, bool *lost)
+{
+	long size = 0;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (buf) {
+		/*
+		 * In snapshot mode ->data_size holds the new address of the
+		 * ring buffer's head.  The size itself is the whole address
+		 * range since we want the latest information.
+		 */
+		if (buf->tmc.snapshot) {
+			size = buf->tmc.nr_pages << PAGE_SHIFT;
+			handle->head = local_xchg(&buf->tmc.data_size, size);
+		}
+
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_etr_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = !!local_xchg(&buf->tmc.lost, 0);
+		size = local_xchg(&buf->tmc.data_size, 0);
+	}
+
+	/* Get ready for another run */
+	drvdata->vaddr = NULL;
+	drvdata->paddr = 0;
+
+	return size;
+}
+
+static void tmc_update_etr_buffer(struct coresight_device *csdev,
+				  struct perf_output_handle *handle,
+				  void *sink_config)
+{
+	int i, cur;
+	u32 *buf_ptr;
+	u32 read_ptr, write_ptr;
+	u32 status, to_read;
+	unsigned long offset;
+	struct cs_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!buf)
+		return;
+
+	/* This shouldn't happen */
+	if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF))
+		return;
+
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+
+	read_ptr = readl_relaxed(drvdata->base + TMC_RRP);
+	write_ptr = readl_relaxed(drvdata->base + TMC_RWP);
+
+	/*
+	 * Get a hold of the status register and see if a wrap around
+	 * has occurred.  If so adjust things accordingly.
+	 */
+	status = readl_relaxed(drvdata->base + TMC_STS);
+	if (status & TMC_STS_FULL) {
+		local_inc(&buf->lost);
+		to_read = drvdata->size;
+	} else {
+		to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size);
+	}
+
+	/*
+	 * The TMC RAM buffer may be bigger than the space available in the
+	 * perf ring buffer (handle->size).  If so advance the RRP so that we
+	 * get the latest trace data.
+	 */
+	if (to_read > handle->size) {
+		u32 buffer_start, mask = 0;
+
+		/* Read buffer start address in system memory */
+		buffer_start = readl_relaxed(drvdata->base + TMC_DBALO);
+
+		/*
+		 * The value written to RRP must be byte-address aligned to
+		 * the width of the trace memory databus _and_ to a frame
+		 * boundary (16 byte), whichever is the biggest. For example,
+		 * for 32-bit, 64-bit and 128-bit wide trace memory, the four
+		 * LSBs must be 0s. For 256-bit wide trace memory, the five
+		 * LSBs must be 0s.
+		 */
+		switch (drvdata->memwidth) {
+		case TMC_MEM_INTF_WIDTH_32BITS:
+		case TMC_MEM_INTF_WIDTH_64BITS:
+		case TMC_MEM_INTF_WIDTH_128BITS:
+			mask = GENMASK(31, 5);
+			break;
+		case TMC_MEM_INTF_WIDTH_256BITS:
+			mask = GENMASK(31, 6);
+			break;
+		}
+
+		/*
+		 * Make sure the new size is aligned in accordance with the
+		 * requirement explained above.
+		 */
+		to_read = handle->size & mask;
+		/* Move the RAM read pointer up */
+		read_ptr = (write_ptr + drvdata->size) - to_read;
+		/* Make sure we are still within our limits */
+		if (read_ptr > (buffer_start + (drvdata->size - 1)))
+			read_ptr -= drvdata->size;
+		/* Tell the HW */
+		writel_relaxed(read_ptr, drvdata->base + TMC_RRP);
+		local_inc(&buf->lost);
+	}
+
+	cur = buf->cur;
+	offset = buf->offset;
+
+	/* for every byte to read */
+	for (i = 0; i < to_read; i += 4) {
+		buf_ptr = buf->data_pages[cur] + offset;
+		*buf_ptr = readl_relaxed(drvdata->base + TMC_RRD);
+
+		offset += 4;
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			cur++;
+			/* wrap around at the end of the buffer */
+			cur &= buf->nr_pages - 1;
+		}
+	}
+
+	/*
+	 * In snapshot mode all we have to do is communicate to
+	 * perf_aux_output_end() the address of the current head.  In full
+	 * trace mode the same function expects a size to move rb->aux_head
+	 * forward.
+	 */
+	if (buf->snapshot)
+		local_set(&buf->data_size, (cur * PAGE_SIZE) + offset);
+	else
+		local_add(to_read, &buf->data_size);
+
+	CS_LOCK(drvdata->base);
+}
+
 static const struct coresight_ops_sink tmc_etr_sink_ops = {
 	.enable		= tmc_enable_etr_sink,
 	.disable	= tmc_disable_etr_sink,
+	.alloc_buffer	= tmc_alloc_etr_buffer,
+	.free_buffer	= tmc_free_etr_buffer,
+	.set_buffer	= tmc_set_etr_buffer,
+	.reset_buffer	= tmc_reset_etr_buffer,
+	.update_buffer	= tmc_update_etr_buffer,
 };
 
 const struct coresight_ops tmc_etr_cs_ops = {
@@ -253,7 +485,6 @@ const struct coresight_ops tmc_etr_cs_ops = {
 int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
-	long val;
 	unsigned long flags;
 
 	/* config types are set a boot time and never change */
@@ -266,9 +497,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 		goto out;
 	}
 
-	val = local_read(&drvdata->mode);
 	/* Don't interfere if operated from Perf */
-	if (val == CS_MODE_PERF) {
+	if (drvdata->mode == CS_MODE_PERF) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -280,7 +510,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 	}
 
 	/* Disable the TMC if need be */
-	if (val == CS_MODE_SYSFS)
+	if (drvdata->mode == CS_MODE_SYSFS)
 		tmc_etr_disable_hw(drvdata);
 
 	drvdata->reading = true;
@@ -303,7 +533,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	/* RE-enable the TMC if need be */
-	if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
+	if (drvdata->mode == CS_MODE_SYSFS) {
 		/*
 		 * The trace run will continue with the same allocated trace
 		 * buffer. The trace buffer is cleared in tmc_etr_enable_hw(),
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 44b3ae3..51c0185 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -117,7 +117,7 @@ struct tmc_drvdata {
 	void __iomem		*vaddr;
 	u32			size;
 	u32			len;
-	local_t			mode;
+	u32			mode;
 	enum tmc_config_type	config_type;
 	enum tmc_mem_intf_width	memwidth;
 	u32			trigger_cntr;
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 7bf00a0..40ede64 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -368,6 +368,40 @@ struct coresight_device *coresight_get_sink(struct list_head *path)
 	return csdev;
 }
 
+static int coresight_enabled_sink(struct device *dev, void *data)
+{
+	bool *reset = data;
+	struct coresight_device *csdev = to_coresight_device(dev);
+
+	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
+	     csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
+	     csdev->activated) {
+		/*
+		 * Now that we have a handle on the sink for this session,
+		 * disable the sysFS "enable_sink" flag so that possible
+		 * concurrent perf session that wish to use another sink don't
+		 * trip on it.  Doing so has no ramification for the current
+		 * session.
+		 */
+		if (*reset)
+			csdev->activated = false;
+
+		return 1;
+	}
+
+	return 0;
+}
+
+struct coresight_device *coresight_get_enabled_sink(bool reset)
+{
+	struct device *dev = NULL;
+
+	dev = bus_find_device(&coresight_bustype, NULL, &reset,
+			      coresight_enabled_sink);
+
+	return dev ? to_coresight_device(dev) : NULL;
+}
+
 /**
  * _coresight_build_path - recursively build a path from a @csdev to a sink.
  * @csdev:	The device to start from.
@@ -380,6 +414,7 @@ struct coresight_device *coresight_get_sink(struct list_head *path)
  * last one.
  */
 static int _coresight_build_path(struct coresight_device *csdev,
+				 struct coresight_device *sink,
 				 struct list_head *path)
 {
 	int i;
@@ -387,15 +422,15 @@ static int _coresight_build_path(struct coresight_device *csdev,
 	struct coresight_node *node;
 
 	/* An activated sink has been found.  Enqueue the element */
-	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
-	     csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated)
+	if (csdev == sink)
 		goto out;
 
 	/* Not a sink - recursively explore each port found on this element */
 	for (i = 0; i < csdev->nr_outport; i++) {
 		struct coresight_device *child_dev = csdev->conns[i].child_dev;
 
-		if (child_dev && _coresight_build_path(child_dev, path) == 0) {
+		if (child_dev &&
+		    _coresight_build_path(child_dev, sink, path) == 0) {
 			found = true;
 			break;
 		}
@@ -422,18 +457,22 @@ static int _coresight_build_path(struct coresight_device *csdev,
 	return 0;
 }
 
-struct list_head *coresight_build_path(struct coresight_device *csdev)
+struct list_head *coresight_build_path(struct coresight_device *source,
+				       struct coresight_device *sink)
 {
 	struct list_head *path;
 	int rc;
 
+	if (!sink)
+		return ERR_PTR(-EINVAL);
+
 	path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
 	if (!path)
 		return ERR_PTR(-ENOMEM);
 
 	INIT_LIST_HEAD(path);
 
-	rc = _coresight_build_path(csdev, path);
+	rc = _coresight_build_path(source, sink, path);
 	if (rc) {
 		kfree(path);
 		return ERR_PTR(rc);
@@ -497,6 +536,7 @@ static int coresight_validate_source(struct coresight_device *csdev,
 int coresight_enable(struct coresight_device *csdev)
 {
 	int cpu, ret = 0;
+	struct coresight_device *sink;
 	struct list_head *path;
 
 	mutex_lock(&coresight_mutex);
@@ -508,7 +548,17 @@ int coresight_enable(struct coresight_device *csdev)
 	if (csdev->enable)
 		goto out;
 
-	path = coresight_build_path(csdev);
+	/*
+	 * Search for a valid sink for this session but don't reset the
+	 * "enable_sink" flag in sysFS.  Users get to do that explicitly.
+	 */
+	sink = coresight_get_enabled_sink(false);
+	if (!sink) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	path = coresight_build_path(csdev, sink);
 	if (IS_ERR(path)) {
 		pr_err("building path(s) failed\n");
 		ret = PTR_ERR(path);
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 4ec127b..fe465b5 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -533,6 +533,24 @@ endif
 grep-libs  = $(filter -l%,$(1))
 strip-libs = $(filter-out -l%,$(1))
 
+ifdef CSTRACE_PATH
+  ifeq (${IS_64_BIT}, 1)
+    CSTRACE_LNX = linux64
+  else
+    CSTRACE_LNX = linux
+  endif
+  ifeq (${DEBUG}, 1)
+    LIBCSTRACE = -lcstraced_c_api -lcstraced
+    CSTRACE_LIB_PATH = $(CSTRACE_PATH)/lib/$(CSTRACE_LNX)/dbg
+  else
+    LIBCSTRACE = -lcstraced_c_api -lcstraced
+    CSTRACE_LIB_PATH = $(CSTRACE_PATH)/lib/$(CSTRACE_LNX)/rel
+  endif
+  $(call detected,CSTRACE)
+  $(call detected_var,CSTRACE_PATH)
+  EXTLIBS += -L$(CSTRACE_LIB_PATH) $(LIBCSTRACE) -lstdc++
+endif
+
 ifdef NO_LIBPERL
   CFLAGS += -DNO_LIBPERL
 else
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index a10f064..2810a64 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -86,6 +86,9 @@ include ../scripts/utilities.mak
 #
 # Define FEATURES_DUMP to provide features detection dump file
 # and bypass the feature detection
+#
+# Define NO_CSTRACE if you do not want CoreSight trace decoding support
+#
 
 # As per kernel Makefile, avoid funny character set dependencies
 unexport LC_ALL
diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c
index 47d584d..dfea6b6 100644
--- a/tools/perf/arch/arm/util/cs-etm.c
+++ b/tools/perf/arch/arm/util/cs-etm.c
@@ -575,8 +575,6 @@ static FILE *cs_device__open_file(const char *name)
 	snprintf(path, PATH_MAX,
 		 "%s" CS_BUS_DEVICE_PATH "%s", sysfs, name);
 
-	printf("path: %s\n", path);
-
 	if (stat(path, &st) < 0)
 		return NULL;
 
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 7228d14..667f8c2 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -109,7 +109,8 @@ static struct {
 
 		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
 			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
-			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
+			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_ADDR |
+			      PERF_OUTPUT_IP |
 			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
 			      PERF_OUTPUT_PERIOD,
 
diff --git a/tools/perf/scripts/python/cs-trace-disasm.py b/tools/perf/scripts/python/cs-trace-disasm.py
new file mode 100644
index 0000000..c370e26
--- /dev/null
+++ b/tools/perf/scripts/python/cs-trace-disasm.py
@@ -0,0 +1,134 @@
+# perf script event handlers, generated by perf script -g python
+# Licensed under the terms of the GNU GPL License version 2
+
+# The common_* event handler fields are the most useful fields common to
+# all events.  They don't necessarily correspond to the 'common_*' fields
+# in the format files.  Those fields not available as handler params can
+# be retrieved using Python functions of the form common_*(context).
+# See the perf-trace-python Documentation for the list of available functions.
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+                '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from subprocess import *
+from Core import *
+import re;
+
+from optparse import OptionParser
+
+#
+# Add options to specify vmlinux file and the objdump executable
+#
+parser = OptionParser()
+parser.add_option("-k", "--vmlinux", dest="vmlinux_name",
+                  help="path to vmlinux file")
+parser.add_option("-d", "--objdump", dest="objdump_name",
+                  help="name of objdump executable (in path)")
+(options, args) = parser.parse_args()
+
+if (options.objdump_name == None):
+        sys.exit("No objdump executable specified - use -d or --objdump option")
+
+# initialize global dicts and regular expression
+
+build_ids = dict();
+mmaps = dict();
+disasm_cache = dict();
+disasm_re = re.compile("^\s*([0-9a-fA-F]+):")
+
+cache_size = 16*1024
+
+def trace_begin():
+        cmd_output = check_output(["perf", "buildid-list"]).split('\n');
+        bid_re = re.compile("([a-fA-f0-9]+)[ \t]([^ \n]+)")
+        for line in cmd_output:
+                m = bid_re.search(line)
+                if (m != None) :
+			if (m.group(2) == "[kernel.kallsyms]") :
+				append = "/kallsyms"
+				dirname = "/" + m.group(2)
+			elif (m.group(2) == "[vdso]") :
+				append = "/vdso"
+				dirname = "/" + m.group(2)
+			else:
+				append = "/elf"
+				dirname = m.group(2)
+
+                        build_ids[m.group(2)] =  \
+                        os.environ['PERF_BUILDID_DIR'] +  \
+			dirname + "/" + m.group(1) + append;
+
+        if ((options.vmlinux_name != None) and ("[kernel.kallsyms]" in build_ids)):
+                build_ids['[kernel.kallsyms]'] = options.vmlinux_name;
+        else:
+                del build_ids['[kernel.kallsyms]']
+
+        mmap_re = re.compile("PERF_RECORD_MMAP2 -?[0-9]+/[0-9]+: \[(0x[0-9a-fA-F]+).*:\s.*\s(\S*)")
+        cmd_output= check_output("perf script --show-mmap-events | fgrep PERF_RECORD_MMAP2",shell=True).split('\n')
+        for line in cmd_output:
+                m = mmap_re.search(line)
+                if (m != None) :
+                        mmaps[m.group(2)] = int(m.group(1),0)
+
+
+
+def trace_end():
+        pass
+
+def process_event(t):
+        global cache_size
+        global options
+
+        sample = t['sample']
+        dso = t['dso']
+
+        # don't let the cache get too big, but don't bother with a fancy replacement policy
+        # just clear it when it hits max size
+
+        if (len(disasm_cache) > cache_size):
+                disasm_cache.clear();
+
+        cpu = format(sample['cpu'], "d");
+        addr_range = format(sample['ip'],"x")  + ":" + format(sample['addr'],"x");
+
+        try:
+                disasm_output = disasm_cache[addr_range];
+        except:
+                try:
+                        fname = build_ids[dso];
+                except KeyError:
+                        if (dso == '[kernel.kallsyms]'):
+                                return;
+                        fname = dso;
+
+                if (dso in mmaps):
+                        offset = mmaps[dso];
+                        disasm = [options.objdump_name,"-d","-z", "--adjust-vma="+format(offset,"#x"),"--start-address="+format(sample['ip'],"#x"),"--stop-address="+format(sample['addr'],"#x"), fname]
+                else:
+                        offset = 0
+                        disasm = [options.objdump_name,"-d","-z", "--start-address="+format(sample['ip'],"#x"),"--stop-address="+format(sample['addr'],"#x"),fname]
+                disasm_output = check_output(disasm).split('\n')
+                disasm_cache[addr_range] = disasm_output;
+
+        print "FILE: %s\tCPU: %s" % (dso, cpu);
+        for line in disasm_output:
+                m = disasm_re.search(line)
+                if (m != None) :
+                        try:
+                                print "\t",line
+                        except:
+                                exit(1);
+                else:
+                        continue;
+
+def trace_unhandled(event_name, context, event_fields_dict):
+		print ' '.join(['%s=%s'%(k,str(v))for k,v in sorted(event_fields_dict.items())])
+
+def print_header(event_name, cpu, secs, nsecs, pid, comm):
+        print "print_header"
+	print "%-20s %5u %05u.%09u %8u %-20s " % \
+	(event_name, cpu, secs, nsecs, pid, comm),
diff --git a/tools/perf/scripts/python/cs-trace-ranges.py b/tools/perf/scripts/python/cs-trace-ranges.py
new file mode 100644
index 0000000..c8edacb
--- /dev/null
+++ b/tools/perf/scripts/python/cs-trace-ranges.py
@@ -0,0 +1,44 @@
+#
+# Copyright(C) 2016 Linaro Limited. All rights reserved.
+# Author: Tor Jeremiassen <tor.jeremiassen at linaro.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+                '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+
+def trace_begin():
+        pass;
+
+def trace_end():
+        pass
+
+def process_event(t):
+
+        sample = t['sample']
+
+        print "range:",format(sample['ip'],"x"),"-",format(sample['addr'],"x")
+
+def trace_unhandled(event_name, context, event_fields_dict):
+		print ' '.join(['%s=%s'%(k,str(v))for k,v in sorted(event_fields_dict.items())])
+
+def print_header(event_name, cpu, secs, nsecs, pid, comm):
+        print "print_header"
+	print "%-20s %5u %05u.%09u %8u %-20s " % \
+	(event_name, cpu, secs, nsecs, pid, comm),
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 1dc67ef..2bbb725 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -80,6 +80,8 @@ libperf-$(CONFIG_AUXTRACE) += auxtrace.o
 libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
 libperf-$(CONFIG_AUXTRACE) += intel-pt.o
 libperf-$(CONFIG_AUXTRACE) += intel-bts.o
+libperf-$(CONFIG_AUXTRACE) += cs-etm.o
+libperf-$(CONFIG_AUXTRACE) += cs-etm-decoder/
 libperf-y += parse-branch-options.o
 libperf-y += parse-regs-options.o
 libperf-y += term.o
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index c5a6e0b..4dbd500 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -58,6 +58,7 @@
 
 #include "intel-pt.h"
 #include "intel-bts.h"
+#include "cs-etm.h"
 
 int auxtrace_mmap__mmap(struct auxtrace_mmap *mm,
 			struct auxtrace_mmap_params *mp,
@@ -902,6 +903,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
 	case PERF_AUXTRACE_INTEL_BTS:
 		return intel_bts_process_auxtrace_info(event, session);
 	case PERF_AUXTRACE_CS_ETM:
+		return cs_etm__process_auxtrace_info(event, session);
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/cs-etm-decoder/Build b/tools/perf/util/cs-etm-decoder/Build
new file mode 100644
index 0000000..e097599
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/Build
@@ -0,0 +1,6 @@
+ifeq ($(CSTRACE_PATH),)
+libperf-$(CONFIG_AUXTRACE) += cs-etm-decoder-stub.o
+else
+CFLAGS_cs-etm-decoder.o += -I$(CSTRACE_PATH)/include
+libperf-$(CONFIG_AUXTRACE) += cs-etm-decoder.o
+endif
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
new file mode 100644
index 0000000..d2ebbd2
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder-stub.c
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen at linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU GEneral Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#include "cs-etm-decoder.h"
+#include "../util.h"
+
+
+struct cs_etm_decoder {
+	void *state;
+	int dummy;
+};
+
+int cs_etm_decoder__flush(struct cs_etm_decoder *decoder)
+{
+	(void) decoder;
+	return -1;
+}
+
+int cs_etm_decoder__add_bin_file(struct cs_etm_decoder *decoder,
+				uint64_t offset,
+				uint64_t address,
+				uint64_t len,
+				const char *fname)
+{
+	(void) decoder;
+	(void) offset;
+	(void) address;
+	(void) len;
+	(void) fname;
+	return -1;
+}
+
+const struct cs_etm_state *cs_etm_decoder__process_data_block(
+				struct cs_etm_decoder *decoder,
+				uint64_t indx,
+				const uint8_t *buf,
+				size_t len,
+				size_t *consumed)
+{
+	(void) decoder;
+	(void) indx;
+	(void) buf;
+	(void) len;
+	(void) consumed;
+	return NULL;
+}
+
+int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder,
+				uint64_t address,
+				uint64_t len,
+				cs_etm_mem_cb_type cb_func)
+{
+	(void) decoder;
+	(void) address;
+	(void) len;
+	(void) cb_func;
+	return -1;
+}
+
+int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
+				struct cs_etm_packet *packet)
+{
+	(void) decoder;
+	(void) packet;
+	return -1;
+}
+
+struct cs_etm_decoder *cs_etm_decoder__new(uint32_t num_cpu,
+				struct cs_etm_decoder_params *d_params,
+				struct cs_etm_trace_params t_params[])
+{
+	(void) num_cpu;
+	(void) d_params;
+	(void) t_params;
+	return NULL;
+}
+
+void cs_etm_decoder__free(struct cs_etm_decoder *decoder)
+{
+	(void) decoder;
+	return;
+}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
new file mode 100644
index 0000000..ee2e02f
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -0,0 +1,527 @@
+/*
+ *
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen at linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU GEneral Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <stdlib.h>
+
+#include "../cs-etm.h"
+#include "cs-etm-decoder.h"
+#include "../util.h"
+#include "../util/intlist.h"
+
+#include "c_api/opencsd_c_api.h"
+#include "ocsd_if_types.h"
+#include "etmv4/trc_pkt_types_etmv4.h"
+
+#define MAX_BUFFER 1024
+
+struct cs_etm_decoder {
+	struct cs_etm_state	state;
+	dcd_tree_handle_t	dcd_tree;
+	void (*packet_printer)(const char *);
+	cs_etm_mem_cb_type	mem_access;
+	ocsd_datapath_resp_t	prev_return;
+	size_t			prev_processed;
+	bool			trace_on;
+	bool			discontinuity;
+	struct cs_etm_packet	packet_buffer[MAX_BUFFER];
+	uint32_t		packet_count;
+	uint32_t		head;
+	uint32_t		tail;
+	uint32_t		end_tail;
+};
+
+static uint32_t cs_etm_decoder__mem_access(const void *context,
+					   const ocsd_vaddr_t address,
+					   const ocsd_mem_space_acc_t mem_space,
+					   const uint32_t req_size,
+					   uint8_t *buffer)
+{
+	struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+	(void) mem_space;
+
+	return decoder->mem_access(decoder->state.data, address, req_size, buffer);
+}
+
+static int cs_etm_decoder__gen_etmv4_config(struct cs_etm_trace_params *params,
+					    ocsd_etmv4_cfg *config)
+{
+	config->reg_configr = params->reg_configr;
+	config->reg_traceidr = params->reg_traceidr;
+	config->reg_idr0 = params->reg_idr0;
+	config->reg_idr1 = params->reg_idr1;
+	config->reg_idr2 = params->reg_idr2;
+	config->reg_idr8 = params->reg_idr8;
+
+	config->reg_idr9 = 0;
+	config->reg_idr10 = 0;
+	config->reg_idr11 = 0;
+	config->reg_idr12 = 0;
+	config->reg_idr13 = 0;
+	config->arch_ver = ARCH_V8;
+	config->core_prof = profile_CortexA;
+
+	return 0;
+}
+
+static int cs_etm_decoder__flush_packet(struct cs_etm_decoder *decoder)
+{
+	int err = 0;
+
+	if (decoder == NULL)
+		return -1;
+
+	if (decoder->packet_count >= 31)
+		return -1;
+
+	if (decoder->tail != decoder->end_tail) {
+		decoder->tail = (decoder->tail + 1) & (MAX_BUFFER - 1);
+		decoder->packet_count++;
+	}
+
+	return err;
+}
+
+int cs_etm_decoder__flush(struct cs_etm_decoder *decoder)
+{
+	return cs_etm_decoder__flush_packet(decoder);
+}
+
+static int cs_etm_decoder__buffer_packet(struct cs_etm_decoder *decoder,
+					 const ocsd_generic_trace_elem *elem,
+					 const uint8_t trace_chan_id,
+					 enum cs_etm_sample_type sample_type)
+{
+	int err = 0;
+	uint32_t et = 0;
+	struct int_node *inode = NULL;
+
+	if (decoder == NULL)
+		return -1;
+
+	if (decoder->packet_count >= 31)
+		return -1;
+
+	err = cs_etm_decoder__flush_packet(decoder);
+
+	if (err)
+		return err;
+
+	et = decoder->end_tail;
+	/* Search the RB tree for the cpu associated with this traceID */
+	inode = intlist__find(traceid_list, trace_chan_id);
+	if (!inode)
+		return PTR_ERR(inode);
+
+	decoder->packet_buffer[et].sample_type = sample_type;
+	decoder->packet_buffer[et].start_addr = elem->st_addr;
+	decoder->packet_buffer[et].end_addr   = elem->en_addr;
+	decoder->packet_buffer[et].exc	= false;
+	decoder->packet_buffer[et].exc_ret    = false;
+	decoder->packet_buffer[et].cpu	= *((int *)inode->priv);
+
+	et = (et + 1) & (MAX_BUFFER - 1);
+
+	decoder->end_tail = et;
+
+	return err;
+}
+
+static int cs_etm_decoder__mark_exception(struct cs_etm_decoder *decoder)
+{
+	int err = 0;
+
+	if (decoder == NULL)
+		return -1;
+
+	decoder->packet_buffer[decoder->end_tail].exc = true;
+
+	return err;
+}
+
+static int cs_etm_decoder__mark_exception_return(struct cs_etm_decoder *decoder)
+{
+	int err = 0;
+
+	if (decoder == NULL)
+		return -1;
+
+	decoder->packet_buffer[decoder->end_tail].exc_ret = true;
+
+	return err;
+}
+
+static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
+			const void *context,
+			const ocsd_trc_index_t indx,
+			const uint8_t trace_chan_id,
+			const ocsd_generic_trace_elem *elem)
+{
+	ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
+	struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+
+	(void) indx;
+	(void) trace_chan_id;
+
+	switch (elem->elem_type) {
+	case OCSD_GEN_TRC_ELEM_UNKNOWN:
+		break;
+	case OCSD_GEN_TRC_ELEM_NO_SYNC:
+		decoder->trace_on = false;
+		break;
+	case OCSD_GEN_TRC_ELEM_TRACE_ON:
+		decoder->trace_on = true;
+		break;
+	case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
+		cs_etm_decoder__buffer_packet(decoder, elem,
+					     trace_chan_id, CS_ETM_RANGE);
+		resp = OCSD_RESP_WAIT;
+		break;
+	case OCSD_GEN_TRC_ELEM_EXCEPTION:
+		cs_etm_decoder__mark_exception(decoder);
+		break;
+	case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
+		cs_etm_decoder__mark_exception_return(decoder);
+		break;
+	case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
+	case OCSD_GEN_TRC_ELEM_EO_TRACE:
+	case OCSD_GEN_TRC_ELEM_ADDR_NACC:
+	case OCSD_GEN_TRC_ELEM_TIMESTAMP:
+	case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
+	case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
+	case OCSD_GEN_TRC_ELEM_EVENT:
+	case OCSD_GEN_TRC_ELEM_SWTRACE:
+	case OCSD_GEN_TRC_ELEM_CUSTOM:
+	default:
+		break;
+	}
+
+	decoder->state.err = 0;
+
+	return resp;
+}
+
+static ocsd_datapath_resp_t cs_etm_decoder__etmv4i_packet_printer(
+	const void *context,
+	const ocsd_datapath_op_t op,
+	const ocsd_trc_index_t indx,
+	const ocsd_etmv4_i_pkt *pkt)
+{
+	const size_t PACKET_STR_LEN = 1024;
+	ocsd_datapath_resp_t ret = OCSD_RESP_CONT;
+	char packet_str[PACKET_STR_LEN];
+	size_t offset;
+	struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+
+	sprintf(packet_str, "%ld: ", (long int) indx);
+	offset = strlen(packet_str);
+
+	switch (op) {
+	case OCSD_OP_DATA:
+		if (ocsd_pkt_str(OCSD_PROTOCOL_ETMV4I,
+				  (void *)pkt,
+				  packet_str+offset,
+				  PACKET_STR_LEN-offset) != OCSD_OK)
+			ret = OCSD_RESP_FATAL_INVALID_PARAM;
+		break;
+	case OCSD_OP_EOT:
+		sprintf(packet_str, "**** END OF TRACE ****\n");
+		break;
+	case OCSD_OP_FLUSH:
+	case OCSD_OP_RESET:
+	default:
+		break;
+	}
+
+	decoder->packet_printer(packet_str);
+
+	return ret;
+}
+
+static int cs_etm_decoder__create_etmv4i_packet_printer(struct cs_etm_decoder_params *d_params,
+							struct cs_etm_trace_params *t_params,
+							struct cs_etm_decoder *decoder)
+{
+	ocsd_etmv4_cfg trace_config;
+	int ret = 0;
+	unsigned char CSID; /* CSID extracted from the config data */
+
+	if (d_params->packet_printer == NULL)
+		return -1;
+
+	ret = cs_etm_decoder__gen_etmv4_config(t_params, &trace_config);
+
+	if (ret != 0)
+		return -1;
+
+	decoder->packet_printer = d_params->packet_printer;
+
+	ret = ocsd_dt_create_decoder(decoder->dcd_tree,
+				     OCSD_BUILTIN_DCD_ETMV4I,
+				     OCSD_CREATE_FLG_PACKET_PROC,
+				     (void *)&trace_config,
+				     &CSID);
+
+	if (ret != 0)
+		return -1;
+
+	ret = ocsd_dt_attach_packet_callback(decoder->dcd_tree,
+					  CSID,
+					  OCSD_C_API_CB_PKT_SINK,
+					  cs_etm_decoder__etmv4i_packet_printer,
+					  decoder);
+	return ret;
+}
+
+static int cs_etm_decoder__create_etmv4i_packet_decoder(struct cs_etm_decoder_params *d_params,
+							struct cs_etm_trace_params *t_params,
+							struct cs_etm_decoder *decoder)
+{
+	ocsd_etmv4_cfg trace_config;
+	int ret = 0;
+	unsigned char CSID; /* CSID extracted from the config data */
+
+	decoder->packet_printer = d_params->packet_printer;
+
+	ret = cs_etm_decoder__gen_etmv4_config(t_params, &trace_config);
+
+	if (ret != 0)
+		return -1;
+
+	ret = ocsd_dt_create_decoder(decoder->dcd_tree,
+				     OCSD_BUILTIN_DCD_ETMV4I,
+				     OCSD_CREATE_FLG_FULL_DECODER,
+				     (void *)&trace_config,
+				     &CSID);
+
+	if (ret != 0)
+		return -1;
+
+	ret = ocsd_dt_set_gen_elem_outfn(decoder->dcd_tree,
+					cs_etm_decoder__gen_trace_elem_printer, decoder);
+	return ret;
+}
+
+int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder,
+				      uint64_t address,
+				      uint64_t len,
+				      cs_etm_mem_cb_type cb_func)
+{
+	int err;
+
+	decoder->mem_access = cb_func;
+	err = ocsd_dt_add_callback_mem_acc(decoder->dcd_tree,
+					   address,
+					   address+len-1,
+					   OCSD_MEM_SPACE_ANY,
+					   cs_etm_decoder__mem_access,
+					   decoder);
+	return err;
+}
+
+
+int cs_etm_decoder__add_bin_file(struct cs_etm_decoder *decoder,
+				 uint64_t offset,
+				 uint64_t address,
+				 uint64_t len,
+				 const char *fname)
+{
+	int err = 0;
+	ocsd_file_mem_region_t region;
+
+	(void) len;
+	if (NULL == decoder)
+		return -1;
+
+	if (NULL == decoder->dcd_tree)
+		return -1;
+
+	region.file_offset = offset;
+	region.start_address = address;
+	region.region_size = len;
+	err = ocsd_dt_add_binfile_region_mem_acc(decoder->dcd_tree,
+					   &region,
+					   1,
+					   OCSD_MEM_SPACE_ANY,
+					   fname);
+
+	return err;
+}
+
+const struct cs_etm_state *cs_etm_decoder__process_data_block(struct cs_etm_decoder *decoder,
+					uint64_t indx,
+					const uint8_t *buf,
+					size_t len,
+					size_t *consumed)
+{
+	int ret = 0;
+	ocsd_datapath_resp_t dp_ret = decoder->prev_return;
+	size_t processed = 0;
+
+	if (decoder->packet_count > 0) {
+		decoder->state.err = ret;
+		*consumed = processed;
+		return &(decoder->state);
+	}
+
+	while ((processed < len) && (0 == ret)) {
+
+		if (OCSD_DATA_RESP_IS_CONT(dp_ret)) {
+			uint32_t count;
+			dp_ret = ocsd_dt_process_data(decoder->dcd_tree,
+							OCSD_OP_DATA,
+							indx+processed,
+							len - processed,
+							&buf[processed],
+							&count);
+			processed += count;
+
+		} else if (OCSD_DATA_RESP_IS_WAIT(dp_ret)) {
+			dp_ret = ocsd_dt_process_data(decoder->dcd_tree,
+							OCSD_OP_FLUSH,
+							0,
+							0,
+							NULL,
+							NULL);
+			break;
+		} else
+			ret = -1;
+	}
+	if (OCSD_DATA_RESP_IS_WAIT(dp_ret)) {
+		if (OCSD_DATA_RESP_IS_CONT(decoder->prev_return)) {
+			decoder->prev_processed = processed;
+		}
+		processed = 0;
+	} else if (OCSD_DATA_RESP_IS_WAIT(decoder->prev_return)) {
+		processed = decoder->prev_processed;
+		decoder->prev_processed = 0;
+	}
+	*consumed = processed;
+	decoder->prev_return = dp_ret;
+	decoder->state.err = ret;
+	return &(decoder->state);
+}
+
+int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
+			       struct cs_etm_packet *packet)
+{
+	if (decoder->packet_count == 0)
+		return -1;
+
+	if (packet == NULL)
+		return -1;
+
+	*packet = decoder->packet_buffer[decoder->head];
+
+	decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1);
+
+	decoder->packet_count--;
+
+	return 0;
+}
+
+static void cs_etm_decoder__clear_buffer(struct cs_etm_decoder *decoder)
+{
+	unsigned i;
+
+	decoder->head = 0;
+	decoder->tail = 0;
+	decoder->end_tail = 0;
+	decoder->packet_count = 0;
+	for (i = 0; i < MAX_BUFFER; i++) {
+		decoder->packet_buffer[i].start_addr = 0xdeadbeefdeadbeefUL;
+		decoder->packet_buffer[i].end_addr   = 0xdeadbeefdeadbeefUL;
+		decoder->packet_buffer[i].exc	= false;
+		decoder->packet_buffer[i].exc_ret    = false;
+		decoder->packet_buffer[i].cpu	= INT_MIN;
+	}
+}
+
+struct cs_etm_decoder *cs_etm_decoder__new(uint32_t num_cpu,
+					   struct cs_etm_decoder_params *d_params,
+					   struct cs_etm_trace_params t_params[])
+{
+	struct cs_etm_decoder *decoder;
+	ocsd_dcd_tree_src_t format;
+	uint32_t flags;
+	int ret;
+	size_t i;
+
+	if ((t_params == NULL) || (d_params == 0))
+		return NULL;
+
+	decoder = zalloc(sizeof(struct cs_etm_decoder));
+
+	if (decoder == NULL)
+		return NULL;
+
+	decoder->state.data = d_params->data;
+	decoder->prev_return = OCSD_RESP_CONT;
+	cs_etm_decoder__clear_buffer(decoder);
+	format = (d_params->formatted ? OCSD_TRC_SRC_FRAME_FORMATTED :
+					 OCSD_TRC_SRC_SINGLE);
+	flags = 0;
+	flags |= (d_params->fsyncs ? OCSD_DFRMTR_HAS_FSYNCS : 0);
+	flags |= (d_params->hsyncs ? OCSD_DFRMTR_HAS_HSYNCS : 0);
+	flags |= (d_params->frame_aligned ? OCSD_DFRMTR_FRAME_MEM_ALIGN : 0);
+
+	/* Create decode tree for the data source */
+	decoder->dcd_tree = ocsd_create_dcd_tree(format, flags);
+
+	if (decoder->dcd_tree == 0)
+		goto err_free_decoder;
+
+	for (i = 0; i < num_cpu; ++i) {
+		switch (t_params[i].protocol) {
+		case CS_ETM_PROTO_ETMV4i:
+			if (d_params->operation == CS_ETM_OPERATION_PRINT)
+				ret = cs_etm_decoder__create_etmv4i_packet_printer(d_params, &t_params[i], decoder);
+			else if (d_params->operation == CS_ETM_OPERATION_DECODE)
+				ret = cs_etm_decoder__create_etmv4i_packet_decoder(d_params, &t_params[i], decoder);
+			else
+				ret = -CS_ETM_ERR_PARAM;
+			if (ret != 0)
+				goto err_free_decoder_tree;
+			break;
+		default:
+			goto err_free_decoder_tree;
+			break;
+		}
+	}
+
+
+	return decoder;
+
+err_free_decoder_tree:
+	ocsd_destroy_dcd_tree(decoder->dcd_tree);
+err_free_decoder:
+	free(decoder);
+	return NULL;
+}
+
+
+void cs_etm_decoder__free(struct cs_etm_decoder *decoder)
+{
+	if (decoder == NULL)
+		return;
+
+	ocsd_destroy_dcd_tree(decoder->dcd_tree);
+	decoder->dcd_tree = NULL;
+
+	free(decoder);
+}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
new file mode 100644
index 0000000..7e9db4c
--- /dev/null
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen at linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU GEneral Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDE__CS_ETM_DECODER_H__
+#define INCLUDE__CS_ETM_DECODER_H__
+
+#include <linux/types.h>
+#include <stdio.h>
+
+struct cs_etm_decoder;
+
+struct cs_etm_buffer {
+	const unsigned char *buf;
+	size_t  len;
+	uint64_t offset;
+	//bool    consecutive;
+	uint64_t	ref_timestamp;
+	//uint64_t	trace_nr;
+};
+
+enum cs_etm_sample_type {
+	CS_ETM_RANGE      = 1 << 0,
+};
+
+struct cs_etm_state {
+	int err;
+	void *data;
+	unsigned isa;
+	uint64_t start;
+	uint64_t end;
+	uint64_t timestamp;
+};
+
+struct cs_etm_packet {
+	enum cs_etm_sample_type sample_type;
+	uint64_t start_addr;
+	uint64_t end_addr;
+	bool     exc;
+	bool     exc_ret;
+	int cpu;
+};
+
+
+struct cs_etm_queue;
+typedef uint32_t (*cs_etm_mem_cb_type)(struct cs_etm_queue *, uint64_t, size_t, uint8_t *);
+
+struct cs_etm_trace_params {
+	void *etmv4i_packet_handler;
+	uint32_t reg_idr0;
+	uint32_t reg_idr1;
+	uint32_t reg_idr2;
+	uint32_t reg_idr8;
+	uint32_t reg_configr;
+	uint32_t reg_traceidr;
+	int  protocol;
+};
+
+struct cs_etm_decoder_params {
+	int  operation;
+	void (*packet_printer)(const char *);
+	cs_etm_mem_cb_type  mem_acc_cb;
+	bool formatted;
+	bool fsyncs;
+	bool hsyncs;
+	bool frame_aligned;
+	void *data;
+};
+
+enum {
+	CS_ETM_PROTO_ETMV3 = 1,
+	CS_ETM_PROTO_ETMV4i,
+	CS_ETM_PROTO_ETMV4d,
+};
+
+enum {
+	CS_ETM_OPERATION_PRINT = 1,
+	CS_ETM_OPERATION_DECODE,
+};
+
+enum {
+	CS_ETM_ERR_NOMEM = 1,
+	CS_ETM_ERR_NODATA,
+	CS_ETM_ERR_PARAM,
+};
+
+
+struct cs_etm_decoder *cs_etm_decoder__new(uint32_t num_cpu, struct cs_etm_decoder_params *, struct cs_etm_trace_params []);
+
+int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *, uint64_t, uint64_t, cs_etm_mem_cb_type);
+
+int cs_etm_decoder__flush(struct cs_etm_decoder *);
+void cs_etm_decoder__free(struct cs_etm_decoder *);
+int cs_etm_decoder__get_packet(struct cs_etm_decoder *, struct cs_etm_packet *);
+
+int cs_etm_decoder__add_bin_file(struct cs_etm_decoder *, uint64_t, uint64_t, uint64_t, const char *);
+
+const struct cs_etm_state *cs_etm_decoder__process_data_block(struct cs_etm_decoder *,
+				       uint64_t,
+				       const uint8_t *,
+				       size_t,
+				       size_t *);
+
+#endif /* INCLUDE__CS_ETM_DECODER_H__ */
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
new file mode 100644
index 0000000..91d6a8a
--- /dev/null
+++ b/tools/perf/util/cs-etm.c
@@ -0,0 +1,1501 @@
+/*
+ * Copyright(C) 2016 Linaro Limited. All rights reserved.
+ * Author: Tor Jeremiassen <tor.jeremiassen at linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+
+#include "perf.h"
+#include "thread_map.h"
+#include "thread.h"
+#include "thread-stack.h"
+#include "callchain.h"
+#include "auxtrace.h"
+#include "evlist.h"
+#include "machine.h"
+#include "util.h"
+#include "util/intlist.h"
+#include "color.h"
+#include "cs-etm.h"
+#include "cs-etm-decoder/cs-etm-decoder.h"
+#include "debug.h"
+
+#include <stdlib.h>
+
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+#define MAX_TIMESTAMP (~0ULL)
+
+struct cs_etm_auxtrace {
+	struct auxtrace		auxtrace;
+	struct auxtrace_queues	queues;
+	struct auxtrace_heap	heap;
+	u64			**metadata;
+	u32			auxtrace_type;
+	struct perf_session	*session;
+	struct machine		*machine;
+	struct perf_evsel	*switch_evsel;
+	struct thread		*unknown_thread;
+	uint32_t		num_cpu;
+	bool			timeless_decoding;
+	bool			sampling_mode;
+	bool			snapshot_mode;
+	bool			data_queued;
+	bool			sync_switch;
+	bool			synth_needs_swap;
+	int			have_sched_switch;
+
+	bool			sample_instructions;
+	u64			instructions_sample_type;
+	u64			instructions_sample_period;
+	u64			instructions_id;
+	struct itrace_synth_opts synth_opts;
+	unsigned		pmu_type;
+};
+
+struct cs_etm_queue {
+	struct cs_etm_auxtrace	*etm;
+	unsigned		queue_nr;
+	struct auxtrace_buffer	*buffer;
+	const struct		cs_etm_state *state;
+	struct ip_callchain	*chain;
+	union perf_event	*event_buf;
+	bool			on_heap;
+	bool			step_through_buffers;
+	bool			use_buffer_pid_tid;
+	pid_t			pid, tid;
+	int			cpu;
+	struct thread		*thread;
+	u64			time;
+	u64			timestamp;
+	bool			stop;
+	struct cs_etm_decoder	*decoder;
+	u64			offset;
+	bool			eot;
+	bool			kernel_mapped;
+};
+
+static int cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq);
+static int cs_etm__update_queues(struct cs_etm_auxtrace *);
+static int cs_etm__process_queues(struct cs_etm_auxtrace *, u64);
+static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *, pid_t, u64);
+static uint32_t cs_etm__mem_access(struct cs_etm_queue *, uint64_t, size_t, uint8_t *);
+
+static void cs_etm__packet_dump(const char *pkt_string)
+{
+	const char *color = PERF_COLOR_BLUE;
+
+	color_fprintf(stdout, color, "  %s\n", pkt_string);
+	fflush(stdout);
+}
+
+static void cs_etm__dump_event(struct cs_etm_auxtrace *etm,
+			      struct auxtrace_buffer *buffer)
+{
+	const char *color = PERF_COLOR_BLUE;
+	struct cs_etm_decoder_params d_params;
+	struct cs_etm_trace_params *t_params;
+	struct cs_etm_decoder *decoder;
+	size_t buffer_used = 0;
+	size_t i;
+
+	fprintf(stdout, "\n");
+	color_fprintf(stdout, color,
+		     ". ... CoreSight ETM Trace data: size %zu bytes\n",
+		     buffer->size);
+
+	t_params = zalloc(sizeof(struct cs_etm_trace_params) * etm->num_cpu);
+	for (i = 0; i < etm->num_cpu; ++i) {
+		t_params[i].protocol = CS_ETM_PROTO_ETMV4i;
+		t_params[i].reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0];
+		t_params[i].reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1];
+		t_params[i].reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2];
+		t_params[i].reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8];
+		t_params[i].reg_configr = etm->metadata[i][CS_ETMV4_TRCCONFIGR];
+		t_params[i].reg_traceidr = etm->metadata[i][CS_ETMV4_TRCTRACEIDR];
+  //[CS_ETMV4_TRCAUTHSTATUS] = "   TRCAUTHSTATUS		  %"PRIx64"\n",
+	}
+	d_params.packet_printer = cs_etm__packet_dump;
+	d_params.operation = CS_ETM_OPERATION_PRINT;
+	d_params.formatted = true;
+	d_params.fsyncs = false;
+	d_params.hsyncs = false;
+	d_params.frame_aligned = true;
+
+	decoder = cs_etm_decoder__new(etm->num_cpu, &d_params, t_params);
+
+	zfree(&t_params);
+
+	if (decoder == NULL)
+		return;
+	do {
+	    size_t consumed;
+	    cs_etm_decoder__process_data_block(decoder, buffer->offset,
+					       &(((uint8_t *)buffer->data)[buffer_used]),
+					       buffer->size - buffer_used, &consumed);
+	    buffer_used += consumed;
+	} while (buffer_used < buffer->size);
+	cs_etm_decoder__free(decoder);
+}
+
+static int cs_etm__flush_events(struct perf_session *session, struct perf_tool *tool)
+{
+	struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
+						   struct cs_etm_auxtrace,
+						   auxtrace);
+
+	int ret;
+
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events)
+		return -EINVAL;
+
+	ret = cs_etm__update_queues(etm);
+
+	if (ret < 0)
+		return ret;
+
+	if (etm->timeless_decoding)
+		return cs_etm__process_timeless_queues(etm, -1, MAX_TIMESTAMP - 1);
+
+	return cs_etm__process_queues(etm, MAX_TIMESTAMP);
+}
+
+static void  cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm,
+				    struct auxtrace_queue *queue)
+{
+	struct cs_etm_queue *etmq = queue->priv;
+
+	if ((queue->tid == -1) || (etm->have_sched_switch)) {
+		etmq->tid = machine__get_current_tid(etm->machine, etmq->cpu);
+		thread__zput(etmq->thread);
+	}
+
+	if ((!etmq->thread) && (etmq->tid != -1))
+		etmq->thread = machine__find_thread(etm->machine, -1, etmq->tid);
+
+	if (etmq->thread) {
+		etmq->pid = etmq->thread->pid_;
+		if (queue->cpu == -1)
+			etmq->cpu = etmq->thread->cpu;
+	}
+}
+
+static void cs_etm__free_queue(void *priv)
+{
+	struct cs_etm_queue *etmq = priv;
+
+	if (!etmq)
+		return;
+
+	thread__zput(etmq->thread);
+	cs_etm_decoder__free(etmq->decoder);
+	zfree(&etmq->event_buf);
+	zfree(&etmq->chain);
+	free(etmq);
+}
+
+static void cs_etm__free_events(struct perf_session *session)
+{
+	struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
+						   struct cs_etm_auxtrace,
+						   auxtrace);
+
+	struct auxtrace_queues *queues = &(aux->queues);
+
+	unsigned i;
+
+	for (i = 0; i < queues->nr_queues; ++i) {
+		cs_etm__free_queue(queues->queue_array[i].priv);
+		queues->queue_array[i].priv = 0;
+	}
+
+	auxtrace_queues__free(queues);
+
+}
+
+static void cs_etm__free(struct perf_session *session)
+{
+
+	size_t i;
+	struct int_node *inode, *tmp;
+	struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
+						   struct cs_etm_auxtrace,
+						   auxtrace);
+	auxtrace_heap__free(&aux->heap);
+	cs_etm__free_events(session);
+	session->auxtrace = NULL;
+
+	/* First remove all traceID/CPU# nodes from the RB tree */
+	intlist__for_each_entry_safe(inode, tmp, traceid_list)
+		intlist__remove(traceid_list, inode);
+	/* Then the RB tree itself */
+	intlist__delete(traceid_list);
+
+	//thread__delete(aux->unknown_thread);
+	for (i = 0; i < aux->num_cpu; ++i)
+		zfree(&aux->metadata[i]);
+	zfree(&aux->metadata);
+	free(aux);
+}
+
+static void cs_etm__use_buffer_pid_tid(struct cs_etm_queue *etmq,
+				      struct auxtrace_queue *queue,
+				      struct auxtrace_buffer *buffer)
+{
+	if ((queue->cpu == -1) && (buffer->cpu != -1))
+		etmq->cpu = buffer->cpu;
+
+	etmq->pid = buffer->pid;
+	etmq->tid = buffer->tid;
+
+	thread__zput(etmq->thread);
+
+	if (etmq->tid != -1) {
+		if (etmq->pid != -1) {
+			etmq->thread = machine__findnew_thread(etmq->etm->machine,
+							       etmq->pid,
+							       etmq->tid);
+		} else {
+			etmq->thread = machine__findnew_thread(etmq->etm->machine,
+							       -1,
+							       etmq->tid);
+		}
+	}
+}
+
+
+static int cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq)
+{
+	struct auxtrace_buffer *aux_buffer = etmq->buffer;
+	struct auxtrace_buffer *old_buffer = aux_buffer;
+	struct auxtrace_queue *queue;
+
+	if (etmq->stop) {
+		buff->len = 0;
+		return 0;
+	}
+
+	queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
+
+	aux_buffer = auxtrace_buffer__next(queue, aux_buffer);
+
+	if (!aux_buffer) {
+		if (old_buffer)
+			auxtrace_buffer__drop_data(old_buffer);
+		buff->len = 0;
+		return 0;
+	}
+
+	etmq->buffer = aux_buffer;
+
+	if (!aux_buffer->data) {
+		int fd = perf_data_file__fd(etmq->etm->session->file);
+
+		aux_buffer->data = auxtrace_buffer__get_data(aux_buffer, fd);
+		if (!aux_buffer->data)
+			return -ENOMEM;
+	}
+
+	if (old_buffer)
+		auxtrace_buffer__drop_data(old_buffer);
+
+	if (aux_buffer->use_data) {
+		buff->offset = aux_buffer->offset;
+		buff->len = aux_buffer->use_size;
+		buff->buf = aux_buffer->use_data;
+	} else {
+		buff->offset = aux_buffer->offset;
+		buff->len = aux_buffer->size;
+		buff->buf = aux_buffer->data;
+	}
+	/*
+	 * buff->offset = 0;
+	 * buff->len = sizeof(cstrace);
+	 * buff->buf = cstrace;
+	*/
+
+	buff->ref_timestamp = aux_buffer->reference;
+
+	if (etmq->use_buffer_pid_tid &&
+	    ((etmq->pid != aux_buffer->pid) ||
+	     (etmq->tid != aux_buffer->tid))) {
+		cs_etm__use_buffer_pid_tid(etmq, queue, aux_buffer);
+	}
+
+	if (etmq->step_through_buffers)
+		etmq->stop = true;
+
+	return buff->len;
+}
+
+static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm,
+					       unsigned int queue_nr)
+{
+	struct cs_etm_decoder_params d_params;
+	struct cs_etm_trace_params   *t_params;
+	struct cs_etm_queue *etmq;
+	size_t i;
+
+	etmq = zalloc(sizeof(struct cs_etm_queue));
+	if (!etmq)
+		return NULL;
+
+	if (etm->synth_opts.callchain) {
+		size_t sz = sizeof(struct ip_callchain);
+
+		sz += etm->synth_opts.callchain_sz * sizeof(u64);
+		etmq->chain = zalloc(sz);
+		if (!etmq->chain)
+			goto out_free;
+	} else {
+		etmq->chain = NULL;
+	}
+
+	etmq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
+	if (!etmq->event_buf)
+		goto out_free;
+
+	etmq->etm = etm;
+	etmq->queue_nr = queue_nr;
+	etmq->pid = -1;
+	etmq->tid = -1;
+	etmq->cpu = -1;
+	etmq->stop = false;
+	etmq->kernel_mapped = false;
+
+	t_params = zalloc(sizeof(struct cs_etm_trace_params)*etm->num_cpu);
+
+	for (i = 0; i < etm->num_cpu; ++i) {
+		t_params[i].reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0];
+		t_params[i].reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1];
+		t_params[i].reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2];
+		t_params[i].reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8];
+		t_params[i].reg_configr = etm->metadata[i][CS_ETMV4_TRCCONFIGR];
+		t_params[i].reg_traceidr = etm->metadata[i][CS_ETMV4_TRCTRACEIDR];
+		t_params[i].protocol = CS_ETM_PROTO_ETMV4i;
+	}
+	d_params.packet_printer = cs_etm__packet_dump;
+	d_params.operation = CS_ETM_OPERATION_DECODE;
+	d_params.formatted = true;
+	d_params.fsyncs = false;
+	d_params.hsyncs = false;
+	d_params.frame_aligned = true;
+	d_params.data = etmq;
+
+	etmq->decoder = cs_etm_decoder__new(etm->num_cpu, &d_params, t_params);
+
+
+	zfree(&t_params);
+
+	if (!etmq->decoder)
+		goto out_free;
+
+	etmq->offset = 0;
+	etmq->eot = false;
+
+	return etmq;
+
+out_free:
+	zfree(&etmq->event_buf);
+	zfree(&etmq->chain);
+	free(etmq);
+	return NULL;
+}
+
+static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
+			      struct auxtrace_queue *queue,
+			      unsigned int queue_nr)
+{
+	struct cs_etm_queue *etmq = queue->priv;
+
+	if (list_empty(&(queue->head)))
+		return 0;
+
+	if (etmq == NULL) {
+		etmq = cs_etm__alloc_queue(etm, queue_nr);
+
+		if (etmq == NULL)
+			return -ENOMEM;
+
+		queue->priv = etmq;
+
+		if (queue->cpu != -1)
+			etmq->cpu = queue->cpu;
+
+		etmq->tid = queue->tid;
+
+		if (etm->sampling_mode)
+			if (etm->timeless_decoding)
+				etmq->step_through_buffers = true;
+			if (etm->timeless_decoding || !etm->have_sched_switch)
+				etmq->use_buffer_pid_tid = true;
+	}
+
+	if (!etmq->on_heap &&
+	    (!etm->sync_switch)) {
+		const struct cs_etm_state *state;
+		int ret = 0;
+
+		if (etm->timeless_decoding)
+			return ret;
+
+		//cs_etm__log("queue %u getting timestamp\n", queue_nr);
+		//cs_etm__log("queue %u decoding cpu %d pid %d tid %d\n",
+			   //queue_nr, etmq->cpu, etmq->pid, etmq->tid);
+		(void) state;
+		return ret;
+		/*
+		while (1) {
+			state = cs_etm_decoder__decode(etmq->decoder);
+			if (state->err) {
+				if (state->err == CS_ETM_ERR_NODATA) {
+					//cs_etm__log("queue %u has no timestamp\n",
+						   //queue_nr);
+					return 0;
+				}
+				continue;
+			}
+			if (state->timestamp)
+				break;
+		}
+
+		etmq->timestamp = state->timestamp;
+		//cs_etm__log("queue %u timestamp 0x%"PRIx64 "\n",
+			   //queue_nr, etmq->timestamp);
+		etmq->state = state;
+		etmq->have_sample = true;
+		//cs_etm__sample_flags(etmq);
+		ret = auxtrace_heap__add(&etm->heap, queue_nr, etmq->timestamp);
+		if (ret)
+			return ret;
+		etmq->on_heap = true;
+		*/
+	}
+
+	return 0;
+}
+
+
+static int cs_etm__setup_queues(struct cs_etm_auxtrace *etm)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < etm->queues.nr_queues; i++) {
+		ret = cs_etm__setup_queue(etm, &(etm->queues.queue_array[i]), i);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+#if 0
+struct cs_etm_cache_entry {
+	struct auxtrace_cache_entry	entry;
+	uint64_t			icount;
+	uint64_t			bcount;
+};
+
+static size_t cs_etm__cache_divisor(void)
+{
+	static size_t d = 64;
+
+	return d;
+}
+
+static size_t cs_etm__cache_size(struct dso *dso,
+				struct machine *machine)
+{
+	off_t size;
+
+	size = dso__data_size(dso, machine);
+	size /= cs_etm__cache_divisor();
+
+	if (size < 1000)
+		return 10;
+
+	if (size > (1 << 21))
+		return 21;
+
+	return 32 - __builtin_clz(size);
+}
+
+static struct auxtrace_cache *cs_etm__cache(struct dso *dso,
+					   struct machine *machine)
+{
+	struct auxtrace_cache *c;
+	size_t bits;
+
+	if (dso->auxtrace_cache)
+		return dso->auxtrace_cache;
+
+	bits = cs_etm__cache_size(dso, machine);
+
+	c = auxtrace_cache__new(bits, sizeof(struct cs_etm_cache_entry), 200);
+
+	dso->auxtrace_cache = c;
+
+	return c;
+}
+
+static int cs_etm__cache_add(struct dso *dso, struct machine *machine,
+			    uint64_t offset, uint64_t icount, uint64_t bcount)
+{
+	struct auxtrace_cache *c = cs_etm__cache(dso, machine);
+	struct cs_etm_cache_entry *e;
+	int err;
+
+	if (!c)
+		return -ENOMEM;
+
+	e = auxtrace_cache__alloc_entry(c);
+	if (!e)
+		return -ENOMEM;
+
+	e->icount = icount;
+	e->bcount = bcount;
+
+	err = auxtrace_cache__add(c, offset, &e->entry);
+
+	if (err)
+		auxtrace_cache__free_entry(c, e);
+
+	return err;
+}
+
+static struct cs_etm_cache_entry *cs_etm__cache_lookup(struct dso *dso,
+						      struct machine *machine,
+						      uint64_t offset)
+{
+	struct auxtrace_cache *c = cs_etm__cache(dso, machine);
+
+	if (!c)
+		return NULL;
+
+	return auxtrace_cache__lookup(dso->auxtrace_cache, offset);
+}
+#endif
+
+static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
+					    struct cs_etm_packet *packet)
+{
+	int ret = 0;
+	struct cs_etm_auxtrace *etm = etmq->etm;
+	union perf_event *event = etmq->event_buf;
+	struct perf_sample sample = {.ip = 0,};
+	uint64_t start_addr = packet->start_addr;
+	uint64_t end_addr = packet->end_addr;
+
+	event->sample.header.type = PERF_RECORD_SAMPLE;
+	event->sample.header.misc = PERF_RECORD_MISC_USER;
+	event->sample.header.size = sizeof(struct perf_event_header);
+
+
+	sample.ip = start_addr;
+	sample.pid = etmq->pid;
+	sample.tid = etmq->tid;
+	sample.addr = end_addr;
+	sample.id = etmq->etm->instructions_id;
+	sample.stream_id = etmq->etm->instructions_id;
+	sample.period = (end_addr - start_addr) >> 2;
+	sample.cpu = packet->cpu;
+	sample.flags = 0; // etmq->flags;
+	sample.insn_len = 1; // etmq->insn_len;
+	sample.cpumode = event->header.misc;
+
+	//etmq->last_insn_cnt = etmq->state->tot_insn_cnt;
+
+#if 0
+	{
+		struct   addr_location al;
+		uint64_t offset;
+		struct   thread *thread;
+		struct   machine *machine = etmq->etm->machine;
+		uint8_t  cpumode;
+		struct   cs_etm_cache_entry *e;
+		uint8_t  buf[256];
+		size_t   bufsz;
+
+		thread = etmq->thread;
+
+		if (!thread)
+			thread = etmq->etm->unknown_thread;
+
+		if (start_addr > 0xffffffc000000000UL)
+			cpumode = PERF_RECORD_MISC_KERNEL;
+		else
+			cpumode = PERF_RECORD_MISC_USER;
+
+		thread__find_addr_map(thread, cpumode, MAP__FUNCTION, start_addr, &al);
+		if (!al.map || !al.map->dso)
+			goto endTest;
+		if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR &&
+		    dso__data_status_seen(al.map->dso, DSO_DATA_STATUS_SEEN_ITRACE))
+			goto endTest;
+
+		offset = al.map->map_ip(al.map, start_addr);
+
+
+		e = cs_etm__cache_lookup(al.map->dso, machine, offset);
+
+		if (e)
+			(void) e;
+		else {
+			int len;
+			map__load(al.map, machine->symbol_filter);
+
+			bufsz = sizeof(buf);
+			len = dso__data_read_offset(al.map->dso, machine,
+						    offset, buf, bufsz);
+
+			if (len <= 0)
+				goto endTest;
+
+			cs_etm__cache_add(al.map->dso, machine, offset, (end_addr - start_addr) >> 2, end_addr - start_addr);
+
+		}
+endTest:
+		(void) offset;
+	}
+#endif
+
+	ret = perf_session__deliver_synth_event(etm->session, event, &sample);
+
+	if (ret)
+		pr_err("CS ETM Trace: failed to deliver instruction event, error %d\n", ret);
+
+	return ret;
+}
+
+struct cs_etm_synth {
+	struct perf_tool dummy_tool;
+	struct perf_session *session;
+};
+
+
+static int cs_etm__event_synth(struct perf_tool *tool,
+			      union perf_event *event,
+			      struct perf_sample *sample,
+			      struct machine *machine)
+{
+	struct cs_etm_synth *cs_etm_synth =
+		      container_of(tool, struct cs_etm_synth, dummy_tool);
+
+	(void) sample;
+	(void) machine;
+
+	return perf_session__deliver_synth_event(cs_etm_synth->session, event, NULL);
+
+}
+
+
+static int cs_etm__synth_event(struct perf_session *session,
+			      struct perf_event_attr *attr, u64 id)
+{
+	struct cs_etm_synth cs_etm_synth;
+
+	memset(&cs_etm_synth, 0, sizeof(struct cs_etm_synth));
+	cs_etm_synth.session = session;
+
+	return perf_event__synthesize_attr(&cs_etm_synth.dummy_tool, attr, 1,
+					   &id, cs_etm__event_synth);
+}
+
+static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
+				struct perf_session *session)
+{
+	struct perf_evlist *evlist = session->evlist;
+	struct perf_evsel *evsel;
+	struct perf_event_attr attr;
+	bool found = false;
+	u64 id;
+	int err;
+
+	evlist__for_each_entry(evlist, evsel) {
+
+		if (evsel->attr.type == etm->pmu_type) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		pr_debug("There are no selected events with Core Sight Trace data\n");
+		return 0;
+	}
+
+	memset(&attr, 0, sizeof(struct perf_event_attr));
+	attr.size = sizeof(struct perf_event_attr);
+	attr.type = PERF_TYPE_HARDWARE;
+	attr.sample_type = evsel->attr.sample_type & PERF_SAMPLE_MASK;
+	attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
+			    PERF_SAMPLE_PERIOD;
+	if (etm->timeless_decoding)
+		attr.sample_type &= ~(u64)PERF_SAMPLE_TIME;
+	else
+		attr.sample_type |= PERF_SAMPLE_TIME;
+
+	attr.exclude_user = evsel->attr.exclude_user;
+	attr.exclude_kernel = evsel->attr.exclude_kernel;
+	attr.exclude_hv = evsel->attr.exclude_hv;
+	attr.exclude_host = evsel->attr.exclude_host;
+	attr.exclude_guest = evsel->attr.exclude_guest;
+	attr.sample_id_all = evsel->attr.sample_id_all;
+	attr.read_format = evsel->attr.read_format;
+
+	id = evsel->id[0] + 1000000000;
+
+	if (!id)
+		id = 1;
+
+	if (etm->synth_opts.instructions) {
+		attr.config = PERF_COUNT_HW_INSTRUCTIONS;
+		attr.sample_period = etm->synth_opts.period;
+		etm->instructions_sample_period = attr.sample_period;
+		err = cs_etm__synth_event(session, &attr, id);
+
+		if (err) {
+			pr_err("%s: failed to synthesize 'instructions' event type\n",
+			       __func__);
+			return err;
+		}
+		etm->sample_instructions = true;
+		etm->instructions_sample_type = attr.sample_type;
+		etm->instructions_id = id;
+		id += 1;
+	}
+
+	etm->synth_needs_swap = evsel->needs_swap;
+	return 0;
+}
+
+static int cs_etm__sample(struct cs_etm_queue *etmq)
+{
+	//const struct cs_etm_state *state = etmq->state;
+	struct cs_etm_packet packet;
+	//struct cs_etm_auxtrace *etm = etmq->etm;
+	int err;
+
+	err = cs_etm_decoder__get_packet(etmq->decoder, &packet);
+	// if there is no sample, it returns err = -1, no real error
+
+	if (!err && packet.sample_type & CS_ETM_RANGE) {
+		err = cs_etm__synth_instruction_sample(etmq, &packet);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int cs_etm__run_decoder(struct cs_etm_queue *etmq, u64 *timestamp)
+{
+	struct cs_etm_buffer buffer;
+	size_t buffer_used;
+	int err = 0;
+
+	/* Go through each buffer in the queue and decode them one by one */
+more:
+	buffer_used = 0;
+	memset(&buffer, 0, sizeof(buffer));
+	err = cs_etm__get_trace(&buffer, etmq);
+	if (err <= 0)
+		return err;
+
+	do {
+		size_t processed = 0;
+		etmq->state = cs_etm_decoder__process_data_block(etmq->decoder,
+					etmq->offset,
+					&buffer.buf[buffer_used],
+					buffer.len-buffer_used,
+					&processed);
+		err = etmq->state->err;
+		etmq->offset += processed;
+		buffer_used += processed;
+		if (!err)
+			cs_etm__sample(etmq);
+	} while (!etmq->eot && (buffer.len > buffer_used));
+goto more;
+
+	(void) timestamp;
+
+	return err;
+}
+
+static int cs_etm__update_queues(struct cs_etm_auxtrace *etm)
+{
+  if (etm->queues.new_data) {
+	etm->queues.new_data = false;
+	return cs_etm__setup_queues(etm);
+  }
+  return 0;
+}
+
+static int cs_etm__process_queues(struct cs_etm_auxtrace *etm, u64 timestamp)
+{
+	unsigned int queue_nr;
+	u64 ts;
+	int ret;
+
+	while (1) {
+		struct auxtrace_queue *queue;
+		struct cs_etm_queue *etmq;
+
+		if (!etm->heap.heap_cnt)
+			return 0;
+
+		if (etm->heap.heap_array[0].ordinal >= timestamp)
+			return 0;
+
+		queue_nr = etm->heap.heap_array[0].queue_nr;
+		queue = &etm->queues.queue_array[queue_nr];
+		etmq = queue->priv;
+
+		//cs_etm__log("queue %u processing 0x%" PRIx64 " to 0x%" PRIx64 "\n",
+			   //queue_nr, etm->heap.heap_array[0].ordinal,
+			   //timestamp);
+
+		auxtrace_heap__pop(&etm->heap);
+
+		if (etm->heap.heap_cnt) {
+			ts = etm->heap.heap_array[0].ordinal + 1;
+			if (ts > timestamp)
+				ts = timestamp;
+		} else {
+			ts = timestamp;
+		}
+
+		cs_etm__set_pid_tid_cpu(etm, queue);
+
+		ret = cs_etm__run_decoder(etmq, &ts);
+
+		if (ret < 0) {
+			auxtrace_heap__add(&etm->heap, queue_nr, ts);
+			return ret;
+		}
+
+		if (!ret) {
+			ret = auxtrace_heap__add(&etm->heap, queue_nr, ts);
+			if (ret < 0)
+				return ret;
+		} else {
+			etmq->on_heap = false;
+		}
+	}
+	return 0;
+}
+
+static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
+					  pid_t tid,
+					  u64 time_)
+{
+	struct auxtrace_queues *queues = &etm->queues;
+	unsigned int i;
+	u64 ts = 0;
+
+	for (i = 0; i < queues->nr_queues; ++i) {
+		struct auxtrace_queue *queue = &(etm->queues.queue_array[i]);
+		struct cs_etm_queue *etmq = queue->priv;
+
+		if (etmq && ((tid == -1) || (etmq->tid == tid))) {
+			etmq->time = time_;
+			cs_etm__set_pid_tid_cpu(etm, queue);
+			cs_etm__run_decoder(etmq, &ts);
+
+		}
+	}
+	return 0;
+}
+
+static struct cs_etm_queue *cs_etm__cpu_to_etmq(struct cs_etm_auxtrace *etm,
+						int cpu)
+{
+	unsigned q, j;
+
+	if (etm->queues.nr_queues == 0)
+		return NULL;
+
+	if (cpu < 0)
+		q = 0;
+	else if ((unsigned) cpu >= etm->queues.nr_queues)
+		q = etm->queues.nr_queues - 1;
+	else
+		q = cpu;
+
+	if (etm->queues.queue_array[q].cpu == cpu)
+		return etm->queues.queue_array[q].priv;
+
+	for (j = 0; q > 0; j++) {
+		if (etm->queues.queue_array[--q].cpu == cpu)
+			return etm->queues.queue_array[q].priv;
+	}
+
+	for (; j < etm->queues.nr_queues; j++) {
+		if (etm->queues.queue_array[j].cpu == cpu)
+			return etm->queues.queue_array[j].priv;
+
+	}
+
+	return NULL;
+}
+
+static uint32_t cs_etm__mem_access(struct cs_etm_queue *etmq, uint64_t address, size_t size, uint8_t *buffer)
+{
+	struct   addr_location al;
+	uint64_t offset;
+	struct   thread *thread;
+	struct   machine *machine;
+	uint8_t  cpumode;
+	int len;
+
+	if (etmq == NULL)
+		return -1;
+
+	machine = etmq->etm->machine;
+	thread = etmq->thread;
+	if (address > 0xffffffc000000000UL)
+		cpumode = PERF_RECORD_MISC_KERNEL;
+	else
+		cpumode = PERF_RECORD_MISC_USER;
+
+	thread__find_addr_map(thread, cpumode, MAP__FUNCTION, address, &al);
+
+	if (!al.map || !al.map->dso)
+		return 0;
+
+	if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR &&
+	    dso__data_status_seen(al.map->dso, DSO_DATA_STATUS_SEEN_ITRACE))
+		return 0;
+
+	offset = al.map->map_ip(al.map, address);
+
+	map__load(al.map);
+
+	len = dso__data_read_offset(al.map->dso, machine,
+				    offset, buffer, size);
+
+	if (len <= 0)
+		return 0;
+
+	return len;
+}
+
+static bool check_need_swap(int file_endian)
+{
+	const int data = 1;
+	u8 *check = (u8 *)&data;
+	int host_endian;
+
+	if (check[0] == 1)
+		host_endian = ELFDATA2LSB;
+	else
+		host_endian = ELFDATA2MSB;
+
+	return host_endian != file_endian;
+}
+
+static int cs_etm__read_elf_info(const char *fname, uint64_t *foffset, uint64_t *fstart, uint64_t *fsize)
+{
+	FILE *fp;
+	u8 e_ident[EI_NIDENT];
+	int ret = -1;
+	bool need_swap = false;
+	size_t buf_size;
+	void *buf;
+	int i;
+
+	fp = fopen(fname, "r");
+	if (fp == NULL)
+		return -1;
+
+	if (fread(e_ident, sizeof(e_ident), 1, fp) != 1)
+		goto out;
+
+	if (memcmp(e_ident, ELFMAG, SELFMAG) ||
+	    e_ident[EI_VERSION] != EV_CURRENT)
+		goto out;
+
+	need_swap = check_need_swap(e_ident[EI_DATA]);
+
+	/* for simplicity */
+	fseek(fp, 0, SEEK_SET);
+
+	if (e_ident[EI_CLASS] == ELFCLASS32) {
+		Elf32_Ehdr ehdr;
+		Elf32_Phdr *phdr;
+
+		if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+			goto out;
+
+		if (need_swap) {
+			ehdr.e_phoff = bswap_32(ehdr.e_phoff);
+			ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+			ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+		}
+
+		buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+		buf = malloc(buf_size);
+		if (buf == NULL)
+			goto out;
+
+		fseek(fp, ehdr.e_phoff, SEEK_SET);
+		if (fread(buf, buf_size, 1, fp) != 1)
+			goto out_free;
+
+		for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+
+			if (need_swap) {
+				phdr->p_type = bswap_32(phdr->p_type);
+				phdr->p_offset = bswap_32(phdr->p_offset);
+				phdr->p_filesz = bswap_32(phdr->p_filesz);
+			}
+
+			if (phdr->p_type != PT_LOAD)
+				continue;
+
+			*foffset = phdr->p_offset;
+			*fstart = phdr->p_vaddr;
+			*fsize = phdr->p_filesz;
+			ret = 0;
+			break;
+		}
+	} else {
+		Elf64_Ehdr ehdr;
+		Elf64_Phdr *phdr;
+
+		if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+			goto out;
+
+		if (need_swap) {
+			ehdr.e_phoff = bswap_64(ehdr.e_phoff);
+			ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+			ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+		}
+
+		buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+		buf = malloc(buf_size);
+		if (buf == NULL)
+			goto out;
+
+		fseek(fp, ehdr.e_phoff, SEEK_SET);
+		if (fread(buf, buf_size, 1, fp) != 1)
+			goto out_free;
+
+		for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+
+			if (need_swap) {
+				phdr->p_type = bswap_32(phdr->p_type);
+				phdr->p_offset = bswap_64(phdr->p_offset);
+				phdr->p_filesz = bswap_64(phdr->p_filesz);
+			}
+
+			if (phdr->p_type != PT_LOAD)
+				continue;
+
+			*foffset = phdr->p_offset;
+			*fstart = phdr->p_vaddr;
+			*fsize = phdr->p_filesz;
+			ret = 0;
+			break;
+		}
+	}
+out_free:
+	free(buf);
+out:
+	fclose(fp);
+	return ret;
+}
+
+static int cs_etm__process_event(struct perf_session *session,
+				union perf_event *event,
+				struct perf_sample *sample,
+				struct perf_tool *tool)
+{
+	struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
+						   struct cs_etm_auxtrace,
+						   auxtrace);
+
+	u64 timestamp;
+	int err = 0;
+
+	if (dump_trace)
+		return 0;
+
+	if (!tool->ordered_events) {
+		pr_err("CoreSight ETM Trace requires ordered events\n");
+		return -EINVAL;
+	}
+
+	if (sample->time && (sample->time != (u64)-1))
+		timestamp = sample->time;
+	else
+		timestamp = 0;
+
+	if (timestamp || etm->timeless_decoding) {
+		err = cs_etm__update_queues(etm);
+		if (err)
+			return err;
+
+	}
+
+	if (event->header.type == PERF_RECORD_MMAP2) {
+		struct dso *dso;
+		int cpu;
+		struct cs_etm_queue *etmq;
+
+		cpu = sample->cpu;
+
+		etmq = cs_etm__cpu_to_etmq(etm, cpu);
+
+		if (!etmq)
+			return -1;
+
+		dso = dsos__find(&(etm->machine->dsos), event->mmap2.filename, false);
+		if (NULL != dso) {
+			err = cs_etm_decoder__add_mem_access_cb(
+			    etmq->decoder,
+			    event->mmap2.start,
+			    event->mmap2.len,
+			    cs_etm__mem_access);
+		}
+
+		if ((symbol_conf.vmlinux_name != NULL) && (!etmq->kernel_mapped)) {
+			uint64_t foffset;
+			uint64_t fstart;
+			uint64_t fsize;
+
+			err = cs_etm__read_elf_info(symbol_conf.vmlinux_name,
+						      &foffset, &fstart, &fsize);
+
+			if (!err) {
+				cs_etm_decoder__add_bin_file(
+					etmq->decoder,
+					foffset,
+					fstart,
+					fsize & ~0xULL,
+					symbol_conf.vmlinux_name);
+
+				etmq->kernel_mapped = true;
+			}
+		}
+
+	}
+
+	if (etm->timeless_decoding) {
+		if (event->header.type == PERF_RECORD_EXIT) {
+			err = cs_etm__process_timeless_queues(etm,
+							     event->fork.tid,
+							     sample->time);
+		}
+	} else if (timestamp) {
+		err = cs_etm__process_queues(etm, timestamp);
+	}
+
+	//cs_etm__log("event %s (%u): cpu %d time%"PRIu64" tsc %#"PRIx64"\n",
+		   //perf_event__name(event->header.type), event->header.type,
+		   //sample->cpu, sample->time, timestamp);
+	return err;
+}
+
+static int cs_etm__process_auxtrace_event(struct perf_session *session,
+				  union perf_event *event,
+				  struct perf_tool *tool)
+{
+	struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
+						   struct cs_etm_auxtrace,
+						   auxtrace);
+
+	(void) tool;
+
+	if (!etm->data_queued) {
+		struct auxtrace_buffer *buffer;
+		off_t  data_offset;
+		int fd = perf_data_file__fd(session->file);
+		bool is_pipe = perf_data_file__is_pipe(session->file);
+		int err;
+
+		if (is_pipe)
+			data_offset = 0;
+		else {
+			data_offset = lseek(fd, 0, SEEK_CUR);
+			if (data_offset == -1)
+				return -errno;
+		}
+
+		err = auxtrace_queues__add_event(&etm->queues,
+						 session,
+						 event,
+						 data_offset,
+						 &buffer);
+		if (err)
+			return err;
+
+		if (dump_trace)
+			if (auxtrace_buffer__get_data(buffer, fd)) {
+				cs_etm__dump_event(etm, buffer);
+				auxtrace_buffer__put_data(buffer);
+			}
+	}
+
+	return 0;
+
+}
+
+static const char * const cs_etm_global_header_fmts[] = {
+  [CS_HEADER_VERSION_0]	= "   Header version		%"PRIx64"\n",
+  [CS_PMU_TYPE_CPUS]	= "   PMU type/num cpus		%"PRIx64"\n",
+  [CS_ETM_SNAPSHOT]	= "   Snapshot			%"PRIx64"\n",
+};
+
+static const char * const cs_etm_priv_fmts[] = {
+  [CS_ETM_MAGIC]	= "   Magic number		%"PRIx64"\n",
+  [CS_ETM_CPU]		= "   CPU			%"PRIx64"\n",
+  [CS_ETM_ETMCR]	= "   ETMCR			%"PRIx64"\n",
+  [CS_ETM_ETMTRACEIDR]	= "   ETMTRACEIDR		%"PRIx64"\n",
+  [CS_ETM_ETMCCER]	= "   ETMCCER			%"PRIx64"\n",
+  [CS_ETM_ETMIDR]	= "   ETMIDR			%"PRIx64"\n",
+};
+
+static const char * const cs_etmv4_priv_fmts[] = {
+  [CS_ETM_MAGIC]		= "   Magic number		%"PRIx64"\n",
+  [CS_ETM_CPU]			= "   CPU			%"PRIx64"\n",
+  [CS_ETMV4_TRCCONFIGR]		= "   TRCCONFIGR		%"PRIx64"\n",
+  [CS_ETMV4_TRCTRACEIDR]	= "   TRCTRACEIDR		%"PRIx64"\n",
+  [CS_ETMV4_TRCIDR0]		= "   TRCIDR0			%"PRIx64"\n",
+  [CS_ETMV4_TRCIDR1]		= "   TRCIDR1			%"PRIx64"\n",
+  [CS_ETMV4_TRCIDR2]		= "   TRCIDR2			%"PRIx64"\n",
+  [CS_ETMV4_TRCIDR8]		= "   TRCIDR8			%"PRIx64"\n",
+  [CS_ETMV4_TRCAUTHSTATUS]	= "   TRCAUTHSTATUS		%"PRIx64"\n",
+};
+
+static void cs_etm__print_auxtrace_info(u64 *val, size_t num)
+{
+	unsigned i, j, cpu;
+
+	for (i = 0, cpu = 0; cpu < num; ++cpu)
+		if (val[i] == __perf_cs_etmv3_magic)
+			for (j = 0; j < CS_ETM_PRIV_MAX; ++j, ++i)
+				fprintf(stdout, cs_etm_priv_fmts[j], val[i]);
+		else if (val[i] == __perf_cs_etmv4_magic)
+			for (j = 0; j < CS_ETMV4_PRIV_MAX; ++j, ++i)
+				fprintf(stdout, cs_etmv4_priv_fmts[j], val[i]);
+		else
+			// failure.. return
+			return;
+}
+
+int cs_etm__process_auxtrace_info(union perf_event *event,
+				 struct perf_session *session)
+{
+	struct auxtrace_info_event *auxtrace_info = &(event->auxtrace_info);
+	size_t event_header_size = sizeof(struct perf_event_header);
+	size_t info_header_size = 8;
+	size_t total_size = auxtrace_info->header.size;
+	size_t priv_size = 0;
+	size_t num_cpu;
+	struct cs_etm_auxtrace *etm = 0;
+	int err = 0, idx = -1;
+	u64 *ptr;
+	u64 *hdr = NULL;
+	u64 **metadata = NULL;
+	size_t i, j, k;
+	unsigned pmu_type;
+	struct int_node *inode;
+
+	/*
+	 * sizeof(auxtrace_info_event::type) +
+	 * sizeof(auxtrace_info_event::reserved) == 8
+	 */
+	info_header_size = 8;
+
+	if (total_size < (event_header_size + info_header_size))
+		return -EINVAL;
+
+	priv_size = total_size - event_header_size - info_header_size;
+
+	// First the global part
+
+	ptr = (u64 *) auxtrace_info->priv;
+	if (ptr[0] == 0) {
+		hdr = zalloc(sizeof(u64 *) * CS_HEADER_VERSION_0_MAX);
+		if (hdr == NULL)
+			return -EINVAL;
+		for (i = 0; i < CS_HEADER_VERSION_0_MAX; ++i)
+			hdr[i] = ptr[i];
+		num_cpu = hdr[CS_PMU_TYPE_CPUS] & 0xffffffff;
+		pmu_type = (unsigned) ((hdr[CS_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+	} else
+		return -EINVAL;
+
+	/*
+	 * Create an RB tree for traceID-CPU# tuple.  Since the conversion has
+	 * to be made for each packet that gets decoded optimizing access in
+	 * anything other than a sequential array is worth doing.
+	 */
+	traceid_list = intlist__new(NULL);
+	if (!traceid_list)
+		return -ENOMEM;
+
+	metadata = zalloc(sizeof(u64 *) * num_cpu);
+	if (!metadata) {
+		err = -ENOMEM;
+		goto err_free_traceid_list;
+	}
+
+	if (metadata == NULL)
+		return -EINVAL;
+
+	for (j = 0; j < num_cpu; ++j) {
+		if (ptr[i] == __perf_cs_etmv3_magic) {
+			metadata[j] = zalloc(sizeof(u64)*CS_ETM_PRIV_MAX);
+			if (metadata == NULL)
+				return -EINVAL;
+			for (k = 0; k < CS_ETM_PRIV_MAX; k++)
+				metadata[j][k] = ptr[i+k];
+
+			/* The traceID is our handle */
+			idx = metadata[j][CS_ETM_ETMIDR];
+			i += CS_ETM_PRIV_MAX;
+		} else if (ptr[i] == __perf_cs_etmv4_magic) {
+			metadata[j] = zalloc(sizeof(u64)*CS_ETMV4_PRIV_MAX);
+			if (metadata == NULL)
+				return -EINVAL;
+			for (k = 0; k < CS_ETMV4_PRIV_MAX; k++)
+				metadata[j][k] = ptr[i+k];
+
+			/* The traceID is our handle */
+			idx = metadata[j][CS_ETMV4_TRCTRACEIDR];
+			i += CS_ETMV4_PRIV_MAX;
+		}
+
+		/* Get an RB node for this CPU */
+		inode = intlist__findnew(traceid_list, idx);
+
+		/* Something went wrong, no need to continue */
+		if (!inode) {
+			err = PTR_ERR(inode);
+			goto err_free_metadata;
+		}
+
+		/*
+		 * The node for that CPU should not have been taken already.
+		 * Backout if that's the case.
+		 */
+		if (inode->priv) {
+			err = -EINVAL;
+			goto err_free_metadata;
+		}
+
+		/* All good, associate the traceID with the CPU# */
+		inode->priv = &metadata[j][CS_ETM_CPU];
+
+	}
+
+	if (i*8 != priv_size)
+		return -EINVAL;
+
+	if (dump_trace)
+		cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu);
+
+	etm = zalloc(sizeof(struct cs_etm_auxtrace));
+
+	etm->num_cpu = num_cpu;
+	etm->pmu_type = pmu_type;
+	etm->snapshot_mode = (hdr[CS_ETM_SNAPSHOT] != 0);
+
+	if (!etm)
+		return -ENOMEM;
+
+
+	err = auxtrace_queues__init(&etm->queues);
+	if (err)
+		goto err_free;
+
+	etm->unknown_thread = thread__new(999999999, 999999999);
+	if (etm->unknown_thread == NULL) {
+		err = -ENOMEM;
+		goto err_free_queues;
+	}
+	err = thread__set_comm(etm->unknown_thread, "unknown", 0);
+	if (err)
+		goto err_delete_thread;
+
+	if (thread__init_map_groups(etm->unknown_thread,
+				    etm->machine)) {
+		err = -ENOMEM;
+		goto err_delete_thread;
+	}
+
+	etm->timeless_decoding = true;
+	etm->sampling_mode = false;
+	etm->metadata = metadata;
+	etm->session = session;
+	etm->machine = &session->machines.host;
+	etm->auxtrace_type = auxtrace_info->type;
+
+	etm->auxtrace.process_event = cs_etm__process_event;
+	etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event;
+	etm->auxtrace.flush_events = cs_etm__flush_events;
+	etm->auxtrace.free_events  = cs_etm__free_events;
+	etm->auxtrace.free	 = cs_etm__free;
+	session->auxtrace = &(etm->auxtrace);
+
+	if (dump_trace)
+		return 0;
+
+	if (session->itrace_synth_opts && session->itrace_synth_opts->set)
+		etm->synth_opts = *session->itrace_synth_opts;
+	else
+		itrace_synth_opts__set_default(&etm->synth_opts);
+	etm->synth_opts.branches = false;
+	etm->synth_opts.callchain = false;
+	etm->synth_opts.calls = false;
+	etm->synth_opts.returns = false;
+
+	err = cs_etm__synth_events(etm, session);
+	if (err)
+		goto err_delete_thread;
+
+	err = auxtrace_queues__process_index(&etm->queues, session);
+	if (err)
+		goto err_delete_thread;
+
+	etm->data_queued = etm->queues.populated;
+
+	return 0;
+
+err_delete_thread:
+	thread__delete(etm->unknown_thread);
+err_free_queues:
+	auxtrace_queues__free(&etm->queues);
+	session->auxtrace = NULL;
+err_free:
+	free(etm);
+err_free_metadata:
+	/* No need to check @metadata[j], free(NULL) is supported */
+	for (j = 0; j < num_cpu; ++j)
+		free(metadata[j]);
+	free(metadata);
+err_free_traceid_list:
+	intlist__delete(traceid_list);
+
+	return err;
+}
diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
index 3cc6bc3..32400ac 100644
--- a/tools/perf/util/cs-etm.h
+++ b/tools/perf/util/cs-etm.h
@@ -18,6 +18,10 @@
 #ifndef INCLUDE__UTIL_PERF_CS_ETM_H__
 #define INCLUDE__UTIL_PERF_CS_ETM_H__
 
+#include "util/event.h"
+#include "util/intlist.h"
+#include "util/session.h"
+
 /* Versionning header in case things need tro change in the future.  That way
  * decoding of old snapshot is still possible.
  */
@@ -61,6 +65,9 @@ enum {
 	CS_ETMV4_PRIV_MAX,
 };
 
+/* RB tree for quick conversion between traceID and CPUs */
+struct intlist *traceid_list;
+
 #define KiB(x) ((x) * 1024)
 #define MiB(x) ((x) * 1024 * 1024)
 
@@ -71,4 +78,7 @@ static const u64 __perf_cs_etmv4_magic   = 0x4040404040404040ULL;
 #define CS_ETMV3_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
 #define CS_ETMV4_PRIV_SIZE (CS_ETMV4_PRIV_MAX * sizeof(u64))
 
+int cs_etm__process_auxtrace_info(union perf_event *event,
+				  struct perf_session *session);
+
 #endif
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index df85b9e..bcec333 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1,3 +1,4 @@
+#include "build-id.h"
 #include "callchain.h"
 #include "debug.h"
 #include "event.h"
@@ -483,7 +484,8 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event
 }
 
 int machine__process_lost_event(struct machine *machine __maybe_unused,
-				union perf_event *event, struct perf_sample *sample __maybe_unused)
+				union perf_event *event,
+				struct perf_sample *sample __maybe_unused)
 {
 	dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
 		    event->lost.id, event->lost.lost);
@@ -491,7 +493,8 @@ int machine__process_lost_event(struct machine *machine __maybe_unused,
 }
 
 int machine__process_lost_samples_event(struct machine *machine __maybe_unused,
-					union perf_event *event, struct perf_sample *sample)
+					union perf_event *event,
+					struct perf_sample *sample)
 {
 	dump_printf(": id:%" PRIu64 ": lost samples :%" PRIu64 "\n",
 		    sample->id, event->lost_samples.lost);
@@ -711,8 +714,16 @@ static struct dso *machine__get_kernel(struct machine *machine)
 						 DSO_TYPE_GUEST_KERNEL);
 	}
 
-	if (kernel != NULL && (!kernel->has_build_id))
-		dso__read_running_kernel_build_id(kernel, machine);
+	if (kernel != NULL && (!kernel->has_build_id)) {
+		if (symbol_conf.vmlinux_name != NULL) {
+			filename__read_build_id(symbol_conf.vmlinux_name,
+						kernel->build_id,
+						sizeof(kernel->build_id));
+			kernel->has_build_id = 1;
+		} else {
+			dso__read_running_kernel_build_id(kernel, machine);
+		}
+	}
 
 	return kernel;
 }
@@ -726,8 +737,19 @@ static void machine__get_kallsyms_filename(struct machine *machine, char *buf,
 {
 	if (machine__is_default_guest(machine))
 		scnprintf(buf, bufsz, "%s", symbol_conf.default_guest_kallsyms);
-	else
-		scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir);
+	else {
+		if (symbol_conf.vmlinux_name != 0) {
+			unsigned char build_id[BUILD_ID_SIZE];
+			char build_id_hex[SBUILD_ID_SIZE];
+			filename__read_build_id(symbol_conf.vmlinux_name,
+						build_id,
+						sizeof(build_id));
+			build_id__sprintf(build_id, sizeof(build_id), build_id_hex);
+			build_id_cache__linkname((char *)build_id_hex, buf, bufsz);
+		} else {
+			scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir);
+		}
+	}
 }
 
 const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
@@ -736,7 +758,7 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
  * Returns the name of the start symbol in *symbol_name. Pass in NULL as
  * symbol_name if it's not that important.
  */
-static u64 machine__get_running_kernel_start(struct machine *machine,
+static u64 machine__get_kallsyms_kernel_start(struct machine *machine,
 					     const char **symbol_name)
 {
 	char filename[PATH_MAX];
@@ -764,7 +786,7 @@ static u64 machine__get_running_kernel_start(struct machine *machine,
 int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
 {
 	enum map_type type;
-	u64 start = machine__get_running_kernel_start(machine, NULL);
+	u64 start = machine__get_kallsyms_kernel_start(machine, NULL);
 
 	/* In case of renewal the kernel map, destroy previous one */
 	machine__destroy_kernel_maps(machine);
@@ -1126,10 +1148,10 @@ int machine__create_kernel_maps(struct machine *machine)
 {
 	struct dso *kernel = machine__get_kernel(machine);
 	const char *name;
-	u64 addr;
+	u64 addr = machine__get_kallsyms_kernel_start(machine, &name);
 	int ret;
 
-	if (kernel == NULL)
+	if (!addr || kernel == NULL)
 		return -1;
 
 	ret = __machine__create_kernel_maps(machine, kernel);
@@ -1151,7 +1173,7 @@ int machine__create_kernel_maps(struct machine *machine)
 	 */
 	map_groups__fixup_end(&machine->kmaps);
 
-	addr = machine__get_running_kernel_start(machine, &name);
+	addr = machine__get_kallsyms_kernel_start(machine, &name);
 	if (!addr) {
 	} else if (maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, name, addr)) {
 		machine__destroy_kernel_maps(machine);
@@ -1901,7 +1923,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
 		ip = chain->ips[j];
 
 		if (ip < PERF_CONTEXT_MAX)
-                       ++nr_entries;
+		       ++nr_entries;
 
 		err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);
 
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index fdbbf04..7698291 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -833,6 +833,8 @@ static void python_process_general_event(struct perf_sample *sample,
 			PyInt_FromLong(sample->cpu));
 	pydict_set_item_string_decref(dict_sample, "ip",
 			PyLong_FromUnsignedLongLong(sample->ip));
+	pydict_set_item_string_decref(dict_sample, "addr",
+			PyLong_FromUnsignedLongLong(sample->addr));
 	pydict_set_item_string_decref(dict_sample, "time",
 			PyLong_FromUnsignedLongLong(sample->time));
 	pydict_set_item_string_decref(dict_sample, "period",
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index 11cdde9..c094091 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -342,9 +342,8 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
 	if (ret >= 0)
 		dso->is_64_bit = ret;
 
-	if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) {
+	if ((!dso->has_build_id) && (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0))
 		dso__set_build_id(dso, build_id);
-	}
 	return 0;
 }
 
-- 
2.7.4



More information about the linux-yocto mailing list