All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] perf: add new uncore command
@ 2013-01-31 13:54 Stephane Eranian
  2013-01-31 13:54 ` [PATCH 1/2] perf tools: add cpu_map__get_socket() Stephane Eranian
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Stephane Eranian @ 2013-01-31 13:54 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, ak, acme, jolsa, namhyung.kim

This patchset adds a new command to perf: perf uncore.
It is used to measure processor socket-level metrics
on a system-wide basis and at all priv levels.

The command comes with a set of predefined key metrics
which are useful to measure multi-socket system imbalance
and various bandwidths.

The following metrics are currently defined:
  - memory bandwidth (Nehalem, Westmere, SandyBridge-EP)
  - PCIe bandwidth (SandyBridge-EP)
  - QPI bandwidth (SandyBridge-EP)
  - C-state residency (SandyBridge-EP)

Others can be added in the future.

The command provides options to modify the unit of the metrics
(default: MB/s for bandwidth).

Example on Nehalem:

 # perf uncore
 #------------------------------
 #           Socket0           |
 #------------------------------
 #        RAM Bandwidth        |
 #            Wr             Rd|
 #          MB/s           MB/s|
 #------------------------------
         4954.99       14897.29
         4953.97       14894.56
         4947.52       14874.97

To make plotting easier, the output can be augmented with a timestamp:

 # perf uncore -T
 #----------------------------------------
 #         |           Socket0           |
 #         |------------------------------
 #   Time  |        RAM Bandwidth        |
 #    in   |            Wr             Rd|
 #   secs  |          MB/s           MB/s|
 #----------------------------------------
          1        4952.50       14890.49
          2        4955.55       14900.19
          3        4949.13       14879.60
          4        4954.66       14896.26

The code is split between generic layer (builtin-uncore.c) and
an arch specific layer (arch/*/util/uncore.c). All events are
hardcoded because they don't change for a given processor and
metric computation requires combining multiple events.

Signed-off-by: Stephane Eranian <eranian@google.com>
---

Stephane Eranian (2):
  perf tools: add cpu_map__get_socket()
  perf: add new perf uncore command

 tools/perf/Documentation/perf-uncore.txt |   88 ++++
 tools/perf/Makefile                      |    1 +
 tools/perf/arch/x86/Makefile             |    1 +
 tools/perf/arch/x86/util/uncore.c        |  539 ++++++++++++++++++++++
 tools/perf/builtin-uncore.c              |  739 ++++++++++++++++++++++++++++++
 tools/perf/builtin.h                     |    1 +
 tools/perf/command-list.txt              |    1 +
 tools/perf/perf.c                        |    1 +
 tools/perf/util/cpumap.c                 |   26 ++
 tools/perf/util/cpumap.h                 |    1 +
 tools/perf/util/uncore.h                 |   56 +++
 11 files changed, 1454 insertions(+)
 create mode 100644 tools/perf/Documentation/perf-uncore.txt
 create mode 100644 tools/perf/arch/x86/util/uncore.c
 create mode 100644 tools/perf/builtin-uncore.c
 create mode 100644 tools/perf/util/uncore.h

-- 
1.7.10.4


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

* [PATCH 1/2] perf tools: add cpu_map__get_socket()
  2013-01-31 13:54 [PATCH 0/2] perf: add new uncore command Stephane Eranian
@ 2013-01-31 13:54 ` Stephane Eranian
  2013-01-31 13:54 ` [PATCH 2/2] perf: add new perf uncore command Stephane Eranian
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2013-01-31 13:54 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, ak, acme, jolsa, namhyung.kim

To get the socket number associated with a CPU.
Useful for uncore output.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/util/cpumap.c |   26 ++++++++++++++++++++++++++
 tools/perf/util/cpumap.h |    1 +
 2 files changed, 27 insertions(+)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 2b32ffa..adb2e9c 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -1,4 +1,5 @@
 #include "util.h"
+#include "sysfs.h"
 #include "../perf.h"
 #include "cpumap.h"
 #include <assert.h>
@@ -201,3 +202,28 @@ void cpu_map__delete(struct cpu_map *map)
 {
 	free(map);
 }
+
+int cpu_map__get_socket(struct cpu_map *map, int idx)
+{
+	FILE *fp;
+	char path[PATH_MAX];
+	const char *sysfs;
+	int cpu, ret;
+
+	if (idx > map->nr)
+		return -1;
+
+	cpu = map->map[idx];
+
+	sysfs = sysfs_find_mountpoint();
+	if (!sysfs)
+		return -1;
+
+	sprintf(path, "%s/devices/system/cpu/cpu%d/topology/physical_package_id", sysfs, cpu);
+	fp = fopen(path, "r");
+	if (!fp)
+		return -1;
+	ret = fscanf(fp, "%d", &cpu);
+	fclose(fp);
+	return ret == 1 ? cpu : -1;
+}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 2f68a3b..0178e49 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -14,6 +14,7 @@ struct cpu_map *cpu_map__dummy_new(void);
 void cpu_map__delete(struct cpu_map *map);
 struct cpu_map *cpu_map__read(FILE *file);
 size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
+int cpu_map__get_socket(struct cpu_map *map, int idx);
 
 static inline int cpu_map__nr(const struct cpu_map *map)
 {
-- 
1.7.10.4


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

* [PATCH 2/2] perf: add new perf uncore command
  2013-01-31 13:54 [PATCH 0/2] perf: add new uncore command Stephane Eranian
  2013-01-31 13:54 ` [PATCH 1/2] perf tools: add cpu_map__get_socket() Stephane Eranian
@ 2013-01-31 13:54 ` Stephane Eranian
  2013-02-01  8:45   ` Namhyung Kim
  2013-01-31 14:21 ` [PATCH 0/2] perf: add new " Ingo Molnar
  2013-02-01  8:42 ` Namhyung Kim
  3 siblings, 1 reply; 9+ messages in thread
From: Stephane Eranian @ 2013-01-31 13:54 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, ak, acme, jolsa, namhyung.kim

The perf uncore command measures key metrics at the processor
socket level for select Intel processors. It operates like
perf stat, except it prints metrics directly instead of raw
event values. The metrics are printed at regular time intervals.

The defined metrics are:
  - memory bandwidth (Nehalem, Westmere, SandyBridge-EP)
  - PCIe bandwidth (SandyBridge-EP)
  - QPI bandwidth (SandyBridge-EP)
  - C-state residency (SandyBridge-EP)

Running perf uncore requires root pivilege as all the measurements
are done in system-wide mode. Note that metrics are collected
at the socket-level and at all privilege level.

The command provides options to modify the unit of the metrics
(default: MB/s for bandwidth).

Example on Nehalem:
 # perf uncore
 #------------------------------
 #           Socket0           |
 #------------------------------
 #        RAM Bandwidth        |
 #            Wr             Rd|
 #          MB/s           MB/s|
 #------------------------------
         4954.99       14897.29
         4953.97       14894.56
         4947.52       14874.97

To make plotting easier, the output can be augmented with a timestamp:

 # perf uncore -T
 #----------------------------------------
 #         |           Socket0           |
 #         |------------------------------
 #   Time  |        RAM Bandwidth        |
 #    in   |            Wr             Rd|
 #   secs  |          MB/s           MB/s|
 #----------------------------------------
          1        4952.50       14890.49
          2        4955.55       14900.19
          3        4949.13       14879.60
          4        4954.66       14896.26

