All of lore.kernel.org
 help / color / mirror / Atom feed
* [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf
@ 2020-02-18 22:56 Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 1/6] lib/intel_chipset: identify Elkhart Lake Lionel Landwerlin
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Lionel Landwerlin @ 2020-02-18 22:56 UTC (permalink / raw)
  To: igt-dev

A v2 with only minor changes suggested by Chris.

Cheers,

Lionel Landwerlin (6):
  lib/intel_chipset: identify Elkhart Lake
  lib: Add i915_perf library
  lib/i915-perf: Add support for loading perf configurations
  tools: add i915 perf recorder tool
  lib/i915-perf: add i915 perf data reader
  tools: add i915-perf-reader

 lib/i915-perf.pc.in                           |    11 +
 lib/i915/perf-configs/README.md               |   115 +
 lib/i915/perf-configs/codegen.py              |    33 +
 lib/i915/perf-configs/guids.xml               |   319 +
 lib/i915/perf-configs/mdapi-xml-convert.py    |  1006 +
 lib/i915/perf-configs/oa-bdw.xml              | 15653 ++++++++++++++++
 lib/i915/perf-configs/oa-bxt.xml              |  9595 ++++++++++
 lib/i915/perf-configs/oa-cflgt2.xml           | 10866 +++++++++++
 lib/i915/perf-configs/oa-cflgt3.xml           | 10933 +++++++++++
 lib/i915/perf-configs/oa-chv.xml              |  9757 ++++++++++
 lib/i915/perf-configs/oa-cnl.xml              | 10411 ++++++++++
 lib/i915/perf-configs/oa-glk.xml              |  9346 +++++++++
 lib/i915/perf-configs/oa-hsw.xml              |  4615 +++++
 lib/i915/perf-configs/oa-icl.xml              | 11869 ++++++++++++
 lib/i915/perf-configs/oa-kblgt2.xml           | 10866 +++++++++++
 lib/i915/perf-configs/oa-kblgt3.xml           | 10933 +++++++++++
 lib/i915/perf-configs/oa-lkf.xml              | 11803 ++++++++++++
 lib/i915/perf-configs/oa-sklgt2.xml           | 11895 ++++++++++++
 lib/i915/perf-configs/oa-sklgt3.xml           | 10933 +++++++++++
 lib/i915/perf-configs/oa-sklgt4.xml           | 10956 +++++++++++
 lib/i915/perf-configs/oa-tgl.xml              |  8491 +++++++++
 lib/i915/perf-configs/oa_guid_registry.py     |    73 +
 lib/i915/perf-configs/perf-codegen.py         |   850 +
 lib/i915/perf-configs/update-guids.py         |   231 +
 lib/i915/perf.c                               |   584 +
 lib/i915/perf.h                               |   240 +
 lib/i915/perf_data.h                          |   118 +
 lib/i915/perf_data_reader.c                   |   383 +
 lib/i915/perf_data_reader.h                   |   105 +
 lib/intel_chipset.h                           |     1 +
 lib/intel_device_info.c                       |     8 +-
 lib/meson.build                               |    68 +
 lib/tests/i915_perf_data_alignment.c          |    40 +
 lib/tests/meson.build                         |     1 +
 tools/i915-perf/i915_perf_configs.c           |   277 +
 tools/i915-perf/i915_perf_control.c           |   133 +
 tools/i915-perf/i915_perf_reader.c            |   279 +
 tools/i915-perf/i915_perf_recorder.c          |  1052 ++
 tools/i915-perf/i915_perf_recorder_commands.h |    39 +
 tools/i915-perf/meson.build                   |    22 +
 tools/meson.build                             |     1 +
 41 files changed, 174910 insertions(+), 1 deletion(-)
 create mode 100644 lib/i915-perf.pc.in
 create mode 100644 lib/i915/perf-configs/README.md
 create mode 100644 lib/i915/perf-configs/codegen.py
 create mode 100644 lib/i915/perf-configs/guids.xml
 create mode 100755 lib/i915/perf-configs/mdapi-xml-convert.py
 create mode 100644 lib/i915/perf-configs/oa-bdw.xml
 create mode 100644 lib/i915/perf-configs/oa-bxt.xml
 create mode 100644 lib/i915/perf-configs/oa-cflgt2.xml
 create mode 100644 lib/i915/perf-configs/oa-cflgt3.xml
 create mode 100644 lib/i915/perf-configs/oa-chv.xml
 create mode 100644 lib/i915/perf-configs/oa-cnl.xml
 create mode 100644 lib/i915/perf-configs/oa-glk.xml
 create mode 100644 lib/i915/perf-configs/oa-hsw.xml
 create mode 100644 lib/i915/perf-configs/oa-icl.xml
 create mode 100644 lib/i915/perf-configs/oa-kblgt2.xml
 create mode 100644 lib/i915/perf-configs/oa-kblgt3.xml
 create mode 100644 lib/i915/perf-configs/oa-lkf.xml
 create mode 100644 lib/i915/perf-configs/oa-sklgt2.xml
 create mode 100644 lib/i915/perf-configs/oa-sklgt3.xml
 create mode 100644 lib/i915/perf-configs/oa-sklgt4.xml
 create mode 100644 lib/i915/perf-configs/oa-tgl.xml
 create mode 100644 lib/i915/perf-configs/oa_guid_registry.py
 create mode 100755 lib/i915/perf-configs/perf-codegen.py
 create mode 100755 lib/i915/perf-configs/update-guids.py
 create mode 100644 lib/i915/perf.c
 create mode 100644 lib/i915/perf.h
 create mode 100644 lib/i915/perf_data.h
 create mode 100644 lib/i915/perf_data_reader.c
 create mode 100644 lib/i915/perf_data_reader.h
 create mode 100644 lib/tests/i915_perf_data_alignment.c
 create mode 100644 tools/i915-perf/i915_perf_configs.c
 create mode 100644 tools/i915-perf/i915_perf_control.c
 create mode 100644 tools/i915-perf/i915_perf_reader.c
 create mode 100644 tools/i915-perf/i915_perf_recorder.c
 create mode 100644 tools/i915-perf/i915_perf_recorder_commands.h
 create mode 100644 tools/i915-perf/meson.build

--
2.25.0
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [igt-dev] [PATCH i-g-t v2 1/6] lib/intel_chipset: identify Elkhart Lake
  2020-02-18 22:56 [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf Lionel Landwerlin
@ 2020-02-18 22:56 ` Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 3/6] lib/i915-perf: Add support for loading perf configurations Lionel Landwerlin
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Lionel Landwerlin @ 2020-02-18 22:56 UTC (permalink / raw)
  To: igt-dev

We'll need to identify this platform for performance tools.

v2: add codename (Chris)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/intel_chipset.h     | 1 +
 lib/intel_device_info.c | 8 +++++++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/lib/intel_chipset.h b/lib/intel_chipset.h
index 2bd57f4f..929fac53 100644
--- a/lib/intel_chipset.h
+++ b/lib/intel_chipset.h
@@ -71,6 +71,7 @@ struct intel_device_info {
 	bool is_cometlake : 1;
 	bool is_cannonlake : 1;
 	bool is_icelake : 1;
+	bool is_elkhartlake : 1;
 	bool is_tigerlake : 1;
 	const char *codename;
 };
diff --git a/lib/intel_device_info.c b/lib/intel_device_info.c
index 4f96577d..208965d1 100644
--- a/lib/intel_device_info.c
+++ b/lib/intel_device_info.c
@@ -309,6 +309,12 @@ static const struct intel_device_info intel_icelake_info = {
 	.codename = "icelake"
 };
 
+static const struct intel_device_info intel_elkhartlake_info = {
+	.gen = BIT(10),
+	.is_elkhartlake = true,
+	.codename = "elkhartlake"
+};
+
 static const struct intel_device_info intel_tigerlake_info = {
 	.gen = BIT(11),
 	.is_tigerlake = true,
@@ -394,7 +400,7 @@ static const struct pci_id_match intel_device_match[] = {
 
 	INTEL_ICL_11_IDS(&intel_icelake_info),
 
-	INTEL_EHL_IDS(&intel_icelake_info),
+	INTEL_EHL_IDS(&intel_elkhartlake_info),
 
 	INTEL_TGL_12_IDS(&intel_tigerlake_info),
 
-- 
2.25.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [igt-dev] [PATCH i-g-t v2 3/6] lib/i915-perf: Add support for loading perf configurations
  2020-02-18 22:56 [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 1/6] lib/intel_chipset: identify Elkhart Lake Lionel Landwerlin
@ 2020-02-18 22:56 ` Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 4/6] tools: add i915 perf recorder tool Lionel Landwerlin
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Lionel Landwerlin @ 2020-02-18 22:56 UTC (permalink / raw)
  To: igt-dev

Loads programming of various HW bits into i915.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915/perf.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/i915/perf.h |  2 ++
 2 files changed, 89 insertions(+)

diff --git a/lib/i915/perf.c b/lib/i915/perf.c
index 31888627..6c92fd02 100644
--- a/lib/i915/perf.c
+++ b/lib/i915/perf.c
@@ -422,3 +422,90 @@ intel_perf_add_metric_set(struct intel_perf *perf,
 {
 	igt_list_add_tail(&metric_set->link, &perf->metric_sets);
 }
+
+static void
+load_metric_set_config(struct intel_perf_metric_set *metric_set, int drm_fd)
+{
+	struct drm_i915_perf_oa_config config;
+	uint64_t config_id = 0;
+
+	memset(&config, 0, sizeof(config));
+
+	memcpy(config.uuid, metric_set->hw_config_guid, sizeof(config.uuid));
+
+	config.n_mux_regs = metric_set->n_mux_regs;
+	config.mux_regs_ptr = (uintptr_t) metric_set->mux_regs;
+
+	config.n_boolean_regs = metric_set->n_b_counter_regs;
+	config.boolean_regs_ptr = (uintptr_t) metric_set->b_counter_regs;
+
+	config.n_flex_regs = metric_set->n_flex_regs;
+	config.flex_regs_ptr = (uintptr_t) metric_set->flex_regs;
+
+	while (ioctl(drm_fd, DRM_IOCTL_I915_PERF_ADD_CONFIG, &config) < 0 &&
+	       (errno == EAGAIN || errno == EINTR));
+
+	metric_set->perf_oa_metrics_set = config_id;
+}
+
+void
+intel_perf_load_perf_configs(struct intel_perf *perf, int drm_fd)
+{
+	int sysfs_dir_fd = open_master_sysfs_dir(drm_fd);
+	struct dirent *entry;
+	int metrics_dir_fd;
+	DIR *metrics_dir;
+	struct intel_perf_metric_set *metric_set;
+
+	if (sysfs_dir_fd < 0)
+		return;
+
+	metrics_dir_fd = openat(sysfs_dir_fd, "metrics", O_DIRECTORY);
+	close(sysfs_dir_fd);
+	if (metrics_dir_fd < -1)
+		return;
+
+	metrics_dir = fdopendir(metrics_dir_fd);
+	if (!metrics_dir) {
+		close(metrics_dir_fd);
+		return;
+	}
+
+	while ((entry = readdir(metrics_dir))) {
+		bool metric_id_read;
+		uint64_t metric_id;
+		char path[256 + 4];
+		int id_fd;
+
+		if (entry->d_type != DT_DIR)
+			continue;
+
+		snprintf(path, sizeof(path), "%s/id", entry->d_name);
+
+		id_fd = openat(metrics_dir_fd, path, O_RDONLY);
+		if (id_fd < 0)
+			continue;
+
+		metric_id_read = read_fd_uint64(id_fd, &metric_id);
+		close(id_fd);
+
+		if (!metric_id_read)
+			continue;
+
+		igt_list_for_each_entry(metric_set, &perf->metric_sets, link) {
+			if (!strcmp(metric_set->hw_config_guid, entry->d_name)) {
+				metric_set->perf_oa_metrics_set = metric_id;
+				break;
+			}
+		}
+	}
+
+	closedir(metrics_dir);
+
+	igt_list_for_each_entry(metric_set, &perf->metric_sets, link) {
+		if (metric_set->perf_oa_metrics_set)
+			continue;
+
+		load_metric_set_config(metric_set, drm_fd);
+	}
+}
diff --git a/lib/i915/perf.h b/lib/i915/perf.h
index 7dfa0472..246d06cf 100644
--- a/lib/i915/perf.h
+++ b/lib/i915/perf.h
@@ -220,6 +220,8 @@ void intel_perf_add_logical_counter(struct intel_perf *perf,
 void intel_perf_add_metric_set(struct intel_perf *perf,
 			       struct intel_perf_metric_set *metric_set);
 
+void intel_perf_load_perf_configs(struct intel_perf *perf, int drm_fd);
+
 #ifdef __cplusplus
 };
 #endif
-- 
2.25.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [igt-dev] [PATCH i-g-t v2 4/6] tools: add i915 perf recorder tool
  2020-02-18 22:56 [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 1/6] lib/intel_chipset: identify Elkhart Lake Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 3/6] lib/i915-perf: Add support for loading perf configurations Lionel Landwerlin