The code is split between generic layer (builtin-uncore.c) and
an arch specific layer (arch/*/util/uncore.c). All events are
hardcoded because they don't change for a given processor and
metric computation requires combining multiple events.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/Documentation/perf-uncore.txt |   88 ++++
 tools/perf/Makefile                      |    1 +
 tools/perf/arch/x86/Makefile             |    1 +
 tools/perf/arch/x86/util/uncore.c        |  539 ++++++++++++++++++++++
 tools/perf/builtin-uncore.c              |  739 ++++++++++++++++++++++++++++++
 tools/perf/builtin.h                     |    1 +
 tools/perf/command-list.txt              |    1 +
 tools/perf/perf.c                        |    1 +
 tools/perf/util/uncore.h                 |   56 +++
 9 files changed, 1427 insertions(+)
 create mode 100644 tools/perf/Documentation/perf-uncore.txt
 create mode 100644 tools/perf/arch/x86/util/uncore.c
 create mode 100644 tools/perf/builtin-uncore.c
 create mode 100644 tools/perf/util/uncore.h

diff --git a/tools/perf/Documentation/perf-uncore.txt b/tools/perf/Documentation/perf-uncore.txt
new file mode 100644
index 0000000..f87f86b
--- /dev/null
+++ b/tools/perf/Documentation/perf-uncore.txt
@@ -0,0 +1,88 @@
+perf-uncore(1)
+===============
+
+NAME
+----
+perf-uncore - Collect processor socket-level metrics
+
+SYNOPSIS
+--------
+[verse]
+'perf uncore' [-M metrics] [-m unit] [-L] <command>
+
+DESCRIPTION
+-----------
+This command runs a binary and gathers system-wide socket-level performance counter
+statistics. Depending on the perf_event security settings, it may be required to
+have super user privliege to run this command as it operates in system-wide monitoring
+mode.  This command may not work on all processors.
+
+The list of predefined metrics is dependent on the host processor. Use the -L option
+to list the metrics available. By default all the metrics are measured across all the
+processor sockets. It is possible to restrict the certain metrics using the -M option.
+
+The output operates in a way similar to vmstat. One line of output per second. The
+interval can be adjusted with the -I option.
+
+It is possible to add a Time column to make it easier to plot the values later on.
+The -T option enables the Time column.
+
+OPTIONS
+-------
+<command>...::
+	Any command you can specify in a shell. To control the duration of the
+	measurement without influencing too much, the sleep command can be used.
+
+-M::
+--metrics metrics[, metrics]::
+        Comma separated list of metrics to measure. Metrics' name can be obtained
+	via the -L option.
+-m::
+--mem-unit unit::
+        Modify the bandwidth metric unit. The default bandwidth metric is Megabytes
+        (1000*1000 bytes). Bytes (B) and bits (b) metrics are supported.  The available
+        metrics are: KB, MB, GB, KiB, MiB, GiB, Kb, Mb, Gb, Kib, Mib, Gib.
+        The metrics are obviously case sensitive.
+
+-T::
+--time::
+        Add a Time column expressed in seconds (default: off).
+
+-v::
+--verbose::
+        be more verbose (show counter open errors, etc)
+
+-o file::
+--output file::
+	Print the output into the designated file.
+
+--append::
+	Append to the output file designated with the -o option. Ignored if -o is not specified.
+
+--log-fd::
+	Log output to fd, instead of stderr.  Complementary to --output, and mutually exclusive
+	with it.  --append may be used here.  Examples:
+	3>results  perf stat --log-fd 3          -- $cmd
+	3>>results perf stat --log-fd 3 --append -- $cmd
+
+EXAMPLES
+--------
+
+[verse]
+$ perf uncore -T sleep 5
+#----------------------------------------
+#         |           Socket0           |
+#         |------------------------------
+#   Time  |        RAM Bandwidth        |
+#    in   |            Wr             Rd|
+#   secs  |          MB/s           MB/s|
+#----------------------------------------
+         1        1254.23        3781.76
+         2        1265.19        3813.70
+         3        1261.62        3803.81
+         4        1265.28        3814.09
+         5        1253.54        3780.13
+
+SEE ALSO
+--------
+linkperf:perf-stat[1]
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index a84021a..cd1f9fa 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -520,6 +520,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
 BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
 BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
 BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o
+BUILTIN_OBJS += $(OUTPUT)builtin-uncore.o
 
 PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
 
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile
index 815841c..597303d 100644
--- a/tools/perf/arch/x86/Makefile
+++ b/tools/perf/arch/x86/Makefile
@@ -6,3 +6,4 @@ ifndef NO_LIBUNWIND
 LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
 endif
 LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/uncore.o
diff --git a/tools/perf/arch/x86/util/uncore.c b/tools/perf/arch/x86/util/uncore.c
new file mode 100644
index 0000000..b720d74
--- /dev/null
+++ b/tools/perf/arch/x86/util/uncore.c
@@ -0,0 +1,539 @@
+/*
+ * builtin-uncore.c: measure key metrics at processor socket level
+ *
+ * Contributed by: Stephane Eranian <eranian@google.com>
+ *
+ * Released under the GPL v2
+ */
+#include "../../perf.h"
+#include "../../util/util.h"
+#include "../../util/event.h"
+#include "../../util/evlist.h"
+#include "../../util/evsel.h"
+#include "../../util/uncore.h"
+
+extern int uncore_init_metrics(void);
+
+static int x86_family;
+static int x86_model;
+
+static void
+print_bw_hdr(FILE *output, const struct uncore_metric *m, int line,
+	     const char *type)
+{
+	int len = hdr_width(m);
+	int f, r;
+	int mlen;
+
+	len--;
+
+	switch (line) {
+		case 2:
+			mlen = strlen(type);
+			f = (len - 10 - mlen) / 2;
+			r = (len - 10 - mlen) % 2;
+			fprintf(output, "%*s Bandwidth%*s",
+				mlen + f, type,
+				f+1+r, "|");
+			break;
+		case 3:
+			f = (len - 6*2 - 1) / 2;
+			fprintf(output, "%*s %*s|",
+				6 + f, "Wr",
+				6 + f, "Rd");
+			break;
+		case 4:
+			/* use 7 instead of 5 to align with values */
+			f = (len - 5*2 -1) / 2;
+			fprintf(output, "%*s/s %*s/s|",
+				3 + f, uncore_cfg.mem_unit->name,
+				3 + f, uncore_cfg.mem_unit->name);
+			break;
+		default:
+			fprintf(output, "%*s", len, "|");
+	}
+}
+
+/*
+ * nrd = number of events for read bw
+ * nwr = number of events for write bw
+ * csize = event unit size (e.g., cacheline size)
+ */
+static void
+print_bw(FILE *output, const struct uncore_metric *m,
+	 int cpu, int nrd, int nwr, int csize)
+{
+	struct uncore_metric_event *e;
+	struct perf_evsel *evsel;
+	double rd_bw = 0, wr_bw = 0;
+	uint64_t rd, wr;
+	uint64_t unit;
+	int i, f, r;
+	int len = hdr_width(m);
+
+	evsel = &m->evts[0].evsel;
+
+	unit = uncore_cfg.mem_unit->num_bits;
+	/* events here are all counting bytes */
+	unit >>= 3;
+
+	/*
+	 * len is inclusive of border
+	 * no column marker for values, so cut short by 1
+	 * to keep alignment
+	 */
+	len--;
+
+	/*
+	 * 7 = .00 + .00
+	 */
+	f = (len - 6 - 1) / 2;
+	r = (len - 6 - 1) % 2;
+
+	e = m->evts;
+	rd = wr = 0;
+	for (i = 0; i < nrd; i++) {
+		evsel = &e[i].evsel;
+		rd += evsel->counts->cpu[cpu].val;
+	}
+	for (i = nrd; i < (nrd + nwr); i++) {
+		evsel = &e[i].evsel;
+		wr += evsel->counts->cpu[cpu].val;
+	}
+
+	if (uncore_cfg.interval) {
+		wr_bw = (double)wr * csize / (uncore_cfg.interval * unit);
+		rd_bw = (double)rd * csize / (uncore_cfg.interval * unit);
+	}
+
+	fprintf(output, "%*.2f ", 3+f, wr_bw);
+	fprintf(output, "%*.2f %*s", 3+f, rd_bw, r, r ? " " : "");
+}
+
+
+/* cannot be const attr.type modified */
+static struct uncore_metric_event nhm_mem[] = {
+	[0] = CFG(uncore, 0x0163, nhm_unc::UNC_DRAM_READ_CAS:CH0),
+	[1] = CFG(uncore, 0x0463, nhm_unc::UNC_DRAM_READ_CAS:CH1),
+	[2] = CFG(uncore, 0x1063, nhm_unc::UNC_DRAM_READ_CAS:CH2),
+	[3] = CFG(uncore, 0x0164, nhm_unc::UNC_DRAM_WRITE_CAS:CH0),
+	[4] = CFG(uncore, 0x0464, nhm_unc::UNC_DRAM_WRITE_CAS:CH1),
+	[5] = CFG(uncore, 0x1064, nhm_unc::UNC_DRAM_WRITE_CAS:CH2),
+};
+
+static void
+print_nhm_mem_bw_hdr(FILE *output, const struct uncore_metric *m, int line)
+{
+	print_bw_hdr(output, m, line, "RAM");
+}
+
+static void
+print_nhm_mem_bw(FILE *output, const struct uncore_metric *m, int cpu)
+{
+	print_bw(output, m, cpu, 3, 3, 64);
+}
+
+static const struct uncore_metric uncore_nhm_mem_bw = {
+	.name = "mem_bw",
+	.desc = "Nehalem MEM Bandwidth",
+	.print = print_nhm_mem_bw,
+	.print_hdr = print_nhm_mem_bw_hdr,
+	.evts = nhm_mem,
+	.nr_evts = ARRAY_SIZE(nhm_mem),
+	.hdr_width = 30,
+	.num_cols = 2,
+};
+
+/* cannot be const attr.type modified */
+static struct uncore_metric_event snbep_mem[] = {
+	[0] = CFG(uncore_imc_0, 0x0304, snbep_unc_imc0::UNC_M_CAS_COUNT:RD),
+	[1] = CFG(uncore_imc_1, 0x0304, snbep_unc_imc1::UNC_M_CAS_COUNT:RD),
+	[2] = CFG(uncore_imc_0, 0x0c04, snbep_unc_imc0::UNC_M_CAS_COUNT:WR),
+	[3] = CFG(uncore_imc_1, 0x0c04, snbep_unc_imc1::UNC_M_CAS_COUNT:WR),
+};
+
+static void
+print_snbep_mem_bw_hdr(FILE *output, const struct uncore_metric *m, int line)
+{
+	print_bw_hdr(output, m, line, "RAM");
+}
+
+static void
+print_snbep_mem_bw(FILE *output, const struct uncore_metric *m, int cpu)
+{
+	return print_bw(output, m, cpu, 2, 2, 64);
+}
+
+static const struct uncore_metric uncore_snbep_mem_bw = {
+	.name = "mem_bw",
+	.desc = "SNB-EP MEM Bandwidth",
+	.print = print_snbep_mem_bw,
+	.print_hdr = print_snbep_mem_bw_hdr,
+	.evts = snbep_mem,
+	.nr_evts = ARRAY_SIZE(snbep_mem),
+	.hdr_width = 30,
+	.num_cols = 2,
+};
+
+/* cannot be const attr.type modified */
+static struct uncore_metric_event snbep_qpi[] = {
+	[0] = CFG(uncore_qpi_0, 0x0201, snbep_unc_qpi0::UNC_Q_RXL_FLITS_G0:DATA),
+	[1] = CFG(uncore_qpi_1, 0x0201, snbep_unc_qpi1::UNC_Q_RXL_FLITS_G0:DATA),
+	[2] = CFG(uncore_qpi_0, 0x0200, snbep_unc_qpi0::UNC_Q_TXL_FLITS_G0:DATA),
+	[3] = CFG(uncore_qpi_1, 0x0200, snbep_unc_qpi1::UNC_Q_TXL_FLITS_G0:DATA),
+};
+
+static void
+print_snbep_qpi_bw_hdr(FILE *output, const struct uncore_metric *m, int line)
+{
+	print_bw_hdr(output, m, line, "QPI");
+}
+
+static void
+print_snbep_qpi_bw(FILE *output, const struct uncore_metric *m, int cpu)
+{
+	return print_bw(output, m, cpu, 2, 2, 8);
+}
+
+static const struct uncore_metric uncore_snbep_qpi_bw = {
+	.name = "qpi_bw",
+	.desc = "SNB-EP QPI Bandwidth",
+	.print = print_snbep_qpi_bw,
+	.print_hdr = print_snbep_qpi_bw_hdr,
+	.evts = snbep_qpi,
+	.nr_evts = ARRAY_SIZE(snbep_qpi),
+	.hdr_width = 30,
+	.num_cols = 2,
+};
+
+/* cannot be const because attr.type modified */
+static struct uncore_metric_event snbep_pcie[] = {
+	[0]  = CFG(uncore_cbox_0, 0x135, snbep_unc_cbo0::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[1]  = CFG(uncore_cbox_0, 0x135, snbep_unc_cbo0::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[2]  = CFG(uncore_cbox_0, 0x135, snbep_unc_cbo0::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[3]  = CFG(uncore_cbox_1, 0x135, snbep_unc_cbo1::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[4]  = CFG(uncore_cbox_1, 0x135, snbep_unc_cbo1::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[5]  = CFG(uncore_cbox_1, 0x135, snbep_unc_cbo1::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[6]  = CFG(uncore_cbox_2, 0x135, snbep_unc_cbo2::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[7]  = CFG(uncore_cbox_2, 0x135, snbep_unc_cbo2::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[8]  = CFG(uncore_cbox_2, 0x135, snbep_unc_cbo2::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[9]  = CFG(uncore_cbox_3, 0x135, snbep_unc_cbo3::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[10] = CFG(uncore_cbox_3, 0x135, snbep_unc_cbo3::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[11] = CFG(uncore_cbox_3, 0x135, snbep_unc_cbo3::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[12] = CFG(uncore_cbox_4, 0x135, snbep_unc_cbo4::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[13] = CFG(uncore_cbox_4, 0x135, snbep_unc_cbo4::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[14] = CFG(uncore_cbox_4, 0x135, snbep_unc_cbo4::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[15] = CFG(uncore_cbox_5, 0x135, snbep_unc_cbo5::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[16] = CFG(uncore_cbox_5, 0x135, snbep_unc_cbo5::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[17] = CFG(uncore_cbox_5, 0x135, snbep_unc_cbo5::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[18] = CFG(uncore_cbox_6, 0x135, snbep_unc_cbo6::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[19] = CFG(uncore_cbox_6, 0x135, snbep_unc_cbo6::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[20] = CFG(uncore_cbox_6, 0x135, snbep_unc_cbo6::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+	[21] = CFG(uncore_cbox_7, 0x135, snbep_unc_cbo7::unc_c_tor_inserts:opcode:OPC_PCIRDCUR),
+	[22] = CFG(uncore_cbox_7, 0x135, snbep_unc_cbo7::unc_c_tor_inserts:opcode:OPC_PCIPRD),
+	[23] = CFG(uncore_cbox_7, 0x135, snbep_unc_cbo7::unc_c_tor_inserts:opcode:OPC_PCINSRD),
+
+	[24] = CFG(uncore_cbox_0, 0x135, snbep_unc_cbo0::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[25] = CFG(uncore_cbox_0, 0x135, snbep_unc_cbo0::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[26] = CFG(uncore_cbox_0, 0x135, snbep_unc_cbo0::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[27] = CFG(uncore_cbox_1, 0x135, snbep_unc_cbo1::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[28] = CFG(uncore_cbox_1, 0x135, snbep_unc_cbo1::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[29] = CFG(uncore_cbox_1, 0x135, snbep_unc_cbo1::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[30] = CFG(uncore_cbox_2, 0x135, snbep_unc_cbo2::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[31] = CFG(uncore_cbox_2, 0x135, snbep_unc_cbo2::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[32] = CFG(uncore_cbox_2, 0x135, snbep_unc_cbo2::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[33] = CFG(uncore_cbox_3, 0x135, snbep_unc_cbo3::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[34] = CFG(uncore_cbox_3, 0x135, snbep_unc_cbo3::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[35] = CFG(uncore_cbox_3, 0x135, snbep_unc_cbo3::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[36] = CFG(uncore_cbox_4, 0x135, snbep_unc_cbo4::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[37] = CFG(uncore_cbox_4, 0x135, snbep_unc_cbo4::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[38] = CFG(uncore_cbox_4, 0x135, snbep_unc_cbo4::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[39] = CFG(uncore_cbox_5, 0x135, snbep_unc_cbo5::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[40] = CFG(uncore_cbox_5, 0x135, snbep_unc_cbo5::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[41] = CFG(uncore_cbox_5, 0x135, snbep_unc_cbo5::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[42] = CFG(uncore_cbox_6, 0x135, snbep_unc_cbo6::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[43] = CFG(uncore_cbox_6, 0x135, snbep_unc_cbo6::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[44] = CFG(uncore_cbox_6, 0x135, snbep_unc_cbo6::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+	[45] = CFG(uncore_cbox_7, 0x135, snbep_unc_cbo7::unc_c_tor_inserts:opcode:OPC_PCIITOM),
+	[46] = CFG(uncore_cbox_7, 0x135, snbep_unc_cbo7::unc_c_tor_inserts:opcode:OPC_PCIWILF),
+	[47] = CFG(uncore_cbox_7, 0x135, snbep_unc_cbo7::unc_c_tor_inserts:opcode:OPC_PCINSWR),
+};
+
+/*
+ * see justification for this in the comment for fixup_snbep_pcie_config1()
+ */
+static const uint64_t snbep_pcie_config1[] = {
+	[0]  = 0xcf000000,
+	[1]  = 0xca800000,
+	[2]  = 0xf2000000,
+	[3]  = 0xcf000000,
+	[4]  = 0xca800000,
+	[5]  = 0xf2000000,
+	[6]  = 0xcf000000,
+	[7]  = 0xca800000,
+	[8]  = 0xf2000000,
+	[9]  = 0xcf000000,
+	[10] = 0xca800000,
+	[11] = 0xf2000000,
+	[12] = 0xcf000000,
+	[13] = 0xca800000,
+	[14] = 0xf2000000,
+	[15] = 0xcf000000,
+	[16] = 0xca800000,
+	[17] = 0xf2000000,
+	[18] = 0xcf000000,
+	[19] = 0xca800000,
+	[20] = 0xf2000000,
+	[21] = 0xcf000000,
+	[22] = 0xca800000,
+	[23] = 0xf2000000,
+
+	[24] = 0xce000000,
+	[25] = 0xca000000,
+	[26] = 0xf2800000,
+	[27] = 0xce000000,
+	[28] = 0xca000000,
+	[29] = 0xf2800000,
+	[30] = 0xce000000,
+	[31] = 0xca000000,
+	[32] = 0xf2800000,
+	[33] = 0xce000000,
+	[34] = 0xca000000,
+	[35] = 0xf2800000,
+	[36] = 0xce000000,
+	[37] = 0xca000000,
+	[38] = 0xf2800000,
+	[39] = 0xce000000,
+	[40] = 0xca000000,
+	[41] = 0xf2800000,
+	[42] = 0xce000000,
+	[43] = 0xca000000,
+	[44] = 0xf2800000,
+	[45] = 0xce000000,
+	[46] = 0xca000000,
+	[47] = 0xf2800000,
+};
+
+static void
+print_snbep_pcie_bw_hdr(FILE *output, const struct uncore_metric *m, int line)
+{
+	print_bw_hdr(output, m, line, "PCIe");
+}
+
+static void
+print_snbep_pcie_bw(FILE *output, const struct uncore_metric *m, int cpu)
+{
+	return print_bw(output, m, cpu, 24, 24, 64);
+}
+
+static const struct uncore_metric uncore_snbep_pcie_bw = {
+	.name = "pcie_bw",
+	.desc = "SNB-EP PCIe Bandwidth",
+	.print = print_snbep_pcie_bw,
+	.print_hdr = print_snbep_pcie_bw_hdr,
+	.evts = snbep_pcie,
+	.nr_evts = ARRAY_SIZE(snbep_pcie),
+	.hdr_width = 30,
+	.num_cols = 2,
+};
+
+static struct uncore_metric_event snbep_cstate[] = {
+	[0] = CFG(uncore_pcu, 0x0000, snbep_unc_pcu::unc_p_clockticks),
+	[1] = CFG(uncore_pcu, 0x4080, snbep_unc_pcu::UNC_P_POWER_STATE_OCCUPANCY:CORES_C0),
+	[2] = CFG(uncore_pcu, 0xc080, snbep_unc_pcu::UNC_P_POWER_STATE_OCCUPANCY:CORES_C6),
+	[3] = CFG(uncore_pcu, 0x8080, snbep_unc_pcu::UNC_P_POWER_STATE_OCCUPANCY:CORES_C3),
+};
+
+static void
+print_snbep_cstate_hdr(FILE *output, const struct uncore_metric *m, int line)
+{
+	int len = hdr_width(m);
+	int f, r;
+
+	len--;
+
+	switch (line) {
+	case 2:
+		f = (len - 7) / 2;
+		r = (len - 7) % 2;
+		fprintf(output, "%*s%*s",
+			7 + f, "C-state",
+			f+1+r, "|");
+		break;
+	case 3:
+		f = (len - 5*3) / 3;
+		r = (len - 5*3) % 3;
+		fprintf(output, "%*s%*s%*s %*s",
+			5 + f, "C0",
+			5 + f, "C3",
+			5 + f, "C6",
+			r, "|");
+		break;
+	case 4:
+		f = (len - 5*3) / 3;
+		r = (len - 5*3) % 3;
+		fprintf(output, "%*s%*s%*s %*s",
+			5 + f, "%",
+			5 + f, "%",
+			5 + f, "%",
+			r, "|");
+		break;
+	default:
+		fprintf(output, "%*s", len, "|");
+	}
+}
+
+static void
+print_snbep_cstate(FILE *output, const struct uncore_metric *m, int cpu)
+{
+#define SNBEP_NCORES_PER_SOCKET	8
+
+	struct uncore_metric_event *e;
+	struct perf_evsel *evsel;
+	double c0 = 0.0, c6 = 0.0, c3 = 0.0;
+	uint64_t c[4];
+	int i, f, r;
+	int len = hdr_width(m);
+
+	evsel = &m->evts[0].evsel;
+
+	/*
+	 * len is inclusive of border
+	 * no column marker for values, so cut short by 1
+	 * to keep alignment
+	 */
+	len--;
+
+	f = (len - 5*3) / 3;
+	r = (len - 5*3) % 3;
+
+	e = m->evts;
+	for (i = 0; i < 4; i++) {
+		evsel = &e[i].evsel;
+		c[i] = evsel->counts->cpu[cpu].val;
+	}
+	if (c[0] != 0) {
+		c0 = (double)c[1] * 100.0 / c[0] / SNBEP_NCORES_PER_SOCKET;
+		c3 = (double)c[2] * 100.0 / c[0] / SNBEP_NCORES_PER_SOCKET;
+		c6 = (double)c[3] * 100.0 / c[0] / SNBEP_NCORES_PER_SOCKET;
+	}
+
+	fprintf(output, "%*.2f", 5+f, c0);
+	fprintf(output, "%*.2f", 5+f, c3);
+	fprintf(output, "%*.2f %*s", 5+f, c6, r, r ? " " : "");
+}
+
+static const struct uncore_metric uncore_snbep_cstate = {
+	.name = "cstate",
+	.desc = "SNB-EP C-state residency",
+	.print = print_snbep_cstate,
+	.print_hdr = print_snbep_cstate_hdr,
+	.evts = snbep_cstate,
+	.nr_evts = ARRAY_SIZE(snbep_cstate),
+	.hdr_width = 30,
+	.num_cols = 3,
+};
+
+static const struct uncore_metric *snbep_metrics[]={
+	&uncore_snbep_mem_bw,
+	&uncore_snbep_qpi_bw,
+	&uncore_snbep_pcie_bw,
+	&uncore_snbep_cstate,
+	NULL,
+};
+
+static const struct uncore_metric *nhm_metrics[]={
+	&uncore_nhm_mem_bw,
+	NULL,
+};
+
+/*
+ * Prior to gcc-4.6, it was not possible to statically initialize
+ * member of an anon union inside a bigger struct. This happens
+ * with config1 inside perf_event_attr. Here we implement a workaround
+ * which consists in patching in the config1 values at runtime. This is
+ * not pretty but works across the board
+ */
+static void
+fixup_snbep_pcie_config1(void)
+{
+	int i;
+
+	for (i = 0 ; i < uncore_snbep_pcie_bw.nr_evts; i++)
+		snbep_pcie[i].evsel.attr.config1 = snbep_pcie_config1[i];
+}
+
+static inline void
+cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
+      unsigned int *d)
+{
+  __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\tmovl %%ebx, %%esi\n\t.byte 0x5b"
+			: "=a" (*a),
+			"=S" (*b),
+			"=c" (*c),
+			"=d" (*d)
+			: "a" (op));
+}
+
+static int
+intel_x86_detect(void)
+{
+	unsigned int a, b, c, d;
+	char buffer[64];
+
+	cpuid(0, &a, &b, &c, &d);
+	strncpy(&buffer[0], (char *)(&b), 4);
+	strncpy(&buffer[4], (char *)(&d), 4);
+	strncpy(&buffer[8], (char *)(&c), 4);
+	buffer[12] = '\0';
+
+	/* must be Intel */
+	if (strcmp(buffer, "GenuineIntel"))
+		return -1;
+
+	cpuid(1, &a, &b, &c, &d);
+
+	x86_family = (a >> 8) & 0xf;  // bits 11 - 8
+	x86_model  = (a >> 4) & 0xf;  // Bits  7 - 4
+
+	/* extended family */
+	if (x86_family == 0xf)
+		x86_family += (a >> 20) & 0xff;
+
+	/* extended model */
+	if (x86_family >= 0x6)
+		x86_model += ((a >> 16) & 0xf) << 4;
+
+	return 0;
+}
+
+int uncore_init_metrics(void)
+{
+	if (intel_x86_detect()) {
+		fprintf(stderr, "uncore: only supported on Intel processors for now\n");
+		return -1;
+	}
+
+	if (x86_family != 6) {
+		fprintf(stderr, "uncore: unsupported processor family %d\n", x86_family);
+		return -1;
+	}
+
+	switch (x86_model) {
+	case 26: /* NHM */
+	case 30: /* NHM */
+	case 37: /* WSM */
+	case 44: /* WSM-EP */
+	case 47: /* WSM E7 */
+		uncore_cfg.uncore_metrics = nhm_metrics;
+		break;
+	case 45: /* SNB-EP */
+		uncore_cfg.uncore_metrics = snbep_metrics;
+		fixup_snbep_pcie_config1();
+		break;
+	default:
+		fprintf(stderr, "uncore: unsupported processor model %d\n", x86_model);
+		return -1;
+	}
+	return 0;
+}
diff --git a/tools/perf/builtin-uncore.c b/tools/perf/builtin-uncore.c
new file mode 100644
index 0000000..b050712
--- /dev/null
+++ b/tools/perf/builtin-uncore.c
@@ -0,0 +1,739 @@
+/*
+ * builtin-stat.c
+ *
+ * Builtin stat command: Give a precise performance counters summary
+ * overview about any workload, CPU or specific PID.
+ */
+
+#include "perf.h"
+#include "builtin.h"
+#include "util/util.h"
+#include "util/parse-options.h"
+#include "util/parse-events.h"
+#include "util/event.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
+#include "util/debug.h"
+#include "util/header.h"
+#include "util/cpumap.h"
+#include "util/pmu.h"
+#include "util/cgroup.h"
+#include "util/uncore.h"
+
+#include <locale.h>
+
+#define DEFAULT_SEPARATOR " "
+
+static const struct uncore_mem_unit mem_units[] = {
+	[0]  = { .name = "Kb" , .num_bits = 1000ULL, },
+	[1]  = { .name = "Mb" , .num_bits = 1000 * 1000ULL, },
+	[2]  = { .name = "Gb" , .num_bits = 1000 * 1000 * 1000ULL, },
+	[3]  = { .name = "Kib", .num_bits = 1024ULL, },
+	[4]  = { .name = "Mib", .num_bits = 1024 * 1024ULL, },
+	[5]  = { .name = "Gib", .num_bits = 1024 * 1024 * 1024ULL, },
+	[6]  = { .name = "KB" , .num_bits = 8 * 1000ULL, },
+	[7]  = { .name = "MB" , .num_bits = 8 * 1000 * 1000ULL, },
+	[8]  = { .name = "GB" , .num_bits = 8 * 1000 * 1000 * 1000ULL, },
+	[9]  = { .name = "KiB", .num_bits = 8 * 1024ULL, },
+	[10] = { .name = "MiB", .num_bits = 8 * 1024 * 1024ULL, },
+	[11] = { .name = "GiB", .num_bits = 8 * 1024 * 1024 * 1024ULL, },
+	[12] = { .name = NULL, }, /* end marker */
+};
+#define UNCORE_DFL_MEM_UNIT	7 /* MB */
+
+struct uncore_config uncore_cfg = {
+	.interval = 1,
+	.nr_socks = 1,
+};
+
+static struct perf_evlist		*evsel_list;
+static pid_t				child_pid;
+static const char			*csv_sep			= NULL;
+static bool				csv_output			= false;
+static bool				time_sample			= false;
+static bool				list_metrics			= false;
+static const char			*output_name			= NULL;
+static FILE				*output				= NULL;
+static int				output_fd;
+static int				total_len			= 0;
+static bool				append_file;
+static volatile int			done				= 0;
+static u64				uncore_metric_active		= ~0; /* all */
+static struct timespec			ref_time;
+
+static inline int metric_active(const struct uncore_metric **m)
+{
+	return uncore_metric_active & (1ULL << (m - uncore_cfg.uncore_metrics));
+}
+
+static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
+{
+	return evsel->cpus ? evsel->cpus : evsel_list->cpus;
+}
+
+static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
+{
+	return perf_evsel__cpus(evsel)->nr;
+}
+
+static inline void diff_timespec(struct timespec *r, struct timespec *a, struct timespec *b)
+{
+	r->tv_sec = a->tv_sec - b->tv_sec;
+	if (a->tv_nsec < b->tv_nsec) {
+		r->tv_nsec = a->tv_nsec + 1000000000L - b->tv_nsec;
+		r->tv_sec--;
+	} else {
+		r->tv_nsec = a->tv_nsec - b->tv_nsec ;
+	}
+}
+
+static int create_perf_stat_counter(struct perf_evsel *evsel)
+{
+	struct perf_event_attr *attr = &evsel->attr;
+
+	attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
+			    PERF_FORMAT_TOTAL_TIME_RUNNING;
+
+	return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
+}
+
+static int read_counter(struct perf_evsel *counter)
+{
+	int cpu;
+
+	for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
+		if (__perf_evsel__read_on_cpu(counter, cpu, 0, 1) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * default catch all uncore_init_metrics
+ */
+int uncore_init_metrics(void) __attribute__((weak));
+int uncore_init_metrics(void)
+{
+	return -1;
+}
+
+static void
+uncore_print_filler(int len)
+{
+	int i;
+
+	for (i  = 0 ; i < len; i++)
+		fputc('-', output);
+}
+
+static void
+uncore_print_time_hdr(int line, int prefix_len)
+{
+	switch (line) {
+	case 2:
+		fprintf(output, "%*s%*s", 4 + (prefix_len - 4)/2, "Time",
+			(prefix_len - 4) / 2, "|");
+		break;
+	case 3:
+		fprintf(output, "%*s%*s", 2 + (prefix_len - 2) / 2, "in",
+			(prefix_len - 2) / 2, "|");
+		break;
+	case 4:
+		fprintf(output, "%*s%*s", 4 + (prefix_len - 4)/2, "secs",
+			(prefix_len - 4) / 2, "|");
+		break;
+	default:
+		fprintf(output, "%*s", prefix_len, "|");
+	}
+}
+
+static void print_header(int nr, int len, int prefix_len)
+{
+	const struct uncore_metric **mp;
+	struct perf_evsel *counter;
+	struct cpu_map *cpus;
+	int hdr_lines = 5;
+	int j, cpu, s;
+	int f, r;
+
+	fputc('#', output);
+
+	uncore_print_filler(prefix_len + nr * len);
+
+	fputc('\n', output);
+	fputc('#', output);
+
+	if (time_sample)
+		uncore_print_time_hdr(0, prefix_len);
+
+	counter = list_first_entry(&evsel_list->entries, typeof(*counter), node);
+
+	cpus = counter->cpus;
+	for (cpu = 0; cpu < cpus->nr; cpu++) {
+		s = cpu_map__get_socket(cpus, cpu);
+		f = (len - 6 - 2) / 2;
+		r = (len - 6 - 2) % 2;
+		fprintf(output, "%*s%-2d%*s", 6 + f, "Socket", s, f + r, "|");
+	}
+	fputc('\n', output);
+	fputc('#', output);
+	if (time_sample)
+		uncore_print_time_hdr(1, prefix_len);
+
+	uncore_print_filler(nr * len);
+	fputc('\n', output);
+
+	for (j = 2; j < hdr_lines; j++) {
+		fputc('#', output);
+
+		if (time_sample)
+			uncore_print_time_hdr(j, prefix_len);
+
+		for (cpu = 0 ; cpu < nr; cpu++) {
+			for_each_uncore_metric(mp) {
+				if (!metric_active(mp))
+					continue;
+				(*mp)->print_hdr(output, *mp, j);
+			}
+		}
+		fputc('\n', output);
+	}
+	fputc('#', output);
+	uncore_print_filler(prefix_len + nr * len);
+	fputc('\n', output);
+}
+
+static void print_interval(void)
+{
+	static int print_count;
+	const struct uncore_metric **mp;
+	struct perf_evsel *counter;
+	char prefix[64] = { 0, };
+	int prefix_len = 0;
+	int cpu;
+
+	list_for_each_entry(counter, &evsel_list->entries, node) {
+		read_counter(counter);
+	}
+
+	if (time_sample) {
+		struct timespec ts, rs;
+		clock_gettime(CLOCK_MONOTONIC, &ts);
+		diff_timespec(&rs, &ts, &ref_time);
+		//prefix_len = sprintf(prefix, "%5lu.%09lu ", rs.tv_sec, rs.tv_nsec);
+		prefix_len = sprintf(prefix, "%9lu ", rs.tv_sec);
+	}
+
+	if (!print_count)
+		print_header(uncore_cfg.nr_socks, total_len, prefix_len);
+
+	fputc(' ', output); /* compensate '#' */
+
+	if (time_sample)
+		fprintf(output, "%s", prefix);
+
+	for (cpu = 0 ; cpu < uncore_cfg.nr_socks; cpu++) {
+		for_each_uncore_metric(mp) {
+			if (!metric_active(mp))
+				continue;
+			if ((*mp)->print)
+				(*mp)->print(output, *mp, cpu);
+		}
+	}
+	fputc('\n', output);
+
+	if (++print_count == 25)
+		print_count = 0;
+}
+
+static int run_perf_uncore(int argc, const char **argv)
+{
+	struct perf_evsel *counter;
+	struct timespec ts;
+	int status = 0, ret;
+	int child_ready_pipe[2], go_pipe[2];
+	const bool forks = (argc > 0);
+	char buf;
+
+	ts.tv_sec  = uncore_cfg.interval;
+	ts.tv_nsec = 0;
+
+	if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
+		perror("failed to create pipes");
+		return -1;
+	}
+
+	if (forks) {
+		if ((child_pid = fork()) < 0)
+			perror("failed to fork");
+
+		if (!child_pid) {
+			close(child_ready_pipe[0]);
+			close(go_pipe[1]);
+			fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
+
+			/*
+			 * Do a dummy execvp to get the PLT entry resolved,
+			 * so we avoid the resolver overhead on the real
+			 * execvp call.
+			 */
+			execvp("", (char **)argv);
+
+			/*
+			 * Tell the parent we're ready to go
+			 */
+			close(child_ready_pipe[1]);
+
+			/*
+			 * Wait until the parent tells us to go.
+			 */
+			if (read(go_pipe[0], &buf, 1) == -1)
+				perror("unable to read pipe");
+
+			execvp(argv[0], (char **)argv);
+
+			perror(argv[0]);
+			exit(-1);
+		}
+
+		/*
+		 * Wait for the child to be ready to exec.
+		 */
+		close(child_ready_pipe[1]);
+		close(go_pipe[0]);
+		if (read(child_ready_pipe[0], &buf, 1) == -1)
+			perror("unable to read pipe");
+		close(child_ready_pipe[0]);
+	}
+
+	list_for_each_entry(counter, &evsel_list->entries, node) {
+		if (create_perf_stat_counter(counter) < 0) {
+			/*
+			 * PPC returns ENXIO for HW counters until 2.6.37
+			 * (behavior changed with commit b0a873e).
+			 */
+			if (errno == EINVAL || errno == ENOSYS ||
+			    errno == ENOENT || errno == EOPNOTSUPP ||
+			    errno == ENXIO) {
+				if (verbose)
+					ui__warning("%s event is not supported by the kernel.\n",
+						    perf_evsel__name(counter));
+				counter->supported = false;
+				continue;
+			}
+
+			if (errno == EPERM || errno == EACCES) {
+				error("You may not have permission to collect system-wide stats.\n"
+				      "\t Consider tweaking"
+				      " /proc/sys/kernel/perf_event_paranoid or running as root.");
+			} else {
+				error("open_counter returned with %d (%s) for event %s. "
+				      "/bin/dmesg may provide additional information.\n",
+				       errno, strerror(errno),
+				       perf_evsel__name(counter));
+			}
+			if (child_pid != -1)
+				kill(child_pid, SIGTERM);
+
+			pr_err("Not all events could be opened.\n");
+			return -1;
+		}
+		counter->supported = true;
+	}
+
+	/*
+	 * Enable counters and exec the command:
+	 */
+
+	clock_gettime(CLOCK_MONOTONIC, &ref_time);
+
+	if (forks) {
+		close(go_pipe[1]);
+		if (uncore_cfg.interval) {
+			while (!waitpid(child_pid, &status, WNOHANG)) {
+				ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+				if (ret == 0 && uncore_cfg.interval)
+					print_interval();
+			}
+			wait(&status);
+		} else {
+			ret = wait(&status);
+			if (ret > 0 && WEXITSTATUS(status) == 0) {
+				struct timespec rs;
+				clock_gettime(CLOCK_MONOTONIC, &ts);
+				diff_timespec(&rs, &ts, &ref_time);
+				/*
+				 * stash virtual time in interval, so
+				 * ratios are computed
+				 */
+				uncore_cfg.interval = rs.tv_sec;
+
+				print_interval();
+			}
+		}
+		if (WIFSIGNALED(status))
+			psignal(WTERMSIG(status), argv[0]);
+	} else {
+		while(!done) {
+			ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+			if (ret == 0 && uncore_cfg.interval)
+				print_interval();
+		}
+	}
+
+	list_for_each_entry(counter, &evsel_list->entries, node) {
+		perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1);
+	}
+	return WEXITSTATUS(status);
+}
+
+static volatile int signr = -1;
+
+static void skip_signal(int signo)
+{
+	done = 1;
+
+	signr = signo;
+}
+
+static void sig_atexit(void)
+{
+	if (child_pid != -1)
+		kill(child_pid, SIGTERM);
+
+	if (signr == -1)
+		return;
+
+	signal(signr, SIG_DFL);
+	kill(getpid(), signr);
+}
+
+static const char * const uncore_usage[] = {
+	"perf uncore [<options>] [<command>]",
+	NULL
+};
+
+static int
+uncore_init_pmu_type(void)
+{
+	const struct uncore_metric **mp, *m;
+	struct uncore_metric_event *e;
+	struct perf_pmu *pmu = NULL;
+	int j;
+
+	for_each_uncore_metric(mp) {
+		m = *mp;
+		if (!metric_active(mp))
+			continue;
+		e = m->evts;
+
+		for (j = 0; j < m->nr_evts; j++) {
+			/* try reusing the previous pmu struct */
+			if (j == 0 || strcmp(e[j].pmu_name, e[j-1].pmu_name)) {
+				char *s = strdup(e[j].pmu_name);
+				if (!s) {
+					fprintf(stderr, "cannot allocate memory for PMU name %s\n", e[j].pmu_name);
+					return -1;
+				}
+				pmu = perf_pmu__find(s);
+				free(s);
+				if (!pmu) {
+					fprintf(stderr, "cannot determine type for PMU %s\n", e[j].pmu_name);
+					return -1;
+				}
+			}
+
+			if (pmu->cpus == NULL) {
+				fprintf(stderr, "PMU %s has no cpumask\n", e[j].pmu_name);
+				return -1;
+			}
+
+			e[j].evsel.cpus = pmu->cpus;
+			e[j].evsel.attr.type = pmu->type;
+		}
+	}
+	return 0;
+}
+
+static int
+uncore_add_events(struct perf_evlist *evlist)
+{
+	const struct uncore_metric **mp;
+	struct perf_evsel *evsel;
+	struct uncore_metric_event *e;
+	void *addr;
+	size_t sz;
+	int j, ret = -1;
+
+	for_each_uncore_metric(mp) {
+		if (!metric_active(mp))
+			continue;
+
+		total_len += hdr_width(*mp);
+
+		e = (*mp)->evts;
+		for (j = 0; j < (*mp)->nr_evts; j++) {
+			evsel = &e[j].evsel;
+
+			event_attr_init(&evsel->attr);
+
+			uncore_cfg.nr_socks = perf_evsel__nr_cpus(evsel);
+			evsel->name = strdup(e[j].name);
+
+			sz = sizeof(*evsel->counts)
+			   + (uncore_cfg.nr_socks * sizeof(struct perf_counts_values));
+
+			addr = zalloc(sz);
+			if (!addr)
+				goto error;
+
+			evsel->prev_raw_counts =  addr;
+
+			perf_evlist__add(evlist, evsel);
+		}
+	}
+	ret = 0;
+error:
+	return ret;
+
+}
+
+static int
+uncore_setup_evlist(struct perf_evlist *evlist)
+{
+	if (uncore_init_pmu_type())
+		return -1;
+
+	if (uncore_add_events(evlist))
+		return -1;
+
+	perf_evlist__set_leader(evlist);
+
+	return 0;
+}
+
+static void
+uncore_destroy_evlist(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel, *n;
+
+	list_for_each_entry_safe(evsel, n, &evlist->entries, node) {
+		free(evsel->name);
+
+		/* free both raw_counts and prev_raw_counts */
+		free(evsel->prev_raw_counts);
+
+		list_del_init(&evsel->node);
+
+		perf_evsel__exit(evsel);
+	}
+
+}
+
+static void
+print_metrics(void)
+{
+	const struct uncore_metric **mp;
+
+	for_each_uncore_metric(mp) {
+		printf("%s : %s\n", (*mp)->name, (*mp)->desc);
+	}
+}
+
+static int
+parse_mem_unit(const struct option *opt, const char *str, int unset)
+{
+	const struct uncore_mem_unit **mode = (const struct uncore_mem_unit **)opt->value;
+	const struct uncore_mem_unit *m;
+
+	if (unset)
+		return 0;
+
+	/*
+	 * cannot set it twice, -b + --branch-filter for instance
+	 */
+	if (*mode)
+		return -1;
+
+	if (!str)
+		return 0;
+
+	for (m = mem_units; m->name; m++) {
+		if (!strcmp(m->name, str)) {
+			*mode = m;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static int
+parse_metrics(const struct option *opt, const char *str, int unset)
+{
+	uint64_t *mask = (uint64_t *)opt->value;
+	const struct uncore_metric **mp;
+	char *s, *os = NULL, *p;
+	int ret = -1;
+
+	if (unset)
+		return 0;
+
+	/* no metric = all metrics */
+	if (!str)
+		return -1;
+
+	/* because str is read-only */
+	s = os = strdup(str);
+	if (!s)
+		return -1;
+
+	*mask = 0;
+	for (;;) {
+		p = strchr(s, ',');
+		if (p)
+			*p = '\0';
+
+		for_each_uncore_metric(mp) {
+			if (!strcasecmp(s, (*mp)->name))
+				break;
+		}
+
+		if (!(*mp)) {
+			fprintf(stderr, "unknown metric %s,"
+				" use perf uncore --list-metrics\n", s);
+			goto error;
+		}
+		/* sanity check for mask */
+		if ((mp - uncore_cfg.uncore_metrics) >= 64) {
+			fprintf(stderr, "perf uncore error: num metrics >= 64\n");
+			goto error;
+		}
+		*mask |= 1ULL << (mp - uncore_cfg.uncore_metrics);
+
+		if (!p)
+			break;
+
+		s = p + 1;
+	}
+	ret = 0;
+error:
+	free(os);
+	return ret;
+}
+
+static const struct option options[] = {
+	OPT_INCR('v', "verbose", &verbose,
+		    "be more verbose (show counter open errors, etc)"),
+	OPT_STRING('o', "output", &output_name, "file",
+		   "output file name"),
+	OPT_BOOLEAN(0, "append", &append_file, "append to the output file"),
+	OPT_INTEGER(0, "log-fd", &output_fd,
+		    "log output to fd, instead of stderr"),
+	OPT_INTEGER('I', "interval", &uncore_cfg.interval,
+		    "print interval in sec (default: 1)"),
+	OPT_BOOLEAN('T', "time", &time_sample, "add timing to each sample"),
+	OPT_CALLBACK('m', "mem-unit", &uncore_cfg.mem_unit, "memory bandwidth unit",
+		     "set memory unit from, [K,M,G][i,][b,B] (default: MB)",
+		     parse_mem_unit),
+	OPT_CALLBACK('M', "metrics", &uncore_metric_active,
+		     "metric[,metric]", "metric to collect (default: all)",
+		     parse_metrics),
+	OPT_BOOLEAN('L', "list-metrics", &list_metrics, "list metrics for host processor"),
+	OPT_END()
+};
+
+int cmd_uncore(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+	struct perf_evsel *pos;
+	int status = -ENOMEM;
+	const char *mode;
+
+	if (uncore_init_metrics()) {
+		fprintf(stderr, "Host CPU not supported for uncore measurements\n");
+		return -1;
+	}
+
+	setlocale(LC_ALL, "");
+
+	evsel_list = perf_evlist__new(NULL, NULL);
+	if (evsel_list == NULL)
+		return -ENOMEM;
+
+	argc = parse_options(argc, argv, options, uncore_usage,
+		PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (list_metrics) {
+		print_metrics();
+		return 0;
+	}
+	output = stderr;
+	if (output_name && strcmp(output_name, "-"))
+		output = NULL;
+
+	if (output_name && output_fd) {
+		fprintf(stderr, "cannot use both --output and --log-fd\n");
+		usage_with_options(uncore_usage, options);
+	}
+
+	if (output_fd < 0) {
+		fprintf(stderr, "argument to --log-fd must be a > 0\n");
+		usage_with_options(uncore_usage, options);
+	}
+
+	if (!output) {
+		struct timespec tm;
+		mode = append_file ? "a" : "w";
+
+		output = fopen(output_name, mode);
+		if (!output) {
+			perror("failed to create output file");
+			return -1;
+		}
+		clock_gettime(CLOCK_REALTIME, &tm);
+		fprintf(output, "# started on %s\n", ctime(&tm.tv_sec));
+	} else if (output_fd > 0) {
+		mode = append_file ? "a" : "w";
+		output = fdopen(output_fd, mode);
+		if (!output) {
+			perror("Failed opening logfd");
+			return -errno;
+		}
+	}
+
+	if (csv_sep) {
+		csv_output = true;
+		if (!strcmp(csv_sep, "\\t"))
+			csv_sep = "\t";
+	} else
+		csv_sep = DEFAULT_SEPARATOR;
+
+	if (!uncore_cfg.mem_unit)
+		uncore_cfg.mem_unit = mem_units + UNCORE_DFL_MEM_UNIT;
+
+	if  (uncore_setup_evlist(evsel_list)) {
+		usage_with_options(uncore_usage, options);
+		return -1;
+	}
+
+	list_for_each_entry(pos, &evsel_list->entries, node) {
+		if (perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0)
+			goto out_free_fd;
+	}
+
+	/*
+	 * We dont want to block the signals - that would cause
+	 * child tasks to inherit that and Ctrl-C would not work.
+	 * What we want is for Ctrl-C to work in the exec()-ed
+	 * task, but being ignored by perf stat itself:
+	 */
+	atexit(sig_atexit);
+	signal(SIGINT,  skip_signal);
+	signal(SIGALRM, skip_signal);
+	signal(SIGABRT, skip_signal);
+
+	status = run_perf_uncore(argc, argv);
+out_free_fd:
+	uncore_destroy_evlist(evsel_list);
+
+	return status;
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 08143bd..7c05a9b 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -36,6 +36,7 @@ extern int cmd_kvm(int argc, const char **argv, const char *prefix);
 extern int cmd_test(int argc, const char **argv, const char *prefix);
 extern int cmd_trace(int argc, const char **argv, const char *prefix);
 extern int cmd_inject(int argc, const char **argv, const char *prefix);
+extern int cmd_uncore(int argc, const char **argv, const char *prefix);
 
 extern int find_scripts(char **scripts_array, char **scripts_path_array);
 #endif
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index 3e86bbd..d3f3970 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -24,3 +24,4 @@ perf-kmem			mainporcelain common
 perf-lock			mainporcelain common
 perf-kvm			mainporcelain common
 perf-test			mainporcelain common
+perf-uncore			mainporcelain common
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 095b882..f895b0d 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -60,6 +60,7 @@ static struct cmd_struct commands[] = {
 	{ "trace",	cmd_trace,	0 },
 #endif
 	{ "inject",	cmd_inject,	0 },
+	{ "uncore",	cmd_uncore,	0 },
 };
 
 struct pager_config {
diff --git a/tools/perf/util/uncore.h b/tools/perf/util/uncore.h
new file mode 100644
index 0000000..f33e68f
--- /dev/null
+++ b/tools/perf/util/uncore.h
@@ -0,0 +1,56 @@
+/*
+ * uncore.h: definitions for perf uncore
+ *
+ * Contributed by: Stephane Eranian <eranian@google.com>
+ *
+ * Released under the GPL v2
+ */
+struct uncore_metric_event {
+	const char *pmu_name;
+	const char *name;
+	struct perf_pmu *pmu;
+	struct perf_evsel evsel;
+	int bit_unit; /* 1 byte => bit_unit = 8, 1 bit => .bit_unit = 1 */
+};
+
+#define CFG(n, a, s) \
+{ .evsel = { .attr = { .config = a,} }, \
+  .name = #s,\
+  .pmu_name = #n,\
+  .bit_unit = 8, \
+}
+
+struct uncore_metric {
+	const char *name;
+	const char *desc;
+	void (*print)(FILE *out, const struct uncore_metric *m, int cpu);
+	void (*print_hdr)(FILE *out, const struct uncore_metric *m, int line);
+	struct uncore_metric_event *evts;
+	int nr_evts;
+	int hdr_width;
+	int num_cols;
+};
+
+struct uncore_config {
+	const struct uncore_metric **uncore_metrics;
+	const struct uncore_mem_unit *mem_unit;
+	int nr_socks;
+	int interval;
+};
+
+#define for_each_uncore_metric(x) \
+	for((x) = uncore_cfg.uncore_metrics; *(x); (x)++)
+
+extern struct uncore_config uncore_cfg;
+
+struct uncore_mem_unit {
+	const char *name;	/* 3 characters max */
+	u64 num_bits;		/* number of bits */
+};
+
+static inline int hdr_width(const struct uncore_metric *m)
+{
+	return m->hdr_width
+	     + (uncore_cfg.mem_unit->num_bits < 8 * 1000 * 1000)
+	     * m->num_cols * 4;
+}
-- 
1.7.10.4


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

* Re: [PATCH 0/2] perf: add new uncore command
  2013-01-31 13:54 [PATCH 0/2] perf: add new uncore command Stephane Eranian
  2013-01-31 13:54 ` [PATCH 1/2] perf tools: add cpu_map__get_socket() Stephane Eranian
  2013-01-31 13:54 ` [PATCH 2/2] perf: add new perf uncore command Stephane Eranian
@ 2013-01-31 14:21 ` Ingo Molnar
  2013-01-31 14:32   ` Stephane Eranian
  2013-02-01  8:42 ` Namhyung Kim
  3 siblings, 1 reply; 9+ messages in thread
From: Ingo Molnar @ 2013-01-31 14:21 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: linux-kernel, peterz, mingo, ak, acme, jolsa, namhyung.kim


* Stephane Eranian <eranian@google.com> wrote:

> This patchset adds a new command to perf: perf uncore.
> It is used to measure processor socket-level metrics
> on a system-wide basis and at all priv levels.
> 
> The command comes with a set of predefined key metrics
> which are useful to measure multi-socket system imbalance
> and various bandwidths.
> 
> The following metrics are currently defined:
>   - memory bandwidth (Nehalem, Westmere, SandyBridge-EP)
>   - PCIe bandwidth (SandyBridge-EP)
>   - QPI bandwidth (SandyBridge-EP)
>   - C-state residency (SandyBridge-EP)
> 
> Others can be added in the future.
> 
> The command provides options to modify the unit of the metrics
> (default: MB/s for bandwidth).
> 
> Example on Nehalem:
> 
>  # perf uncore
>  #------------------------------
>  #           Socket0           |
>  #------------------------------
>  #        RAM Bandwidth        |
>  #            Wr             Rd|
>  #          MB/s           MB/s|
>  #------------------------------
>          4954.99       14897.29
>          4953.97       14894.56
>          4947.52       14874.97
> 
> To make plotting easier, the output can be augmented with a timestamp:
> 
>  # perf uncore -T
>  #----------------------------------------
>  #         |           Socket0           |
>  #         |------------------------------
>  #   Time  |        RAM Bandwidth        |
>  #    in   |            Wr             Rd|
>  #   secs  |          MB/s           MB/s|
>  #----------------------------------------
>           1        4952.50       14890.49
>           2        4955.55       14900.19
>           3        4949.13       14879.60
>           4        4954.66       14896.26

Looks really useful - how about naming it in a bit more generic 
way, because I'm quite sure this command will become popular and 
'perf uncore' is a bit un-intutive.

A couple of possibilities:

  perf system
  perf hw
  perf hw-stat

and also adding subcommands instead of options for the various 
views/metrics, such as:

  perf hw ram
  perf hw pci
  perf hw qpi
  perf hw cstate
  etc.

A plain 'perf hw' command would then list the available 
sub-commands and inform the user about the (current) set of 
hw subsystems that provide metrics.

Hm?

Thanks,

	Ingo

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

* Re: [PATCH 0/2] perf: add new uncore command
  2013-01-31 14:21 ` [PATCH 0/2] perf: add new " Ingo Molnar
@ 2013-01-31 14:32   ` Stephane Eranian
  0 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2013-01-31 14:32 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: LKML, Peter Zijlstra, mingo, ak, Arnaldo Carvalho de Melo,
	Jiri Olsa, Namhyung Kim

On Thu, Jan 31, 2013 at 3:21 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> * Stephane Eranian <eranian@google.com> wrote:
>
>> This patchset adds a new command to perf: perf uncore.
>> It is used to measure processor socket-level metrics
>> on a system-wide basis and at all priv levels.
>>
>> The command comes with a set of predefined key metrics
>> which are useful to measure multi-socket system imbalance
>> and various bandwidths.
>>
>> The following metrics are currently defined:
>>   - memory bandwidth (Nehalem, Westmere, SandyBridge-EP)
>>   - PCIe bandwidth (SandyBridge-EP)
>>   - QPI bandwidth (SandyBridge-EP)
>>   - C-state residency (SandyBridge-EP)
>>
>> Others can be added in the future.
>>
>> The command provides options to modify the unit of the metrics
>> (default: MB/s for bandwidth).
>>
>> Example on Nehalem:
>>
>>  # perf uncore
>>  #------------------------------
>>  #           Socket0           |
>>  #------------------------------
>>  #        RAM Bandwidth        |
>>  #            Wr             Rd|
>>  #          MB/s           MB/s|
>>  #------------------------------
>>          4954.99       14897.29
>>          4953.97       14894.56
>>          4947.52       14874.97
>>
>> To make plotting easier, the output can be augmented with a timestamp:
>>
>>  # perf uncore -T
>>  #----------------------------------------
>>  #         |           Socket0           |
>>  #         |------------------------------
>>  #   Time  |        RAM Bandwidth        |
>>  #    in   |            Wr             Rd|
>>  #   secs  |          MB/s           MB/s|
>>  #----------------------------------------
>>           1        4952.50       14890.49
>>           2        4955.55       14900.19
>>           3        4949.13       14879.60
>>           4        4954.66       14896.26
>
> Looks really useful - how about naming it in a bit more generic
> way, because I'm quite sure this command will become popular and
> 'perf uncore' is a bit un-intutive.
>
> A couple of possibilities:
>
>   perf system
>   perf hw
>   perf hw-stat
>
I am fine with that.  I prefer hw or systems for the command name.

> and also adding subcommands instead of options for the various
> views/metrics, such as:
>
>   perf hw ram
>   perf hw pci
>   perf hw qpi
>   perf hw cstate

Yes, could do that. Even though having all of them measure at once
is also useful (this is how it currently works by default).

>   etc.
>
> A plain 'perf hw' command would then list the available
> sub-commands and inform the user about the (current) set of
> hw subsystems that provide metrics.
>
Yes. this is what perf uncore -L already does.

For instance on SNB-EP:
$ perf uncore -L
mem_bw : SNB-EP MEM Bandwidth
qpi_bw : SNB-EP QPI Bandwidth
pcie_bw : SNB-EP PCIe Bandwidth
cstate : SNB-EP C-state residency

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

* Re: [PATCH 0/2] perf: add new uncore command
  2013-01-31 13:54 [PATCH 0/2] perf: add new uncore command Stephane Eranian
                   ` (2 preceding siblings ...)
  2013-01-31 14:21 ` [PATCH 0/2] perf: add new " Ingo Molnar
@ 2013-02-01  8:42 ` Namhyung Kim
  2013-02-02  1:35   ` Andi Kleen
  3 siblings, 1 reply; 9+ messages in thread
From: Namhyung Kim @ 2013-02-01  8:42 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: linux-kernel, peterz, mingo, ak, acme, jolsa, namhyung.kim

Hi, Stephane

On Thu, 31 Jan 2013 14:54:37 +0100, Stephane Eranian wrote:
> This patchset adds a new command to perf: perf uncore.
> It is used to measure processor socket-level metrics
> on a system-wide basis and at all priv levels.
>
> The command comes with a set of predefined key metrics
> which are useful to measure multi-socket system imbalance
> and various bandwidths.
>
> The following metrics are currently defined:
>   - memory bandwidth (Nehalem, Westmere, SandyBridge-EP)
>   - PCIe bandwidth (SandyBridge-EP)
>   - QPI bandwidth (SandyBridge-EP)
>   - C-state residency (SandyBridge-EP)
>
> Others can be added in the future.
>
> The command provides options to modify the unit of the metrics
> (default: MB/s for bandwidth).
>
> Example on Nehalem:
>
>  # perf uncore
>  #------------------------------
>  #           Socket0           |
>  #------------------------------
>  #        RAM Bandwidth        |
>  #            Wr             Rd|
>  #          MB/s           MB/s|
>  #------------------------------
>          4954.99       14897.29
>          4953.97       14894.56
>          4947.52       14874.97
>
> To make plotting easier, the output can be augmented with a timestamp:
>
>  # perf uncore -T
>  #----------------------------------------
>  #         |           Socket0           |
>  #         |------------------------------
>  #   Time  |        RAM Bandwidth        |
>  #    in   |            Wr             Rd|
>  #   secs  |          MB/s           MB/s|
>  #----------------------------------------
>           1        4952.50       14890.49
>           2        4955.55       14900.19
>           3        4949.13       14879.60
>           4        4954.66       14896.26
>
> The code is split between generic layer (builtin-uncore.c) and
> an arch specific layer (arch/*/util/uncore.c). All events are
> hardcoded because they don't change for a given processor and
> metric computation requires combining multiple events.

>From a quick glancing, I think this is basically what perf stat does
with selected events, right?  As we now have your interval printing I
can't find much difference.

And there's a patchset [1] from Jiri to support some kind of formula -
yeah, now I've written the correct spelling. :) - that might fit to this
purpose if you provide suitable formula file IMHO.  So I guess we don't
need to have another command and can reuse perf stat, no?

Thanks,
Namhyung


[1] http://lwn.net/Articles/532634/

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

* Re: [PATCH 2/2] perf: add new perf uncore command
  2013-01-31 13:54 ` [PATCH 2/2] perf: add new perf uncore command Stephane Eranian
@ 2013-02-01  8:45   ` Namhyung Kim
  0 siblings, 0 replies; 9+ messages in thread
From: Namhyung Kim @ 2013-02-01  8:45 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: linux-kernel, peterz, mingo, ak, acme, jolsa, namhyung.kim

Just a nitpick.  But maybe I shows it can be done with perf stat. :)


On Thu, 31 Jan 2013 14:54:39 +0100, Stephane Eranian wrote:
> diff --git a/tools/perf/builtin-uncore.c b/tools/perf/builtin-uncore.c
> new file mode 100644
> index 0000000..b050712
> --- /dev/null
> +++ b/tools/perf/builtin-uncore.c
> @@ -0,0 +1,739 @@
> +/*
> + * builtin-stat.c

s/stat/uncore/ :)

> + *
> + * Builtin stat command: Give a precise performance counters summary

Ditto.

Thanks,
Namhyung


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

* Re: [PATCH 0/2] perf: add new uncore command
  2013-02-01  8:42 ` Namhyung Kim
@ 2013-02-02  1:35   ` Andi Kleen
  2013-02-04 13:33     ` Jiri Olsa
  0 siblings, 1 reply; 9+ messages in thread
From: Andi Kleen @ 2013-02-02  1:35 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Stephane Eranian, linux-kernel, peterz, mingo, acme, jolsa, namhyung.kim

> And there's a patchset [1] from Jiri to support some kind of formula -
> yeah, now I've written the correct spelling. :) - that might fit to this
> purpose if you provide suitable formula file IMHO.  So I guess we don't
> need to have another command and can reuse perf stat, no?

Yes with a proper expression engine it's all redundant. I haven't 
looked at Jiri's stuff in detail, but it seemed very simple-minded
and may not support all the computation needed.

Also would allow to finally replace the bogus perf stat -d frontend/backend
counters.

For stat there's no really a compelling reason to integrate
it, the usual wrappers work as well. They have the advantage that
they can be written in real programing languages, instead of trying
to invent a new one.

Expressions integrated would be mainly useful for things like
"counting perf top"

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only

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

* Re: [PATCH 0/2] perf: add new uncore command
  2013-02-02  1:35   ` Andi Kleen
@ 2013-02-04 13:33     ` Jiri Olsa
  0 siblings, 0 replies; 9+ messages in thread
From: Jiri Olsa @ 2013-02-04 13:33 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Namhyung Kim, Stephane Eranian, linux-kernel, peterz, mingo,
	acme, namhyung.kim

On Fri, Feb 01, 2013 at 05:35:23PM -0800, Andi Kleen wrote:
> > And there's a patchset [1] from Jiri to support some kind of formula -
> > yeah, now I've written the correct spelling. :) - that might fit to this
> > purpose if you provide suitable formula file IMHO.  So I guess we don't
> > need to have another command and can reuse perf stat, no?
> 
> Yes with a proper expression engine it's all redundant. I haven't 
> looked at Jiri's stuff in detail, but it seemed very simple-minded
> and may not support all the computation needed.

yep, it was just an idea proposal.. it'd need more changes
too be able to handle this uncore stuff, but it's possible

> 
> Also would allow to finally replace the bogus perf stat -d frontend/backend
> counters.

yep

> 
> For stat there's no really a compelling reason to integrate
> it, the usual wrappers work as well. They have the advantage that
> they can be written in real programing languages, instead of trying
> to invent a new one.
> 
> Expressions integrated would be mainly useful for things like
> "counting perf top"

nice idea

jirka

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

end of thread, other threads:[~2013-02-04 13:34 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-31 13:54 [PATCH 0/2] perf: add new uncore command Stephane Eranian
2013-01-31 13:54 ` [PATCH 1/2] perf tools: add cpu_map__get_socket() Stephane Eranian
2013-01-31 13:54 ` [PATCH 2/2] perf: add new perf uncore command Stephane Eranian
2013-02-01  8:45   ` Namhyung Kim
2013-01-31 14:21 ` [PATCH 0/2] perf: add new " Ingo Molnar
2013-01-31 14:32   ` Stephane Eranian
2013-02-01  8:42 ` Namhyung Kim
2013-02-02  1:35   ` Andi Kleen
2013-02-04 13:33     ` Jiri Olsa

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.