@ 2020-02-18 22:56 ` Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 5/6] lib/i915-perf: add i915 perf data reader Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 6/6] tools: add i915-perf-reader Lionel Landwerlin
  4 siblings, 0 replies; 6+ messages in thread
From: Lionel Landwerlin @ 2020-02-18 22:56 UTC (permalink / raw)
  To: igt-dev

A tool recording i915-perf data along with device description &
timestamp correlation points for mapping data to CPU tracepoints.

The format is mostly described in perf_data.h.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915/perf_data.h                          |  118 ++
 lib/meson.build                               |    1 +
 lib/tests/i915_perf_data_alignment.c          |   40 +
 lib/tests/meson.build                         |    1 +
 tools/i915-perf/i915_perf_control.c           |  133 +++
 tools/i915-perf/i915_perf_recorder.c          | 1052 +++++++++++++++++
 tools/i915-perf/i915_perf_recorder_commands.h |   39 +
 tools/i915-perf/meson.build                   |   11 +
 8 files changed, 1395 insertions(+)
 create mode 100644 lib/i915/perf_data.h
 create mode 100644 lib/tests/i915_perf_data_alignment.c
 create mode 100644 tools/i915-perf/i915_perf_control.c
 create mode 100644 tools/i915-perf/i915_perf_recorder.c
 create mode 100644 tools/i915-perf/i915_perf_recorder_commands.h

diff --git a/lib/i915/perf_data.h b/lib/i915/perf_data.h
new file mode 100644
index 00000000..fb3556f6
--- /dev/null
+++ b/lib/i915/perf_data.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef PERF_DATA_H
+#define PERF_DATA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <i915_drm.h>
+
+/* The structures below are embedded in the i915-perf stream so as to
+ * provide metadata. The types used in the
+ * drm_i915_perf_record_header.type are defined in
+ * intel_perf_record_type.
+ */
+
+#include <stdint.h>
+
+enum intel_perf_record_type {
+	/* Start at 65536, which is pretty safe since after 3years the
+	 * kernel hasn't defined more than 3 entries.
+	 */
+
+	INTEL_PERF_RECORD_TYPE_VERSION = 1 << 16,
+
+	/* intel_perf_record_device_info */
+	INTEL_PERF_RECORD_TYPE_DEVICE_INFO,
+
+	/* intel_perf_record_device_topology */
+	INTEL_PERF_RECORD_TYPE_DEVICE_TOPOLOGY,
+
+	/* intel_perf_record_timestamp_correlation */
+	INTEL_PERF_RECORD_TYPE_TIMESTAMP_CORRELATION,
+};
+
+/* This structure cannot ever change. */
+struct intel_perf_record_version {
+	/* Version of the i915-perf file recording format (effectively
+	 * versioning this file).
+	 */
+	uint32_t version;
+
+#define INTEL_PERF_RECORD_VERSION (1)
+
+	uint32_t pad;
+} __attribute__((packed));
+
+struct intel_perf_record_device_info {
+	/* Frequency of the timestamps in the records. */
+	uint64_t timestamp_frequency;
+
+	/* PCI ID */
+	uint32_t device_id;
+
+	/* Stepping */
+	uint32_t device_revision;
+
+	/* GT min/max frequencies */
+	uint32_t gt_min_frequency;
+	uint32_t gt_max_frequency;
+
+	/* Engine */
+	uint32_t engine_class;
+	uint32_t engine_instance;
+
+	/* enum drm_i915_oa_format */
+	uint32_t oa_format;
+
+	/* Metric set name */
+	char metric_set_name[256];
+
+	/* Configuration identifier */
+	char metric_set_uuid[40];
+
+	uint32_t pad;
+ } __attribute__((packed));
+
+/* Topology as reported by i915 (variable length, aligned by the
+ * recorder). */
+struct intel_perf_record_device_topology {
+	struct drm_i915_query_topology_info topology;
+};
+
+/* Timestamp correlation between CPU/GPU. */
+struct intel_perf_record_timestamp_correlation {
+	/* In CLOCK_MONOTONIC */
+	uint64_t cpu_timestamp;
+
+	/* Engine timestamp associated with the OA unit */
+	uint64_t gpu_timestamp;
+} __attribute__((packed));
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* PERF_DATA_H */
diff --git a/lib/meson.build b/lib/meson.build
index 4de1b211..1fe621ec 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -218,6 +218,7 @@ install_headers(
   'igt_list.h',
   'intel_chipset.h',
   'i915/perf.h',
+  'i915/perf_data.h',
   subdir : 'i915-perf'
 )
 
diff --git a/lib/tests/i915_perf_data_alignment.c b/lib/tests/i915_perf_data_alignment.c
new file mode 100644
index 00000000..8c14e0d0
--- /dev/null
+++ b/lib/tests/i915_perf_data_alignment.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2020 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "igt_core.h"
+
+#include "igt_tests_common.h"
+
+#include "i915/perf_data.h"
+
+#define is_aligned(s) ((sizeof(s) % 8) == 0)
+
+igt_main
+{
+	igt_subtest("alignment") {
+		internal_assert(is_aligned(struct intel_perf_record_version));
+		internal_assert(is_aligned(struct intel_perf_record_device_info));
+		internal_assert(is_aligned(struct intel_perf_record_timestamp_correlation));
+	}
+}
diff --git a/lib/tests/meson.build b/lib/tests/meson.build
index e5066665..6cd8cb0e 100644
--- a/lib/tests/meson.build
+++ b/lib/tests/meson.build
@@ -16,6 +16,7 @@ lib_tests = [
 	'igt_simulation',
 	'igt_stats',
 	'igt_subtest_group',
+	'i915_perf_data_alignment',
 ]
 
 lib_fail_tests = [
diff --git a/tools/i915-perf/i915_perf_control.c b/tools/i915-perf/i915_perf_control.c
new file mode 100644
index 00000000..a8d0d30f
--- /dev/null
+++ b/tools/i915-perf/i915_perf_control.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "i915_perf_recorder_commands.h"
+
+static void
+usage(const char *name)
+{
+	fprintf(stdout,
+		"Usage: %s [options]\n"
+		"\n"
+		"     --help,               -h         Print this screen\n"
+		"     --command-fifo,       -f <path>  Path to a command fifo\n"
+		"     --dump,               -d <path>  Write a content of circular buffer to path\n",
+		name);
+}
+
+int
+main(int argc, char *argv[])
+{
+	const struct option long_options[] = {
+		{"help",                       no_argument, 0, 'h'},
+		{"dump",                 required_argument, 0, 'd'},
+		{"command-fifo",         required_argument, 0, 'f'},
+		{"quit",                       no_argument, 0, 'q'},
+		{0, 0, 0, 0}
+	};
+	const char *command_fifo = I915_PERF_RECORD_FIFO_PATH, *dump_file = NULL;
+	FILE *command_fifo_file;
+	int opt;
+	bool quit = false;
+
+	while ((opt = getopt_long(argc, argv, "hd:f:q", long_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			usage(argv[0]);
+			return EXIT_SUCCESS;
+		case 'd':
+			dump_file = optarg;
+			break;
+		case 'f':
+			command_fifo = optarg;
+			break;
+		case 'q':
+			quit = true;
+			break;
+		default:
+			fprintf(stderr, "Internal error: "
+				"unexpected getopt value: %d\n", opt);
+			usage(argv[0]);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!command_fifo)
+		return EXIT_FAILURE;
+
+	command_fifo_file = fopen(command_fifo, "r+");
+	if (!command_fifo_file) {
+		fprintf(stderr, "Unable to open command file\n");
+		return EXIT_FAILURE;
+	}
+
+	if (dump_file) {
+		if (dump_file[0] == '/') {
+			uint32_t total_len =
+				sizeof(struct recorder_command_base) + strlen(dump_file) + 1;
+			struct {
+				struct recorder_command_base base;
+				struct recorder_command_dump dump;
+			} *data = malloc(total_len);
+
+			data->base.command = RECORDER_COMMAND_DUMP;
+			data->base.size = total_len;
+			snprintf((char *) data->dump.path, strlen(dump_file) + 1, "%s", dump_file);
+
+			fwrite(data, total_len, 1, command_fifo_file);
+		} else {
+			char *cwd = get_current_dir_name();
+			uint32_t path_len = strlen(cwd) + 1 + strlen(dump_file) + 1;
+			uint32_t total_len = sizeof(struct recorder_command_base) + path_len;
+			struct {
+				struct recorder_command_base base;
+				struct recorder_command_dump dump;
+			} *data = malloc(total_len);
+
+			data->base.command = RECORDER_COMMAND_DUMP;
+			data->base.size = total_len;
+			snprintf((char *) data->dump.path, path_len, "%s/%s", cwd, dump_file);
+
+			fwrite(data, total_len, 1, command_fifo_file);
+		}
+	}
+
+	if (quit) {
+		struct recorder_command_base base = {
+			.command = RECORDER_COMMAND_QUIT,
+			.size = sizeof(base),
+		};
+
+		fwrite(&base, sizeof(base), 1, command_fifo_file);
+	}
+
+	fclose(command_fifo_file);
+
+	return EXIT_SUCCESS;
+}
diff --git a/tools/i915-perf/i915_perf_recorder.c b/tools/i915-perf/i915_perf_recorder.c
new file mode 100644
index 00000000..760cabf1
--- /dev/null
+++ b/tools/i915-perf/i915_perf_recorder.c
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <i915_drm.h>
+
+#include "igt_core.h"
+#include "intel_chipset.h"
+#include "i915/perf.h"
+#include "i915/perf_data.h"
+
+#include "i915_perf_recorder_commands.h"
+
+#define ALIGN(v, a) (((v) + (a)-1) & ~((a)-1))
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+struct circular_buffer {
+	char   *data;
+	size_t  allocated_size;
+	size_t  size;
+	size_t  beginpos;
+	size_t  endpos;
+};
+
+struct chunk {
+	char *data;
+	size_t len;
+};
+
+static size_t
+circular_available_size(const struct circular_buffer *buffer)
+{
+	assert(buffer->size <= buffer->allocated_size);
+	return buffer->allocated_size - buffer->size;
+}
+
+static void
+get_chunks(struct chunk *chunks, struct circular_buffer *buffer, bool write, size_t len)
+{
+	size_t offset = write ? buffer->endpos : buffer->beginpos;
+
+	if (write)
+		assert(circular_available_size(buffer) >= len);
+	else
+		assert(buffer->size >= len);
+
+	chunks[0].data = &buffer->data[offset];
+
+	if ((offset + len) > buffer->allocated_size) {
+		chunks[0].len = buffer->allocated_size - offset;
+		chunks[1].data = buffer->data;
+		chunks[1].len = len - (buffer->allocated_size - offset);
+	} else {
+		chunks[0].len = len;
+		chunks[1].data = NULL;
+		chunks[1].len = 0;
+	}
+}
+
+static ssize_t
+circular_buffer_read(void *c, char *buf, size_t size)
+{
+	struct circular_buffer *buffer = c;
+	struct chunk chunks[2];
+
+	if (buffer->size < size)
+		return -1;
+
+	get_chunks(chunks, buffer, false, size);
+
+	memcpy(buf, chunks[0].data, chunks[0].len);
+	memcpy(buf + chunks[0].len, chunks[1].data, chunks[1].len);
+	buffer->beginpos = (buffer->beginpos + size) % buffer->allocated_size;
+	buffer->size -= size;
+
+	return size;
+}
+
+static size_t
+peek_item_size(struct circular_buffer *buffer)
+{
+	struct drm_i915_perf_record_header header;
+	struct chunk chunks[2];
+
+	if (!buffer->size)
+		return 0;
+
+	assert(buffer->size >= sizeof(header));
+
+	get_chunks(chunks, buffer, false, sizeof(header));
+	memcpy(&header, chunks[0].data, chunks[0].len);
+	memcpy((char *) &header + chunks[0].len, chunks[1].data, chunks[1].len);
+
+	return header.size;
+}
+
+static void
+circular_shrink(struct circular_buffer *buffer, size_t size)
+{
+	size_t shrank = 0, item_size;
+
+	assert(size <= buffer->allocated_size);
+
+	while (shrank < size && buffer->size > (item_size = peek_item_size(buffer))) {
+		assert(item_size > 0 && item_size <= buffer->allocated_size);
+
+		buffer->beginpos = (buffer->beginpos + item_size) % buffer->allocated_size;
+		buffer->size -= item_size;
+
+		shrank += item_size;
+	}
+}
+
+static ssize_t
+circular_buffer_write(void *c, const char *buf, size_t _size)
+{
+	struct circular_buffer *buffer = c;
+	size_t size = _size;
+
+	while (size) {
+		size_t avail = circular_available_size(buffer), item_size;
+		struct chunk chunks[2];
+
+		/* Make space in the buffer if there is too much data. */
+		if (avail < size)
+			circular_shrink(buffer, size - avail);
+
+		item_size = MIN(circular_available_size(buffer), size);
+
+		get_chunks(chunks, buffer, true, item_size);
+
+		memcpy(chunks[0].data, buf, chunks[0].len);
+		memcpy(chunks[1].data, buf + chunks[0].len, chunks[1].len);
+
+		buf += item_size;
+		size -= item_size;
+
+		buffer->endpos = (buffer->endpos + item_size) % buffer->allocated_size;
+		buffer->size += item_size;
+	}
+
+	return _size;
+}
+
+static int
+circular_buffer_seek(void *c, off64_t *offset, int whence)
+{
+	return -1;
+}
+
+static int
+circular_buffer_close(void *c)
+{
+	return 0;
+}
+
+cookie_io_functions_t circular_buffer_functions = {
+	.read  = circular_buffer_read,
+	.write = circular_buffer_write,
+	.seek  = circular_buffer_seek,
+	.close = circular_buffer_close,
+};
+
+
+static bool
+read_file_uint64(const char *file, uint64_t *value)
+{
+	char buf[32];
+	int fd, n;
+
+	fd = open(file, 0);
+	if (fd < 0)
+		return false;
+	n = read(fd, buf, sizeof (buf) - 1);
+	close(fd);
+	if (n < 0)
+		return false;
+
+	buf[n] = '\0';
+	*value = strtoull(buf, 0, 0);
+
+	return true;
+}
+
+static uint32_t
+read_device_param(const char *stem, int id, const char *param)
+{
+	char *name;
+	int ret = asprintf(&name, "/sys/class/drm/%s%u/device/%s", stem, id, param);
+	uint64_t value;
+	bool success;
+
+	assert(ret != -1);
+
+	success = read_file_uint64(name, &value);
+	free(name);
+
+	return success ? value : 0;
+}
+
+static int
+find_intel_render_node(void)
+{
+	for (int i = 128; i < (128 + 16); i++) {
+		if (read_device_param("renderD", i, "vendor") == 0x8086)
+			return i;
+	}
+
+	return -1;
+}
+
+static int
+open_render_node(uint32_t *devid)
+{
+	char *name;
+	int ret;
+	int fd;
+
+	int render = find_intel_render_node();
+	if (render < 0)
+		return -1;
+
+	ret = asprintf(&name, "/dev/dri/renderD%u", render);
+	assert(ret != -1);
+
+	*devid = read_device_param("renderD", render, "device");
+
+	fd = open(name, O_RDWR);
+	free(name);
+
+	return fd;
+}
+
+static uint32_t
+oa_exponent_for_period(uint64_t device_timestamp_frequency, double period)
+{
+	uint64_t period_ns = 1000 * 1000 * 1000 * period;
+	uint64_t device_periods[32];
+
+	for (uint32_t i = 0; i < ARRAY_SIZE(device_periods); i++)
+		device_periods[i] = 1000000000ull * (1u << i) / device_timestamp_frequency;
+
+	for (uint32_t i = 1; i < ARRAY_SIZE(device_periods); i++) {
+		if (period_ns >= device_periods[i - 1] &&
+		    period_ns < device_periods[i]) {
+			if ((device_periods[i] - period_ns) >
+			    (period_ns - device_periods[i - 1]))
+				return i - 1;
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+static int
+perf_ioctl(int fd, unsigned long request, void *arg)
+{
+	int ret;
+
+	do {
+		ret = ioctl(fd, request, arg);
+	} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+	return ret;
+}
+
+static uint64_t
+get_device_timestamp_frequency(const struct intel_device_info *devinfo, int drm_fd)
+{
+	drm_i915_getparam_t gp;
+	int timestamp_frequency;
+
+	gp.param = I915_PARAM_CS_TIMESTAMP_FREQUENCY;
+	gp.value = &timestamp_frequency;
+	if (perf_ioctl(drm_fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0)
+		return timestamp_frequency;
+
+	if (devinfo->gen > 9) {
+		fprintf(stderr, "Unable to query timestamp frequency from i915, please update kernel.\n");
+		return 0;
+	}
+
+	fprintf(stderr, "Warning: unable to query timestamp frequency from i915, guessing values...\n");
+
+	if (devinfo->gen <= 8)
+		return 12500000;
+	if (devinfo->is_broxton)
+		return 19200000;
+	return 12000000;
+}
+
+struct recording_context {
+	int drm_fd;
+	int perf_fd;
+
+	uint32_t devid;
+	uint64_t timestamp_frequency;
+
+	const struct intel_device_info *devinfo;
+
+	struct drm_i915_query_topology_info *topology;
+	uint32_t topology_size;
+
+	struct intel_perf *perf;
+	struct intel_perf_metric_set *metric_set;
+
+	uint32_t oa_exponent;
+
+	struct circular_buffer circular_buffer;
+	FILE *output_stream;
+
+	const char *command_fifo;
+	int command_fifo_fd;
+};
+
+static int
+perf_open(struct recording_context *ctx)
+{
+	uint64_t properties[DRM_I915_PERF_PROP_MAX * 2];
+	struct drm_i915_perf_open_param param;
+	int p = 0, stream_fd;
+
+	properties[p++] = DRM_I915_PERF_PROP_SAMPLE_OA;
+	properties[p++] = true;
+
+	properties[p++] = DRM_I915_PERF_PROP_OA_METRICS_SET;
+	properties[p++] = ctx->metric_set->perf_oa_metrics_set;
+
+	properties[p++] = DRM_I915_PERF_PROP_OA_FORMAT;
+	properties[p++] = ctx->metric_set->perf_oa_format;
+
+	properties[p++] = DRM_I915_PERF_PROP_OA_EXPONENT;
+	properties[p++] = ctx->oa_exponent;
+
+	memset(&param, 0, sizeof(param));
+	param.flags = 0;
+	param.flags |= I915_PERF_FLAG_FD_CLOEXEC | I915_PERF_FLAG_FD_NONBLOCK;
+	param.properties_ptr = (uintptr_t)properties;
+	param.num_properties = p / 2;
+
+	stream_fd = perf_ioctl(ctx->drm_fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	return stream_fd;
+}
+
+static bool quit = false;
+
+static void
+sigint_handler(int val)
+{
+	quit = true;
+}
+
+static bool
+write_version(FILE *output, struct recording_context *ctx)
+{
+	struct intel_perf_record_version version = {
+		.version = INTEL_PERF_RECORD_VERSION,
+	};
+	struct drm_i915_perf_record_header header = {
+		.type = INTEL_PERF_RECORD_TYPE_VERSION,
+		.size = sizeof(header) + sizeof(version),
+	};
+
+	if (fwrite(&header, sizeof(header), 1, output) != 1)
+		return false;
+
+	if (fwrite(&version, sizeof(version), 1, output) != 1)
+		return false;
+
+	return true;
+}
+
+static bool
+write_header(FILE *output, struct recording_context *ctx)
+{
+	struct intel_perf_record_device_info info = {
+		.timestamp_frequency = ctx->timestamp_frequency,
+		.device_id = ctx->perf->devinfo.devid,
+		.device_revision = ctx->perf->devinfo.revision,
+		.gt_min_frequency = ctx->perf->devinfo.gt_min_freq,
+		.gt_max_frequency = ctx->perf->devinfo.gt_max_freq,
+		.oa_format = ctx->metric_set->perf_oa_format,
+		.engine_class = I915_ENGINE_CLASS_RENDER,
+		.engine_instance = 0,
+	};
+	struct drm_i915_perf_record_header header = {
+		.type = INTEL_PERF_RECORD_TYPE_DEVICE_INFO,
+		.size = sizeof(header) + sizeof(info),
+	};
+
+	snprintf(info.metric_set_name, sizeof(info.metric_set_name),
+		 "%s", ctx->metric_set->symbol_name);
+	snprintf(info.metric_set_uuid, sizeof(info.metric_set_uuid),
+		 "%s", ctx->metric_set->hw_config_guid);
+
+	if (fwrite(&header, sizeof(header), 1, output) != 1)
+		return false;
+
+	if (fwrite(&info, sizeof(info), 1, output) != 1)
+		return false;
+
+	return true;
+}
+
+static struct drm_i915_query_topology_info *
+get_topology(int drm_fd, uint32_t *topology_size)
+{
+	struct drm_i915_query query = {};
+	struct drm_i915_query_topology_info *topo_info;
+	struct drm_i915_query_item item = {
+		.query_id = DRM_I915_QUERY_TOPOLOGY_INFO,
+	};
+	int ret;
+
+	query.num_items = 1;
+	query.items_ptr = (uintptr_t) &item;
+
+	/* Maybe not be available on older kernels. */
+	ret = perf_ioctl(drm_fd, DRM_IOCTL_I915_QUERY, &query);
+	if (ret < 0)
+		return NULL;
+
+	assert(item.length > 0);
+	*topology_size = item.length;
+	topo_info = malloc(item.length);
+	item.data_ptr = (uintptr_t) topo_info;
+
+	ret = perf_ioctl(drm_fd, DRM_IOCTL_I915_QUERY, &query);
+	assert(ret == 0);
+
+	return topo_info;
+}
+
+static bool
+write_topology(FILE *output, struct recording_context *ctx)
+{
+	struct drm_i915_perf_record_header header = {
+		.type = INTEL_PERF_RECORD_TYPE_DEVICE_TOPOLOGY,
+	};
+	char pad[8] = { 0, };
+
+	header.size = sizeof(header) + ctx->topology_size;
+	if (fwrite(&header, sizeof(header), 1, output) != 1)
+		return false;
+
+	if (fwrite(ctx->topology, ctx->topology_size, 1, output) != 1)
+		return false;
+
+	/* Align the size to align all other packets to 8 bytes. */
+	if (ctx->topology_size % 8) {
+		if (fwrite(pad, ctx->topology_size % 8, 1, output) != 1)
+			return false;
+	}
+
+	return true;
+}
+
+static bool
+write_i915_perf_data(FILE *output, int perf_fd)
+{
+	ssize_t ret;
+	char data[4096];
+
+	while ((ret = read(perf_fd, data, sizeof(data))) > 0 ||
+	       errno == EINTR) {
+		if (fwrite(data, ret, 1, output) != 1)
+			return false;
+	}
+
+	return true;
+}
+
+static uint64_t timespec_diff(struct timespec *begin,
+			      struct timespec *end)
+{
+	return 1000000000ull * (end->tv_sec - begin->tv_sec) + end->tv_nsec - begin->tv_nsec;
+}
+
+static clock_t correlation_clock_id = CLOCK_MONOTONIC;
+
+static bool
+get_correlation_timestamps(struct intel_perf_record_timestamp_correlation *corr, int drm_fd)
+{
+	struct drm_i915_reg_read reg_read;
+	struct {
+		struct timespec cpu_ts_begin;
+		struct timespec cpu_ts_end;
+		uint64_t gpu_ts;
+	} attempts[3];
+	uint32_t best = 0;
+
+#define RENDER_RING_TIMESTAMP 0x2358
+
+        reg_read.offset = RENDER_RING_TIMESTAMP | I915_REG_READ_8B_WA;
+
+	/* Gather 3 correlations. */
+	for (uint32_t i = 0; i < ARRAY_SIZE(attempts); i++) {
+		clock_gettime(correlation_clock_id, &attempts[i].cpu_ts_begin);
+		if (perf_ioctl(drm_fd, DRM_IOCTL_I915_REG_READ, &reg_read) < 0)
+			return false;
+		clock_gettime(correlation_clock_id, &attempts[i].cpu_ts_end);
+
+		attempts[i].gpu_ts = reg_read.val;
+	}
+
+	/* Now select the best. */
+	for (uint32_t i = 1; i < ARRAY_SIZE(attempts); i++) {
+		if (timespec_diff(&attempts[i].cpu_ts_begin,
+				  &attempts[i].cpu_ts_end) <
+		    timespec_diff(&attempts[best].cpu_ts_begin,
+				  &attempts[best].cpu_ts_end))
+			best = i;
+	}
+
+	corr->cpu_timestamp =
+		(attempts[best].cpu_ts_begin.tv_sec * 1000000000ull +
+		 attempts[best].cpu_ts_begin.tv_nsec) +
+		timespec_diff(&attempts[best].cpu_ts_begin,
+			      &attempts[best].cpu_ts_end) / 2;
+	corr->gpu_timestamp = attempts[best].gpu_ts;
+
+	return true;
+}
+
+static bool
+write_saved_correlation_timestamps(FILE *output,
+				   const struct intel_perf_record_timestamp_correlation *corr)
+{
+	struct drm_i915_perf_record_header header = {
+		.type = INTEL_PERF_RECORD_TYPE_TIMESTAMP_CORRELATION,
+		.size = sizeof(header) + sizeof(*corr),
+	};
+
+	if (fwrite(&header, sizeof(header), 1, output) != 1)
+		return false;
+
+	if (fwrite(corr, sizeof(*corr), 1, output) != 1)
+		return false;
+
+	return true;
+}
+
+static bool
+write_correlation_timestamps(FILE *output, int drm_fd)
+{
+	struct intel_perf_record_timestamp_correlation corr;
+
+	if (!get_correlation_timestamps(&corr, drm_fd))
+		return false;
+
+	return write_saved_correlation_timestamps(output, &corr);
+}
+
+static void
+read_command_file(struct recording_context *ctx)
+{
+	struct recorder_command_base header;
+	ssize_t ret = read(ctx->command_fifo_fd, &header, sizeof(header));
+
+	if (ret < 0)
+		return;
+
+	switch (header.command) {
+	case RECORDER_COMMAND_DUMP: {
+		uint32_t len = header.size - sizeof(header), offset = 0;
+		struct recorder_command_dump *dump = malloc(len);
+		FILE *file;
+
+		while (offset < len &&
+		       ((ret = read(ctx->command_fifo_fd,
+				    (void *) dump + offset, len - offset)) > 0
+			|| errno == EAGAIN)) {
+			if (ret > 0)
+				offset += ret;
+		}
+
+		fprintf(stdout, "Writing circular buffer to %s\n", dump->path);
+
+		file = fopen((const char *) dump->path, "w+");
+		if (file) {
+			struct chunk chunks[2];
+
+			fflush(ctx->output_stream);
+			get_chunks(chunks, &ctx->circular_buffer,
+				   false, ctx->circular_buffer.size);
+
+			if (!write_version(file, ctx) ||
+			    !write_header(file, ctx) ||
+			    !write_topology(file, ctx) ||
+			    fwrite(chunks[0].data, chunks[0].len, 1, file) != 1 ||
+			    (chunks[1].len > 0 &&
+			     fwrite(chunks[1].data, chunks[1].len, 1, file) != 1) ||
+			    !write_correlation_timestamps(file, ctx->drm_fd)) {
+				fprintf(stderr, "Unable to write circular buffer data in file '%s'\n",
+					dump->path);
+			}
+			fclose(file);
+		} else
+			fprintf(stderr, "Unable to write dump file '%s'\n", dump->path);
+
+		free(dump);
+		break;
+	}
+	case RECORDER_COMMAND_QUIT:
+		quit = true;
+		break;
+	default:
+		fprintf(stderr, "Unknown command 0x%x\n", header.command);
+		break;
+	}
+}
+
+static void
+print_metric_sets(const struct intel_perf *perf)
+{
+	struct intel_perf_metric_set *metric_set;
+	uint32_t longest_name = 0;
+
+	igt_list_for_each_entry(metric_set, &perf->metric_sets, link) {
+		longest_name = MAX(longest_name, strlen(metric_set->symbol_name));
+	}
+
+	igt_list_for_each_entry(metric_set, &perf->metric_sets, link) {
+		fprintf(stdout, "%s:%*s%s\n",
+			metric_set->symbol_name,
+			(int) (longest_name - strlen(metric_set->symbol_name) + 1), " ",
+			metric_set->name);
+	}
+}
+
+static void
+print_metric_set_counters(const struct intel_perf_metric_set *metric_set)
+{
+	uint32_t longest_name = 0;
+
+	for (uint32_t i = 0; i < metric_set->n_counters; i++) {
+		longest_name = MAX(longest_name, strlen(metric_set->counters[i].name));
+	}
+
+	fprintf(stdout, "%s (%s):\n", metric_set->symbol_name, metric_set->name);
+	for (uint32_t i = 0; i < metric_set->n_counters; i++) {
+		struct intel_perf_logical_counter *counter = &metric_set->counters[i];
+
+		fprintf(stdout, "  %s:%*s%s\n",
+			counter->name,
+			(int)(longest_name - strlen(counter->name) + 1), " ",
+			counter->desc);
+	}
+}
+
+static void
+print_metric_sets_counters(struct intel_perf *perf)
+{
+	struct intel_perf_metric_set *metric_set;
+
+	igt_list_for_each_entry(metric_set, &perf->metric_sets, link)
+		print_metric_set_counters(metric_set);
+}
+
+static void
+usage(const char *name)
+{
+	fprintf(stdout,
+		"Usage: %s [options]\n"
+		"Recording tool for i915-perf.\n"
+		"\n"
+		"     --help,               -h          Print this screen\n"
+		"     --correlation-period, -c <value>  Time period of timestamp correlation in seconds\n"
+		"                                       (default = 1.0)\n"
+		"     --perf-period,        -p <value>  Time period of i915-perf reports in seconds\n"
+		"                                       (default = 0.001)\n"
+		"     --metric,             -m <value>  i915 metric to sample with (use value=list to list all metrics)\n"
+		"     --counters,           -C          List counters for a given metric and exit\n"
+		"     --size,               -s <value>  Size of circular buffer to use in kilobytes\n"
+		"                                       If specified, a maximum amount of <value> data will\n"
+		"                                       be recorded.\n"
+		"     --command-fifo,       -f <path>   Path to a command fifo, implies circular buffer\n"
+		"                                       (To use with i915-perf-control)\n"
+		"     --output,             -o <path>   Output file (default = i915_perf.record)\n"
+		"     --cpu-clock,          -k <path>   Cpu clock to use for correlations\n"
+		"                                       Values: boot, mono, mono_raw (default = mono)\n",
+		name);
+}
+
+static void
+teardown_recording_context(struct recording_context *ctx)
+{
+	if (ctx->topology)
+		free(ctx->topology);
+
+	if (ctx->perf)
+		intel_perf_free(ctx->perf);
+
+	if (ctx->command_fifo)
+		unlink(ctx->command_fifo);
+	if (ctx->command_fifo_fd != -1)
+		close(ctx->command_fifo_fd);
+
+	if (ctx->output_stream)
+		fclose(ctx->output_stream);
+
+	free(ctx->circular_buffer.data);
+
+	if (ctx->perf_fd != -1)
+		close(ctx->perf_fd);
+	if (ctx->drm_fd != -1)
+		close(ctx->drm_fd);
+}
+
+int
+main(int argc, char *argv[])
+{
+	const struct option long_options[] = {
+		{"help",                       no_argument, 0, 'h'},
+		{"correlation-period",   required_argument, 0, 'c'},
+		{"perf-period",          required_argument, 0, 'p'},
+		{"metric",               required_argument, 0, 'm'},
+		{"counters",                   no_argument, 0, 'C'},
+		{"output",               required_argument, 0, 'o'},
+		{"size",                 required_argument, 0, 's'},
+		{"command-fifo",         required_argument, 0, 'f'},
+		{"cpu-clock",            required_argument, 0, 'k'},
+		{0, 0, 0, 0}
+	};
+	const struct {
+		clock_t id;
+		const char *name;
+	} clock_names[] = {
+		{ CLOCK_BOOTTIME,      "boot" },
+		{ CLOCK_MONOTONIC,     "mono" },
+		{ CLOCK_MONOTONIC_RAW, "mono_raw" },
+	};
+	double corr_period = 1.0, perf_period = 0.001;
+	const char *metric_name = NULL, *output_file = "i915_perf.record";
+	struct intel_perf_metric_set *metric_set;
+	struct intel_perf_record_timestamp_correlation initial_correlation;
+	struct timespec now;
+	uint64_t corr_period_ns, poll_time_ns;
+	uint32_t circular_size = 0;
+	int opt;
+	bool list_counters = false;
+	FILE *output = NULL;
+	struct recording_context ctx = {
+		.drm_fd = -1,
+		.perf_fd = -1,
+
+		.command_fifo = I915_PERF_RECORD_FIFO_PATH,
+		.command_fifo_fd = -1,
+	};
+
+	while ((opt = getopt_long(argc, argv, "hc:p:m:Co:s:f:k:", long_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			usage(argv[0]);
+			return EXIT_SUCCESS;
+		case 'c':
+			corr_period = atof(optarg);
+			break;
+		case 'p':
+			perf_period = atof(optarg);
+			break;
+		case 'm':
+			metric_name = optarg;
+			break;
+		case 'C':
+			list_counters = true;
+			break;
+		case 'o':
+			output_file = optarg;
+			break;
+		case 's':
+			circular_size = MAX(8, atoi(optarg)) * 1024;
+			break;
+		case 'f':
+			ctx.command_fifo = optarg;
+			circular_size = 8 * 1024 * 1024;
+			break;
+		case 'k': {
+			bool found = false;
+			for (uint32_t i = 0; i < ARRAY_SIZE(clock_names); i++) {
+				if (!strcmp(clock_names[i].name, optarg)) {
+					correlation_clock_id = clock_names[i].id;
+					found = true;
+					break;
+				}
+			}
+			if (!found) {
+				fprintf(stderr, "Unknown clock name '%s'\n", optarg);
+				return EXIT_FAILURE;
+			}
+			break;
+		}
+		default:
+			fprintf(stderr, "Internal error: "
+				"unexpected getopt value: %d\n", opt);
+			usage(argv[0]);
+			return EXIT_FAILURE;
+		}
+	}
+
+	ctx.drm_fd = open_render_node(&ctx.devid);
+	if (ctx.drm_fd < 0) {
+		fprintf(stderr, "Unable to open device.\n");
+		return EXIT_FAILURE;
+	}
+
+	ctx.devinfo = intel_get_device_info(ctx.devid);
+	if (!ctx.devinfo) {
+		fprintf(stderr, "No device info found.\n");
+		goto fail;
+	}
+
+	fprintf(stdout, "Device name=%s gen=%i gt=%i id=0x%x\n",
+		ctx.devinfo->codename, ctx.devinfo->gen, ctx.devinfo->gt, ctx.devid);
+
+	ctx.topology = get_topology(ctx.drm_fd, &ctx.topology_size);
+	if (!ctx.topology) {
+		fprintf(stderr, "Unable to retrieve GPU topology (requires kernel 4.17+).\n");
+		goto fail;
+	}
+
+	ctx.perf = intel_perf_for_fd(ctx.drm_fd);
+	if (!ctx.perf) {
+		fprintf(stderr, "No perf data found.\n");
+		goto fail;
+	}
+
+	intel_perf_load_perf_configs(ctx.perf, ctx.drm_fd);
+
+	if (metric_name) {
+		if (!strcmp(metric_name, "list")) {
+			print_metric_sets(ctx.perf);
+			return EXIT_SUCCESS;
+		}
+
+		igt_list_for_each_entry(metric_set, &ctx.perf->metric_sets, link) {
+			if (!strcasecmp(metric_set->symbol_name, metric_name)) {
+				ctx.metric_set = metric_set;
+				break;
+			}
+		}
+	}
+
+	if (list_counters) {
+		if (!ctx.metric_set)
+			print_metric_sets_counters(ctx.perf);
+		else
+			print_metric_set_counters(ctx.metric_set);
+		teardown_recording_context(&ctx);
+		return EXIT_SUCCESS;
+	}
+
+	if (!ctx.metric_set) {
+		if (!metric_name)
+			fprintf(stderr, "No metric set specified.\n");
+		else
+			fprintf(stderr, "Unknown metric set '%s'.\n", metric_name);
+		print_metric_sets(ctx.perf);
+		goto fail;
+	}
+
+	intel_perf_load_perf_configs(ctx.perf, ctx.drm_fd);
+
+	ctx.timestamp_frequency = get_device_timestamp_frequency(ctx.devinfo, ctx.drm_fd);
+
+	signal(SIGINT, sigint_handler);
+
+	if (ctx.command_fifo) {
+		if (mkfifo(ctx.command_fifo,
+			   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) != 0) {
+			fprintf(stderr, "Unable to create command fifo '%s': %s\n",
+				ctx.command_fifo, strerror(errno));
+			goto fail;
+		}
+
+		ctx.command_fifo_fd = open(ctx.command_fifo, O_RDWR);
+		if (ctx.command_fifo_fd < 0) {
+			fprintf(stderr, "Unable to open command fifo '%s': %s\n",
+				ctx.command_fifo, strerror(errno));
+			goto fail;
+		}
+	}
+
+	if (circular_size) {
+		ctx.circular_buffer.allocated_size = circular_size;
+		ctx.circular_buffer.data = malloc(circular_size);
+		if (!ctx.circular_buffer.data) {
+			fprintf(stderr, "Unable to allocate circular buffer\n");
+			goto fail;
+		}
+
+		ctx.output_stream = fopencookie(&ctx.circular_buffer, "w+",
+						circular_buffer_functions);
+		if (!ctx.output_stream) {
+			fprintf(stderr, "Unable to create circular buffer\n");
+			goto fail;
+		}
+
+		if (!get_correlation_timestamps(&initial_correlation, ctx.drm_fd)) {
+			fprintf(stderr, "Unable to correlation timestamps\n");
+			goto fail;
+		}
+
+		write_correlation_timestamps(ctx.output_stream, ctx.drm_fd);
+		fprintf(stdout,
+			"Recoding in internal circular buffer.\n"
+			"Use i915-perf-control to snapshot into file.\n");
+	} else {
+		output = fopen(output_file, "w+");
+		if (!output) {
+			fprintf(stderr, "Unable to open output file '%s'\n",
+				output_file);
+			goto fail;
+		}
+
+		if (!write_version(output, &ctx) ||
+		    !write_header(output, &ctx) ||
+		    !write_topology(output, &ctx) ||
+		    !write_correlation_timestamps(output, ctx.drm_fd)) {
+			fprintf(stderr, "Unable to write header in file '%s'\n",
+				output_file);
+			goto fail;
+		}
+
+		ctx.output_stream = output;
+		fprintf(stdout, "Writing recoding to %s\n", output_file);
+	}
+
+	if (ctx.metric_set->perf_oa_metrics_set == 0) {
+		fprintf(stderr,
+			"Unable to load performance configuration, consider running:\n"
+			"   sysctl dev.i915.perf_stream_paranoid=0\n");
+		goto fail;
+	}
+
+	ctx.oa_exponent = oa_exponent_for_period(ctx.timestamp_frequency, perf_period);
+	fprintf(stdout, "Opening perf stream with metric_id=%lu oa_exponent=%u\n",
+		ctx.metric_set->perf_oa_metrics_set, ctx.oa_exponent);
+
+	ctx.perf_fd = perf_open(&ctx);
+	if (ctx.perf_fd < 0) {
+		fprintf(stderr, "Unable to open i915 perf stream: %s\n",
+			strerror(errno));
+		goto fail;
+	}
+
+	corr_period_ns = corr_period * 1000000000ul;
+	poll_time_ns = corr_period_ns;
+
+	while (!quit) {
+		struct pollfd pollfd[2] = {
+			{         ctx.perf_fd, POLLIN, 0 },
+			{ ctx.command_fifo_fd, POLLIN, 0 },
+		};
+		uint64_t elapsed_ns;
+		int ret;
+
+		igt_gettime(&now);
+		ret = poll(pollfd, ctx.command_fifo_fd != -1 ? 2 : 1, poll_time_ns / 1000000);
+		if (ret < 0 && errno != EINTR) {
+			fprintf(stderr, "Failed to poll i915-perf stream: %s\n",
+				strerror(errno));
+			break;
+		}
+
+		if (ret > 0) {
+			if (pollfd[0].revents & POLLIN) {
+				if (!write_i915_perf_data(ctx.output_stream, ctx.perf_fd)) {
+					fprintf(stderr, "Failed to write i915-perf data: %s\n",
+						strerror(errno));
+					break;
+				}
+			}
+
+			if (pollfd[1].revents & POLLIN) {
+				read_command_file(&ctx);
+			}
+		}
+
+		elapsed_ns = igt_nsec_elapsed(&now);
+		if (elapsed_ns > poll_time_ns) {
+			poll_time_ns = corr_period_ns;
+			if (!write_correlation_timestamps(ctx.output_stream, ctx.drm_fd)) {
+				fprintf(stderr,
+					"Failed to write i915 timestamp correlation data: %s\n",
+					strerror(errno));
+				break;
+			}
+		} else {
+			poll_time_ns -= elapsed_ns;
+		}
+	}
+
+	fprintf(stdout, "Exiting...\n");
+
+	if (!write_correlation_timestamps(ctx.output_stream, ctx.drm_fd)) {
+		fprintf(stderr,
+			"Failed to write final i915 timestamp correlation data: %s\n",
+			strerror(errno));
+	}
+
+	teardown_recording_context(&ctx);
+
+	return EXIT_SUCCESS;
+
+ fail:
+	teardown_recording_context(&ctx);
+
+	return EXIT_FAILURE;
+}
diff --git a/tools/i915-perf/i915_perf_recorder_commands.h b/tools/i915-perf/i915_perf_recorder_commands.h
new file mode 100644
index 00000000..4855d80f
--- /dev/null
+++ b/tools/i915-perf/i915_perf_recorder_commands.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+
+#define I915_PERF_RECORD_FIFO_PATH "/tmp/.i915-perf-record"
+
+enum recorder_command {
+	RECORDER_COMMAND_DUMP = 1,
+	RECORDER_COMMAND_QUIT,
+};
+
+struct recorder_command_base {
+	uint32_t command;
+	uint32_t size;
+};
+
+struct recorder_command_dump {
+	uint8_t path[0];
+};
diff --git a/tools/i915-perf/meson.build b/tools/i915-perf/meson.build
index 0ebdd185..9884dfd9 100644
--- a/tools/i915-perf/meson.build
+++ b/tools/i915-perf/meson.build
@@ -3,3 +3,14 @@ executable('i915-perf-configs',
            include_directories: inc,
            dependencies: [lib_igt_chipset, lib_igt_i915_perf],
            install: true)
+
+executable('i915-perf-recorder',
+           [ 'i915_perf_recorder.c' ],
+           include_directories: inc,
+           dependencies: [lib_igt, lib_igt_i915_perf],
+           install: true)
+
+executable('i915-perf-control',
+           [ 'i915_perf_control.c' ],
+           include_directories: inc,
+           install: true)
-- 
2.25.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [igt-dev] [PATCH i-g-t v2 5/6] lib/i915-perf: add i915 perf data reader
  2020-02-18 22:56 [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf Lionel Landwerlin
                   ` (2 preceding siblings ...)
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 4/6] tools: add i915 perf recorder tool Lionel Landwerlin
@ 2020-02-18 22:56 ` Lionel Landwerlin
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 6/6] tools: add i915-perf-reader Lionel Landwerlin
  4 siblings, 0 replies; 6+ messages in thread
From: Lionel Landwerlin @ 2020-02-18 22:56 UTC (permalink / raw)
  To: igt-dev

This library allows reading a file recorded by i915-perf-recorder.

v2: Pick required libdrm dependency

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915-perf.pc.in         |   1 +
 lib/i915/perf_data_reader.c | 383 ++++++++++++++++++++++++++++++++++++
 lib/i915/perf_data_reader.h | 105 ++++++++++
 lib/meson.build             |   2 +
 4 files changed, 491 insertions(+)
 create mode 100644 lib/i915/perf_data_reader.c
 create mode 100644 lib/i915/perf_data_reader.h

diff --git a/lib/i915-perf.pc.in b/lib/i915-perf.pc.in
index e72efdc3..58781a42 100644
--- a/lib/i915-perf.pc.in
+++ b/lib/i915-perf.pc.in
@@ -6,5 +6,6 @@ includedir=@includedir@
 Name: i915-perf
 Description: i915 perf library
 Version: @version@
+Requires: libdrm >= 2.4.92
 Libs: -L${libdir} -li915_perf
 Cflags: -I${includedir}/i915-perf
diff --git a/lib/i915/perf_data_reader.c b/lib/i915/perf_data_reader.c
new file mode 100644
index 00000000..3b05a2e5
--- /dev/null
+++ b/lib/i915/perf_data_reader.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <i915_drm.h>
+
+#include "intel_chipset.h"
+#include "perf.h"
+#include "perf_data_reader.h"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static inline bool
+oa_report_ctx_is_valid(const struct intel_perf_devinfo *devinfo,
+		       const uint8_t *_report)
+{
+	const uint32_t *report = (const uint32_t *) _report;
+
+	if (devinfo->gen < 8) {
+		return false; /* TODO */
+	} else if (devinfo->gen == 8) {
+		return report[0] & (1ul << 25);
+	} else if (devinfo->gen > 8) {
+		return report[0] & (1ul << 16);
+	}
+
+	return false;
+}
+
+static uint32_t
+oa_report_ctx_id(const struct intel_perf_devinfo *devinfo, const uint8_t *report)
+{
+	if (!oa_report_ctx_is_valid(devinfo, report))
+		return 0xffffffff;
+	return ((const uint32_t *) report)[2];
+}
+
+static inline uint64_t
+oa_report_timestamp(const uint8_t *report)
+{
+	return ((const uint32_t *)report)[1];
+}
+
+static void
+append_record(struct intel_perf_data_reader *reader,
+	      const struct drm_i915_perf_record_header *header)
+{
+	if (reader->n_records >= reader->n_allocated_records) {
+		reader->n_allocated_records = MAX(100, 2 * reader->n_allocated_records);
+		reader->records =
+			(const struct drm_i915_perf_record_header **)
+			realloc((void *) reader->records,
+				reader->n_allocated_records *
+				sizeof(struct drm_i915_perf_record_header *));
+		assert(reader->records);
+	}
+
+	reader->records[reader->n_records++] = header;
+}
+
+static void
+append_timestamp_correlation(struct intel_perf_data_reader *reader,
+			     const struct intel_perf_record_timestamp_correlation *corr)
+{
+	if (reader->n_correlations >= reader->n_allocated_correlations) {
+		reader->n_allocated_correlations = MAX(100, 2 * reader->n_allocated_correlations);
+		reader->correlations =
+			(const struct intel_perf_record_timestamp_correlation **)
+			realloc((void *) reader->correlations,
+				reader->n_allocated_correlations *
+				sizeof(*reader->correlations));
+		assert(reader->correlations);
+	}
+
+	reader->correlations[reader->n_correlations++] = corr;
+}
+
+static struct intel_perf_metric_set *
+find_metric_set(struct intel_perf *perf, const char *symbol_name)
+{
+	struct intel_perf_metric_set *metric_set;
+
+	igt_list_for_each_entry(metric_set, &perf->metric_sets, link) {
+		if (!strcmp(symbol_name, metric_set->symbol_name))
+			return metric_set;
+	}
+
+	return NULL;
+}
+
+static void
+init_devinfo(struct intel_perf_devinfo *perf_devinfo,
+	     const struct intel_device_info *devinfo,
+	     uint32_t devid,
+	     uint64_t timestamp_frequency)
+{
+	perf_devinfo->devid = devid;
+	perf_devinfo->gen = devinfo->gen;
+	perf_devinfo->timestamp_frequency = timestamp_frequency;
+}
+
+static bool
+parse_data(struct intel_perf_data_reader *reader)
+{
+	const struct intel_perf_record_device_info *record_info;
+	const struct intel_perf_record_device_topology *record_topology;
+	const struct intel_device_info *devinfo;
+	const uint8_t *end = reader->mmap_data + reader->mmap_size;
+	const uint8_t *iter = reader->mmap_data;
+
+	while (iter < end) {
+		const struct drm_i915_perf_record_header *header =
+			(const struct drm_i915_perf_record_header *) iter;
+
+		switch (header->type) {
+		case DRM_I915_PERF_RECORD_SAMPLE:
+			append_record(reader, header);
+			break;
+
+		case DRM_I915_PERF_RECORD_OA_REPORT_LOST:
+		case DRM_I915_PERF_RECORD_OA_BUFFER_LOST:
+			assert(header->size == sizeof(*header));
+			break;
+
+		case INTEL_PERF_RECORD_TYPE_VERSION: {
+			struct intel_perf_record_version *version =
+				(struct intel_perf_record_version*) (header + 1);
+			if (version->version != INTEL_PERF_RECORD_VERSION) {
+				snprintf(reader->error_msg, sizeof(reader->error_msg),
+					 "Unsupported recording version (%u, expected %u)",
+					 version->version, INTEL_PERF_RECORD_VERSION);
+				return false;
+			}
+			break;
+		}
+
+		case INTEL_PERF_RECORD_TYPE_DEVICE_INFO: {
+			reader->record_info = header + 1;
+			assert(header->size == (sizeof(struct intel_perf_record_device_info) +
+						sizeof(*header)));
+			break;
+		}
+
+		case INTEL_PERF_RECORD_TYPE_DEVICE_TOPOLOGY: {
+			reader->record_topology = header + 1;
+			break;
+		}
+
+		case INTEL_PERF_RECORD_TYPE_TIMESTAMP_CORRELATION: {
+			append_timestamp_correlation(reader,
+						     (const struct intel_perf_record_timestamp_correlation *) (header + 1));
+			break;
+		}
+		}
+
+		iter += header->size;
+	}
+
+	if (!reader->record_info ||
+	    !reader->record_topology) {
+		snprintf(reader->error_msg, sizeof(reader->error_msg),
+			 "Invalid file, missing device or topology info");
+		return false;
+	}
+
+	record_info = reader->record_info;
+	record_topology = reader->record_topology;
+
+	devinfo = intel_get_device_info(record_info->device_id);
+	if (!devinfo) {
+		snprintf(reader->error_msg, sizeof(reader->error_msg),
+			 "Recording occured on unsupported device (0x%x)",
+			 record_info->device_id);
+		return false;
+	}
+
+	init_devinfo(&reader->devinfo, devinfo,
+		     record_info->device_id,
+		     record_info->timestamp_frequency);
+	reader->perf = intel_perf_for_devinfo(record_info->device_id,
+					      record_info->device_revision,
+					      record_info->timestamp_frequency,
+					      record_info->gt_min_frequency,
+					      record_info->gt_max_frequency,
+					      &record_topology->topology);
+
+	reader->metric_set_name = record_info->metric_set_name;
+	reader->metric_set_uuid = record_info->metric_set_uuid;
+	reader->metric_set = find_metric_set(reader->perf, record_info->metric_set_name);
+
+	return true;
+}
+
+static uint64_t
+correlate_gpu_timestamp(struct intel_perf_data_reader *reader,
+			uint64_t gpu_ts)
+{
+	/* OA reports only have the lower 32bits of the timestamp
+	 * register, while our correlation data has the whole 36bits.
+	 * Try to figure what portion of the correlation data the
+	 * 32bit timestamp belongs to.
+	 */
+	uint64_t mask = 0xffffffff;
+	int corr_idx = -1;
+
+	for (uint32_t i = 0; i < reader->n_correlation_chunks; i++) {
+		if (gpu_ts >= (reader->correlation_chunks[i].gpu_ts_begin & mask) &&
+		    gpu_ts <= (reader->correlation_chunks[i].gpu_ts_end & mask)) {
+			corr_idx = reader->correlation_chunks[i].idx;
+			break;
+		}
+	}
+
+	/* Not found? Assume prior to the first timestamp correlation.
+	 */
+	if (corr_idx < 0) {
+		return reader->correlations[0]->cpu_timestamp -
+			((reader->correlations[0]->gpu_timestamp & mask) - gpu_ts) *
+			(reader->correlations[1]->cpu_timestamp - reader->correlations[0]->cpu_timestamp) /
+			(reader->correlations[1]->gpu_timestamp - reader->correlations[0]->gpu_timestamp);
+	}
+
+	for (uint32_t i = corr_idx; i < (reader->n_correlations - 1); i++) {
+		if (gpu_ts >= (reader->correlations[i]->gpu_timestamp & mask) &&
+		    gpu_ts < (reader->correlations[i + 1]->gpu_timestamp & mask)) {
+			return reader->correlations[i]->cpu_timestamp +
+				(gpu_ts - (reader->correlations[i]->gpu_timestamp & mask)) *
+				(reader->correlations[i + 1]->cpu_timestamp - reader->correlations[i]->cpu_timestamp) /
+				(reader->correlations[i + 1]->gpu_timestamp - reader->correlations[i]->gpu_timestamp);
+		}
+	}
+
+	/* This is a bit harsh, but the recording tool should ensure we have
+	 * sampling points on either side of the bag of OA reports.
+	 */
+	assert(0);
+}
+
+static void
+append_timeline_event(struct intel_perf_data_reader *reader,
+		      uint64_t ts_start, uint64_t ts_end,
+		      uint32_t record_start, uint32_t record_end,
+		      uint32_t hw_id)
+{
+	if (reader->n_timelines >= reader->n_allocated_timelines) {
+		reader->n_allocated_timelines = MAX(100, 2 * reader->n_allocated_timelines);
+		reader->timelines =
+			(struct intel_perf_timeline_item *)
+			realloc((void *) reader->timelines,
+				reader->n_allocated_timelines *
+				sizeof(*reader->timelines));
+		assert(reader->timelines);
+	}
+
+	reader->timelines[reader->n_timelines].ts_start = ts_start;
+	reader->timelines[reader->n_timelines].ts_end = ts_end;
+	reader->timelines[reader->n_timelines].cpu_ts_start =
+		correlate_gpu_timestamp(reader, ts_start);
+	reader->timelines[reader->n_timelines].cpu_ts_end =
+		correlate_gpu_timestamp(reader, ts_end);
+	reader->timelines[reader->n_timelines].record_start = record_start;
+	reader->timelines[reader->n_timelines].record_end = record_end;
+	reader->timelines[reader->n_timelines].hw_id = hw_id;
+	reader->n_timelines++;
+}
+
+static void
+generate_cpu_events(struct intel_perf_data_reader *reader)
+{
+	uint32_t last_header_idx = 0;
+	const struct drm_i915_perf_record_header *last_header = reader->records[0];
+
+	for (uint32_t i = 1; i < reader->n_records; i++) {
+		const struct drm_i915_perf_record_header *current_header =
+			reader->records[i];
+		const uint8_t *start_report = (const uint8_t *) (last_header + 1),
+			*end_report = (const uint8_t *) (current_header + 1);
+		uint32_t last_ctx_id = oa_report_ctx_id(&reader->devinfo, start_report),
+			current_ctx_id = oa_report_ctx_id(&reader->devinfo, end_report);
+		uint64_t gpu_ts_start = oa_report_timestamp(start_report),
+			gpu_ts_end = oa_report_timestamp(end_report);
+
+		if (last_ctx_id == current_ctx_id)
+			continue;
+
+		append_timeline_event(reader, gpu_ts_start, gpu_ts_end, last_header_idx, i, last_ctx_id);
+
+		last_header = current_header;
+		last_header_idx = i;
+	}
+}
+
+static void
+compute_correlation_chunks(struct intel_perf_data_reader *reader)
+{
+	uint64_t mask = ~(0xffffffff);
+	uint32_t last_idx = 0;
+	uint64_t last_ts = reader->correlations[last_idx]->gpu_timestamp;
+
+	for (uint32_t i = 0; i < reader->n_correlations; i++) {
+		if (!reader->n_correlation_chunks ||
+		    (last_ts & mask) != (reader->correlations[i]->gpu_timestamp & mask)) {
+			assert(reader->n_correlation_chunks < ARRAY_SIZE(reader->correlation_chunks));
+			reader->correlation_chunks[reader->n_correlation_chunks].gpu_ts_begin = last_ts;
+			reader->correlation_chunks[reader->n_correlation_chunks].gpu_ts_end = last_ts | ~mask;
+			reader->correlation_chunks[reader->n_correlation_chunks].idx = last_idx;
+			last_ts = reader->correlation_chunks[reader->n_correlation_chunks].gpu_ts_end + 1;
+			last_idx = i;
+			reader->n_correlation_chunks++;
+		}
+	}
+}
+
+bool
+intel_perf_data_reader_init(struct intel_perf_data_reader *reader,
+			    int perf_file_fd)
+{
+        struct stat st;
+        if (fstat(perf_file_fd, &st) != 0) {
+		snprintf(reader->error_msg, sizeof(reader->error_msg),
+			 "Unable to access file (%s)", strerror(errno));
+		return false;
+	}
+
+	memset(reader, 0, sizeof(*reader));
+
+	reader->mmap_size = st.st_size;
+	reader->mmap_data = (const uint8_t *) mmap(NULL, st.st_size,
+						   PROT_READ, MAP_PRIVATE,
+						   perf_file_fd, 0);
+	if (reader->mmap_data == MAP_FAILED) {
+		snprintf(reader->error_msg, sizeof(reader->error_msg),
+			 "Unable to access file (%s)", strerror(errno));
+		return false;
+	}
+
+	if (!parse_data(reader))
+		return false;
+
+	compute_correlation_chunks(reader);
+	generate_cpu_events(reader);
+
+	return true;
+}
+
+void
+intel_perf_data_reader_fini(struct intel_perf_data_reader *reader)
+{
+	intel_perf_free(reader->perf);
+	free(reader->records);
+	free(reader->timelines);
+	free(reader->correlations);
+	munmap((void *)reader->mmap_data, reader->mmap_size);
+}
diff --git a/lib/i915/perf_data_reader.h b/lib/i915/perf_data_reader.h
new file mode 100644
index 00000000..f625f12d
--- /dev/null
+++ b/lib/i915/perf_data_reader.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef PERF_DATA_READER_H
+#define PERF_DATA_READER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Helper to read a i915-perf recording. */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perf.h"
+#include "perf_data.h"
+
+struct intel_perf_timeline_item {
+	uint64_t ts_start;
+	uint64_t ts_end;
+	uint64_t cpu_ts_start;
+	uint64_t cpu_ts_end;
+
+	/* Offsets into intel_perf_data_reader.records */
+	uint32_t record_start;
+	uint32_t record_end;
+
+	uint32_t hw_id;
+
+	/* User associated data with a given item on the i915 perf
+	 * timeline.
+	 */
+	void *user_data;
+};
+
+struct intel_perf_data_reader {
+	/* Array of pointers into the mmapped i915 perf file. */
+	const struct drm_i915_perf_record_header **records;
+	uint32_t n_records;
+	uint32_t n_allocated_records;
+
+	/**/
+	struct intel_perf_timeline_item *timelines;
+	uint32_t n_timelines;
+	uint32_t n_allocated_timelines;
+
+	/**/
+	const struct intel_perf_record_timestamp_correlation **correlations;
+	uint32_t n_correlations;
+	uint32_t n_allocated_correlations;
+
+	struct {
+		uint64_t gpu_ts_begin;
+		uint64_t gpu_ts_end;
+		uint32_t idx;
+	} correlation_chunks[4];
+	uint32_t n_correlation_chunks;
+
+	const char *metric_set_uuid;
+	const char *metric_set_name;
+
+	struct intel_perf_devinfo devinfo;
+
+	struct intel_perf *perf;
+	struct intel_perf_metric_set *metric_set;
+
+	char error_msg[256];
+
+	/**/
+	const void *record_info;
+	const void *record_topology;
+
+	const uint8_t *mmap_data;
+	size_t mmap_size;
+};
+
+bool intel_perf_data_reader_init(struct intel_perf_data_reader *reader,
+				 int perf_file_fd);
+void intel_perf_data_reader_fini(struct intel_perf_data_reader *reader);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* PERF_DATA_READER_H */
diff --git a/lib/meson.build b/lib/meson.build
index 1fe621ec..05c3b767 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -173,6 +173,7 @@ lib_igt_perf = declare_dependency(link_with : lib_igt_perf_build,
 
 i915_perf_files = [
   'i915/perf.c',
+  'i915/perf_data_reader.c',
 ]
 
 i915_perf_hardware = [
@@ -219,6 +220,7 @@ install_headers(
   'intel_chipset.h',
   'i915/perf.h',
   'i915/perf_data.h',
+  'i915/perf_data_reader.h',
   subdir : 'i915-perf'
 )
 
-- 
2.25.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [igt-dev] [PATCH i-g-t v2 6/6] tools: add i915-perf-reader
  2020-02-18 22:56 [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf Lionel Landwerlin
                   ` (3 preceding siblings ...)
  2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 5/6] lib/i915-perf: add i915 perf data reader Lionel Landwerlin
@ 2020-02-18 22:56 ` Lionel Landwerlin
  4 siblings, 0 replies; 6+ messages in thread
From: Lionel Landwerlin @ 2020-02-18 22:56 UTC (permalink / raw)
  To: igt-dev

Reading & printing out data recorded with i915-perf-recorder.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915/perf.c                    |  73 ++++++++
 lib/i915/perf.h                    |  11 ++
 tools/i915-perf/i915_perf_reader.c | 279 +++++++++++++++++++++++++++++
 tools/i915-perf/meson.build        |   6 +
 4 files changed, 369 insertions(+)
 create mode 100644 tools/i915-perf/i915_perf_reader.c

diff --git a/lib/i915/perf.c b/lib/i915/perf.c
index 6c92fd02..9afdadc3 100644
--- a/lib/i915/perf.c
+++ b/lib/i915/perf.c
@@ -509,3 +509,76 @@ intel_perf_load_perf_configs(struct intel_perf *perf, int drm_fd)
 		load_metric_set_config(metric_set, drm_fd);
 	}
 }
+
+static void
+accumulate_uint32(const uint32_t *report0,
+                  const uint32_t *report1,
+                  uint64_t *deltas)
+{
+	*deltas += (uint32_t)(*report1 - *report0);
+}
+
+static void
+accumulate_uint40(int a_index,
+                  const uint32_t *report0,
+                  const uint32_t *report1,
+                  uint64_t *deltas)
+{
+	const uint8_t *high_bytes0 = (uint8_t *)(report0 + 40);
+	const uint8_t *high_bytes1 = (uint8_t *)(report1 + 40);
+	uint64_t high0 = (uint64_t)(high_bytes0[a_index]) << 32;
+	uint64_t high1 = (uint64_t)(high_bytes1[a_index]) << 32;
+	uint64_t value0 = report0[a_index + 4] | high0;
+	uint64_t value1 = report1[a_index + 4] | high1;
+	uint64_t delta;
+
+	if (value0 > value1)
+		delta = (1ULL << 40) + value1 - value0;
+	else
+		delta = value1 - value0;
+
+	*deltas += delta;
+}
+
+void intel_perf_accumulate_reports(struct intel_perf_accumulator *acc,
+				   int oa_format,
+				   const struct drm_i915_perf_record_header *record0,
+				   const struct drm_i915_perf_record_header *record1)
+{
+	const uint32_t *start = (const uint32_t *)(record0 + 1);
+	const uint32_t *end = (const uint32_t *)(record1 + 1);
+	uint64_t *deltas = acc->deltas;
+	int idx = 0;
+	int i;
+
+	memset(acc, 0, sizeof(*acc));
+
+	switch (oa_format) {
+	case I915_OA_FORMAT_A32u40_A4u32_B8_C8:
+		accumulate_uint32(start + 1, end + 1, deltas + idx++); /* timestamp */
+		accumulate_uint32(start + 3, end + 3, deltas + idx++); /* clock */
+
+		/* 32x 40bit A counters... */
+		for (i = 0; i < 32; i++)
+			accumulate_uint40(i, start, end, deltas + idx++);
+
+		/* 4x 32bit A counters... */
+		for (i = 0; i < 4; i++)
+			accumulate_uint32(start + 36 + i, end + 36 + i, deltas + idx++);
+
+		/* 8x 32bit B counters + 8x 32bit C counters... */
+		for (i = 0; i < 16; i++)
+			accumulate_uint32(start + 48 + i, end + 48 + i, deltas + idx++);
+		break;
+
+	case I915_OA_FORMAT_A45_B8_C8:
+		accumulate_uint32(start + 1, end + 1, deltas); /* timestamp */
+
+		for (i = 0; i < 61; i++)
+			accumulate_uint32(start + 3 + i, end + 3 + i, deltas + 1 + i);
+		break;
+	default:
+		assert(0);
+	}
+
+}
diff --git a/lib/i915/perf.h b/lib/i915/perf.h
index 246d06cf..00ac2f6f 100644
--- a/lib/i915/perf.h
+++ b/lib/i915/perf.h
@@ -114,6 +114,11 @@ typedef enum {
 	INTEL_PERF_LOGICAL_COUNTER_UNIT_MAX
 } intel_perf_logical_counter_unit_t;
 
+/* Hold deltas of raw performance counters. */
+struct intel_perf_accumulator {
+#define INTEL_PERF_MAX_RAW_OA_COUNTERS 62
+	uint64_t deltas[INTEL_PERF_MAX_RAW_OA_COUNTERS];
+};
 
 struct intel_perf;
 struct intel_perf_metric_set;
@@ -202,6 +207,7 @@ struct intel_perf {
 	struct intel_perf_devinfo devinfo;
 };
 
+struct drm_i915_perf_record_header;
 struct drm_i915_query_topology_info;
 
 struct intel_perf *intel_perf_for_fd(int drm_fd);
@@ -222,6 +228,11 @@ void intel_perf_add_metric_set(struct intel_perf *perf,
 
 void intel_perf_load_perf_configs(struct intel_perf *perf, int drm_fd);
 
+void intel_perf_accumulate_reports(struct intel_perf_accumulator *acc,
+				   int oa_format,
+				   const struct drm_i915_perf_record_header *record0,
+				   const struct drm_i915_perf_record_header *record1);
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/tools/i915-perf/i915_perf_reader.c b/tools/i915-perf/i915_perf_reader.c
new file mode 100644
index 00000000..fd944b45
--- /dev/null
+++ b/tools/i915-perf/i915_perf_reader.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2020 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <i915_drm.h>
+
+#include "i915/perf.h"
+#include "i915/perf_data_reader.h"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) > (b) ? (b) : (a))
+
+static void
+usage(void)
+{
+	printf("Usage: i915-perf-reader [options] file\n"
+	       "Reads the content of an i915-perf recording.\n"
+	       "\n"
+	       "     --help,    -h             Print this screen\n"
+	       "     --counters, -c c1,c2,...  List of counters to display values for.\n"
+	       "                               Use 'all' to display all counters.\n"
+	       "                               Use 'list' to list available counters.\n");
+}
+
+static struct intel_perf_logical_counter *
+find_counter(struct intel_perf_metric_set *metric_set,
+	     const char *name)
+{
+	for (uint32_t i = 0; i < metric_set->n_counters; i++) {
+		if (!strcmp(name, metric_set->counters[i].symbol_name)) {
+			return &metric_set->counters[i];
+		}
+	}
+
+	return NULL;
+}
+
+static void
+append_counter(struct intel_perf_logical_counter ***counters,
+	       int32_t *n_counters,
+	       uint32_t *n_allocated_counters,
+	       struct intel_perf_logical_counter *counter)
+{
+	if (*n_counters < *n_allocated_counters) {
+		(*counters)[(*n_counters)++] = counter;
+		return;
+	}
+
+	*n_allocated_counters = MAX(2, *n_allocated_counters * 2);
+	*counters = realloc(*counters,
+			    sizeof(struct intel_perf_logical_counter *) *
+			    (*n_allocated_counters));
+	(*counters)[(*n_counters)++] = counter;
+}
+
+static struct intel_perf_logical_counter **
+get_logical_counters(struct intel_perf_metric_set *metric_set,
+		     const char *counter_list,
+		     int32_t *out_n_counters)
+{
+	struct intel_perf_logical_counter **counters = NULL, *counter;
+	uint32_t n_allocated_counters = 0;
+	const char *current, *next;
+	char counter_name[100];
+
+	if (!counter_list) {
+		*out_n_counters = 0;
+		return NULL;
+	}
+
+	if (!strcmp(counter_list, "list")) {
+		uint32_t longest_name = 0;
+
+		*out_n_counters = -1;
+		for (uint32_t i = 0; i < metric_set->n_counters; i++) {
+			longest_name = MAX(longest_name,
+					   strlen(metric_set->counters[i].symbol_name));
+		}
+
+		fprintf(stdout, "Available counters:\n");
+		for (uint32_t i = 0; i < metric_set->n_counters; i++) {
+			fprintf(stdout, "%s:%*s%s\n",
+				metric_set->counters[i].symbol_name,
+				(int)(longest_name -
+				      strlen(metric_set->counters[i].symbol_name) + 1), " ",
+				metric_set->counters[i].name);
+		}
+		return NULL;
+	}
+
+	if (!strcmp(counter_list, "all")) {
+		counters = malloc(sizeof(*counters) * metric_set->n_counters);
+		*out_n_counters = metric_set->n_counters;
+		for (uint32_t i = 0; i < metric_set->n_counters; i++)
+			counters[i] = &metric_set->counters[i];
+		return counters;
+	}
+
+	*out_n_counters = 0;
+	current = counter_list;
+	while ((next = strstr(current, ","))) {
+		snprintf(counter_name,
+			 MIN((uint32_t)(next - current) + 1, sizeof(counter_name)),
+			 "%s", current);
+
+		counter = find_counter(metric_set, counter_name);
+		if (!counter) {
+			fprintf(stderr, "Unknown counter '%s'.\n", counter_name);
+			free(counters);
+			*out_n_counters = -1;
+			return NULL;
+		}
+
+		append_counter(&counters, out_n_counters, &n_allocated_counters, counter);
+
+		current = next + 1;
+	}
+
+	if (strlen(current) > 0) {
+		counter = find_counter(metric_set, current);
+		if (!counter) {
+			fprintf(stderr, "Unknown counter '%s'.\n", current);
+			free(counters);
+			*out_n_counters = -1;
+			return NULL;
+		}
+
+		append_counter(&counters, out_n_counters, &n_allocated_counters, counter);
+	}
+
+	return counters;
+}
+
+int
+main(int argc, char *argv[])
+{
+	const struct option long_options[] = {
+		{"help",             no_argument, 0, 'h'},
+		{"counters",   required_argument, 0, 'c'},
+		{0, 0, 0, 0}
+	};
+	struct intel_perf_data_reader reader;
+	struct intel_perf_logical_counter **counters;
+	const char *counter_names = NULL;
+	int32_t n_counters;
+	int fd, opt;
+
+	while ((opt = getopt_long(argc, argv, "hc:", long_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		case 'c':
+			counter_names = optarg;
+			break;
+		default:
+			fprintf(stderr, "Internal error: "
+				"unexpected getopt value: %d\n", opt);
+			usage();
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "No recording file specified.\n");
+		return EXIT_FAILURE;
+	}
+
+	fd = open(argv[optind], 0, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot open '%s': %s.\n",
+			argv[optind], strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	if (!intel_perf_data_reader_init(&reader, fd)) {
+		fprintf(stderr, "Unable to parse '%s': %s.\n",
+			argv[optind], reader.error_msg);
+		return EXIT_FAILURE;
+	}
+
+	counters = get_logical_counters(reader.metric_set, counter_names, &n_counters);
+	if (n_counters < 0)
+		goto exit;
+
+	fprintf(stdout, "Recorded on device=0x%x gen=%i\n",
+		reader.devinfo.devid, reader.devinfo.gen);
+	fprintf(stdout, "Metric used : %s (%s) uuid=%s\n",
+		reader.metric_set->symbol_name, reader.metric_set->name,
+		reader.metric_set->hw_config_guid);
+	fprintf(stdout, "Reports: %u\n", reader.n_records);
+	fprintf(stdout, "Context switches: %u\n", reader.n_timelines);
+	fprintf(stdout, "Timestamp correlation points: %u\n", reader.n_correlations);
+
+	if (strcmp(reader.metric_set_uuid, reader.metric_set->hw_config_guid)) {
+		fprintf(stdout,
+			"WARNING: Recording used a different HW configuration.\n"
+			"WARNING: This could lead to inconsistent counter values.\n");
+	}
+
+	for (uint32_t i = 0; i < reader.n_timelines; i++) {
+		const struct intel_perf_timeline_item *item = &reader.timelines[i];
+		const struct drm_i915_perf_record_header *i915_report0 =
+			reader.records[item->record_start];
+		const struct drm_i915_perf_record_header *i915_report1 =
+			reader.records[item->record_end];
+		struct intel_perf_accumulator accu;
+
+		fprintf(stdout, "Time: CPU=0x%016" PRIx64 "-0x%016" PRIx64
+			" GPU=0x%016" PRIx64 "-0x%016" PRIx64"\n",
+			item->cpu_ts_start, item->cpu_ts_end,
+			item->ts_start, item->ts_end);
+		fprintf(stdout, "hw_id=0x%x %s\n",
+			item->hw_id, item->hw_id == 0xffffffff ? "(idle)" : "");
+
+		intel_perf_accumulate_reports(&accu, reader.metric_set->perf_oa_format,
+					      i915_report0, i915_report1);
+
+		for (uint32_t c = 0; c < n_counters; c++) {
+			struct intel_perf_logical_counter *counter = counters[c];
+
+			switch (counter->storage) {
+			case INTEL_PERF_LOGICAL_COUNTER_STORAGE_UINT64:
+			case INTEL_PERF_LOGICAL_COUNTER_STORAGE_UINT32:
+			case INTEL_PERF_LOGICAL_COUNTER_STORAGE_BOOL32:
+				fprintf(stdout, "   %s: %" PRIu64 "\n",
+					counter->symbol_name, counter->read_uint64(reader.perf,
+										   reader.metric_set,
+										   accu.deltas));
+				break;
+			case INTEL_PERF_LOGICAL_COUNTER_STORAGE_DOUBLE:
+			case INTEL_PERF_LOGICAL_COUNTER_STORAGE_FLOAT:
+				fprintf(stdout, "   %s: %f\n",
+					counter->symbol_name, counter->read_float(reader.perf,
+										  reader.metric_set,
+										  accu.deltas));
+				break;
+			}
+		}
+	}
+
+ exit:
+	intel_perf_data_reader_fini(&reader);
+	close(fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/tools/i915-perf/meson.build b/tools/i915-perf/meson.build
index 9884dfd9..bc2d8f39 100644
--- a/tools/i915-perf/meson.build
+++ b/tools/i915-perf/meson.build
@@ -14,3 +14,9 @@ executable('i915-perf-control',
            [ 'i915_perf_control.c' ],
            include_directories: inc,
            install: true)
+
+executable('i915-perf-reader',
+           [ 'i915_perf_reader.c' ],
+           include_directories: inc,
+           dependencies: lib_igt_i915_perf,
+           install: true)
-- 
2.25.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-02-18 22:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-18 22:56 [igt-dev] [PATCH i-g-t v2 0/6] New performance recording/replay tools for i915-perf Lionel Landwerlin
2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 1/6] lib/intel_chipset: identify Elkhart Lake Lionel Landwerlin
2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 3/6] lib/i915-perf: Add support for loading perf configurations Lionel Landwerlin
2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 4/6] tools: add i915 perf recorder tool Lionel Landwerlin
2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 5/6] lib/i915-perf: add i915 perf data reader Lionel Landwerlin
2020-02-18 22:56 ` [igt-dev] [PATCH i-g-t v2 6/6] tools: add i915-perf-reader Lionel Landwerlin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.