The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: "David E. Box" <david.e.box@linux.intel.com>
To: linux-kernel@vger.kernel.org, david.e.box@linux.intel.com,
	ilpo.jarvinen@linux.intel.com, andriy.shevchenko@linux.intel.com,
	platform-driver-x86@vger.kernel.org
Subject: [PATCH 03/17] tools/arch/x86/pmtctl: Add libpmtctl internal logging and utility functions
Date: Mon, 25 May 2026 18:47:01 -0700	[thread overview]
Message-ID: <20260526014719.2248380-4-david.e.box@linux.intel.com> (raw)
In-Reply-To: <20260526014719.2248380-1-david.e.box@linux.intel.com>

Add the internal logging infrastructure and shared utility helpers used by
libpmtctl_core.

The logging layer provides consistent level-filtered diagnostics across the
library, while the utility helpers centralize common sysfs access, string
handling, and scoped resource cleanup functionality used across backend
implementations.

Logging levels mirror the public pmtctl_log_level enum and can be used to
control library verbosity.

Assisted-by: GitHub-Copilot:claude-sonnet-4.6
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---
 tools/arch/x86/pmtctl/include/lib/common.h   |  34 ++++
 tools/arch/x86/pmtctl/include/lib/log.h      |  80 ++++++++
 tools/arch/x86/pmtctl/include/lib/pmt_guid.h |  63 ++++++
 tools/arch/x86/pmtctl/lib/common.c           | 178 +++++++++++++++++
 tools/arch/x86/pmtctl/lib/log.c              |  80 ++++++++
 tools/arch/x86/pmtctl/lib/pmt_guid.c         | 200 +++++++++++++++++++
 6 files changed, 635 insertions(+)
 create mode 100644 tools/arch/x86/pmtctl/include/lib/common.h
 create mode 100644 tools/arch/x86/pmtctl/include/lib/log.h
 create mode 100644 tools/arch/x86/pmtctl/include/lib/pmt_guid.h
 create mode 100644 tools/arch/x86/pmtctl/lib/common.c
 create mode 100644 tools/arch/x86/pmtctl/lib/log.c
 create mode 100644 tools/arch/x86/pmtctl/lib/pmt_guid.c

diff --git a/tools/arch/x86/pmtctl/include/lib/common.h b/tools/arch/x86/pmtctl/include/lib/common.h
new file mode 100644
index 000000000000..cf1540326ec6
--- /dev/null
+++ b/tools/arch/x86/pmtctl/include/lib/common.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef PMTCTL_COMMON_H
+#define PMTCTL_COMMON_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#ifndef __GNUC__
+#error "pmtctl: cleanup helpers require GCC/Clang (__GNUC__)."
+#endif
+
+static inline void freep(void *p)
+{
+	void **ptr = (void **)p;
+
+	if (*ptr) {
+		free(*ptr);
+		*ptr = NULL;
+	}
+}
+
+#define auto_free __attribute__((cleanup(freep)))
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+char *xstrdup(const char *s);
+int read_attr_text(const char *dir, const char *name, char *buf, size_t buflen);
+int read_optional_int_attr(const char *devpath, const char *attr, int *out_id);
+int read_int_attr(const char *devpath, const char *attr, int *out_id);
+int read_u32_hex_attr(const char *devpath, const char *attr, uint32_t *out, int err_invalid);
+#endif
diff --git a/tools/arch/x86/pmtctl/include/lib/log.h b/tools/arch/x86/pmtctl/include/lib/log.h
new file mode 100644
index 000000000000..32f4de05f9de
--- /dev/null
+++ b/tools/arch/x86/pmtctl/include/lib/log.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef PMTCTL_LOG_H
+#define PMTCTL_LOG_H
+
+#include <linux/compiler.h>
+#include "pmtctl_types.h"
+
+#define PMTCTL_EXIT_USER    1
+#define PMTCTL_EXIT_SYSTEM  2
+#define PMTCTL_EXIT_BUG     3
+
+enum {
+	PMTCTL_ERR_PARSE = 1,   /* malformed JSON, invalid numbers, missing fields */
+	PMTCTL_ERR_NOTFOUND,   /* metric/device/group not found */
+	PMTCTL_ERR_BAD_ARG,
+	PMTCTL_ERR_CMD_PARSE,
+	PMTCTL_ERR_CMD_LIST,
+	PMTCTL_ERR_CMD_STAT,
+	PMTCTL_ERR_NOMETRICS,
+	PMTCTL_ERR_METRICS,
+	PMTCTL_ERR_INVALID,   /* bad selector, bad arguments, bad config */
+	PMTCTL_ERR_DEVICE,   /* device mismatch or device internal failure */
+	PMTCTL_ERR_BINDING,   /* binding table construction failed */
+	PMTCTL_ERR_UNSUPPORTED,   /* metric/feature not supported on this system */
+};
+
+#ifdef LOG_PREFIX
+#define _LOG_PREFIXED(fmt) LOG_PREFIX ": " fmt
+#else
+#define _LOG_PREFIXED(fmt) fmt
+#endif
+
+#define LOG_ERROR PMTCTL_LOG_ERROR
+#define LOG_WARN PMTCTL_LOG_WARN
+#define LOG_INFO PMTCTL_LOG_INFO
+#define LOG_DEBUG PMTCTL_LOG_DEBUG
+
+void log_impl(enum pmtctl_log_level lvl, int err, const char *fmt, ...)
+	__printf(3, 4);
+void log_set_level(enum pmtctl_log_level lvl);
+
+#define log_bug_and_exit(fmt, ...) \
+	log_bug_impl(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
+
+#define log_err(err, fmt, ...) \
+	log_impl(LOG_ERROR, (err), _LOG_PREFIXED(fmt), ##__VA_ARGS__)
+
+#define log_warn(fmt, ...) \
+	log_impl(LOG_WARN, 0, _LOG_PREFIXED(fmt), ##__VA_ARGS__)
+
+#define log_info(fmt, ...) \
+	log_impl(LOG_INFO, 0, _LOG_PREFIXED(fmt), ##__VA_ARGS__)
+
+#ifdef DEBUG
+#define log_debug(fmt, ...) \
+	log_impl(LOG_DEBUG, 0, _LOG_PREFIXED(fmt), ##__VA_ARGS__)
+#else
+#define log_debug(fmt, ...) \
+	((void)(0 ? log_info(fmt, ##__VA_ARGS__) : 0))
+#endif
+
+int log_ret(int ret, const char *fmt, ...);
+
+__noreturn
+void log_bug_impl(const char *file, int line, const char *fmt, ...);
+
+#define LOG_ONCE(level, fmt, ...)                             \
+	do {                                                  \
+		static int __done_##__LINE__;                 \
+		if (!__done_##__LINE__) {                     \
+			level(fmt, ##__VA_ARGS__);            \
+			__done_##__LINE__ = 1;                \
+		}                                             \
+	} while (0)
+
+#define log_err_once(fmt, ...)  LOG_ONCE(log_err,  fmt, ##__VA_ARGS__)
+#define log_warn_once(fmt, ...) LOG_ONCE(log_warn, fmt, ##__VA_ARGS__)
+#define log_info_once(fmt, ...) LOG_ONCE(log_info, fmt, ##__VA_ARGS__)
+
+#endif
diff --git a/tools/arch/x86/pmtctl/include/lib/pmt_guid.h b/tools/arch/x86/pmtctl/include/lib/pmt_guid.h
new file mode 100644
index 000000000000..d21bda5bc71b
--- /dev/null
+++ b/tools/arch/x86/pmtctl/include/lib/pmt_guid.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef PMTCTL_PMT_GUID_H
+#define PMTCTL_PMT_GUID_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * Per-GUID metadata derived from the Intel-PMT pmt.xml top-level mapping
+ * table. The compiled-in built-in table (builtin_guids[]) is generated by
+ * scripts/gen_builtin_defs.py. Runtime-loaded providers may register
+ * additional entries via pmt_guid_register().
+ *
+ * A global intern registry ensures pointer-equality across pmt_device and
+ * pmt_metric_def: two references to the same GUID always yield the same
+ * struct pmt_guid pointer.
+ */
+struct pmt_guid {
+	uint32_t    guid;
+	const char *name;        /* basedir lowercased with '/' -> '_'; may be NULL */
+	const char *description; /* may be NULL */
+};
+
+/*
+ * Register an array of pmt_guid entries with the global registry. The
+ * pointer must remain valid for the registry's lifetime. Entries with a
+ * GUID already present in the registry are skipped (first wins).
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+int pmt_guid_register(const struct pmt_guid *entries, int count);
+
+/*
+ * Same as pmt_guid_register(), but the registry takes ownership of `block`
+ * (typically the heap allocation that backs `entries` and any pooled
+ * strings the entries point into). The block is freed by pmt_guid_cleanup().
+ *
+ * `block` may equal `entries` when the array sits at the head of the
+ * allocation. Passing a NULL block degrades to pmt_guid_register().
+ *
+ * Returns 0 on success, negative errno on failure. On failure the caller
+ * retains ownership of `block` and must free it.
+ */
+int pmt_guid_register_owned(void *block, const struct pmt_guid *entries, int count);
+
+/*
+ * Lookup the registered pmt_guid for the given numeric GUID. Returns NULL
+ * if no entry has been registered or interned for that GUID.
+ */
+const struct pmt_guid *pmt_guid_lookup(uint32_t guid);
+
+/*
+ * Resolve numeric GUID to a stable struct pmt_guid pointer. If no entry is
+ * registered, a synthetic entry (name = NULL, description = NULL) is
+ * allocated and returned; subsequent calls with the same GUID return the
+ * same pointer. Returns NULL only on allocation failure.
+ */
+const struct pmt_guid *pmt_guid_intern(uint32_t guid);
+
+/* Free all synthetic (interned) entries and clear registrations. */
+void pmt_guid_cleanup(void);
+
+#endif
diff --git a/tools/arch/x86/pmtctl/lib/common.c b/tools/arch/x86/pmtctl/lib/common.c
new file mode 100644
index 000000000000..42931bf79480
--- /dev/null
+++ b/tools/arch/x86/pmtctl/lib/common.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lib/common.h"
+#include "lib/log.h"
+
+char *xstrdup(const char *s)
+{
+	char *p;
+
+	if (!s)
+		return NULL;
+
+	p = strdup(s);
+	if (!p) {
+		log_err(errno, "out of memory");
+		exit(EXIT_FAILURE);
+	}
+
+	return p;
+}
+
+static void trim_newline(char *s)
+{
+	size_t len;
+	char *end;
+
+	if (!s)
+		return;
+
+	len = strlen(s);
+	if (!len)
+		return;
+
+	end = s + len - 1;
+
+	while (end >= s && (*end == '\n' || *end == '\r')) {
+		*end = '\0';
+		end--;
+	}
+}
+
+int read_attr_text(const char *dir, const char *name, char *buf, size_t buflen)
+{
+	char path[PATH_MAX];
+	int fd;
+	ssize_t n;
+
+	if (!dir || !name || !buf || buflen == 0)
+		return -EINVAL;
+
+	if (snprintf(path, sizeof(path), "%s/%s", dir, name) >= (int)sizeof(path))
+		return -ENAMETOOLONG;
+
+	fd = open(path, O_RDONLY | O_CLOEXEC);
+	if (fd == -1)
+		return -errno;
+
+	n = read(fd, buf, buflen - 1);
+	close(fd);
+
+	if (n == -1)
+		return -errno;
+
+	buf[n] = '\0';
+	trim_newline(buf);
+
+	return 0;
+}
+
+int read_optional_int_attr(const char *devpath, const char *attr, int *out_id)
+{
+	char buf[64];
+	char *end;
+	long v;
+	int ret;
+
+	if (!out_id || !devpath || !attr)
+		return -EINVAL;
+
+	ret = read_attr_text(devpath, attr, buf, sizeof(buf));
+	if (ret != 0) {
+		if (ret == -ENOENT) {
+			/* file not found */
+			*out_id = -1;
+			return 0;
+		}
+
+		return ret;
+	}
+
+	errno = 0;
+
+	/* accept decimal or 0x-prefixed hex */
+	v = strtol(buf, &end, 0);
+	if (errno)
+		return -errno;
+
+	if (end == buf || *end != '\0')
+		return -EINVAL;
+
+	if (v < INT_MIN || v > INT_MAX)
+		return -ERANGE;
+
+	*out_id = (int)v;
+
+	return 0;
+}
+
+int read_int_attr(const char *devpath, const char *attr, int *out_id)
+{
+	char buf[64];
+	char *end;
+	long v;
+	int ret;
+
+	if (!out_id || !devpath || !attr)
+		return -EINVAL;
+
+	ret = read_attr_text(devpath, attr, buf, sizeof(buf));
+	if (ret != 0)
+		return ret;
+
+	errno = 0;
+
+	/* accept decimal or 0x-prefixed hex */
+	v = strtol(buf, &end, 0);
+	if (errno)
+		return -errno;
+
+	if (end == buf || *end != '\0')
+		return -EINVAL;
+
+	if (v < INT_MIN || v > INT_MAX)
+		return -ERANGE;
+
+	*out_id = (int)v;
+
+	return 0;
+}
+
+int read_u32_hex_attr(const char *devpath, const char *attr, uint32_t *out, int err_invalid)
+{
+	char buf[64];
+	char *end;
+	unsigned long v;
+	int ret;
+
+	if (!out || !devpath || !attr)
+		return -EINVAL;
+
+	ret = read_attr_text(devpath, attr, buf, sizeof(buf));
+	if (ret != 0)
+		return ret;
+
+	errno = 0;
+	v = strtoul(buf, &end, 16);
+	if (errno)
+		return -errno;
+
+	if (end == buf || *end != '\0')
+		return err_invalid;
+
+	if (v > UINT32_MAX)
+		return err_invalid;
+
+	*out = (uint32_t)v;
+
+	return 0;
+}
diff --git a/tools/arch/x86/pmtctl/lib/log.c b/tools/arch/x86/pmtctl/lib/log.c
new file mode 100644
index 000000000000..2ce9edacc97a
--- /dev/null
+++ b/tools/arch/x86/pmtctl/lib/log.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "lib/log.h"
+
+static enum pmtctl_log_level g_log_level = LOG_INFO;
+
+void log_set_level(enum pmtctl_log_level lvl)
+{
+	if (lvl < LOG_ERROR || lvl > LOG_DEBUG)
+		g_log_level = LOG_INFO;
+	else
+		g_log_level = lvl;
+}
+
+static void log_vmsg(enum pmtctl_log_level lvl, int err, const char *fmt,
+		     va_list ap)
+{
+	if (lvl > g_log_level)
+		return;
+
+	const char *tag =
+		(lvl == LOG_ERROR) ? "error"  :
+		(lvl == LOG_WARN)  ? "warning" :
+		(lvl == LOG_INFO)  ? "info"   :
+				     "debug";
+
+	fprintf(stderr, "%s: ", tag);
+	vfprintf(stderr, fmt, ap);
+
+	if (err != 0) {
+		int e = (err < 0) ? -err : err;
+
+		if (err < 0)
+			fprintf(stderr, ": %s", strerror(e));
+	}
+
+	fputc('\n', stderr);
+}
+
+void log_impl(enum pmtctl_log_level lvl, int err, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	log_vmsg(lvl, err, fmt, ap);
+	va_end(ap);
+}
+
+int log_ret(int ret, const char *fmt, ...)
+{
+	if (ret == 0)
+		return 0;
+
+	va_list ap;
+
+	va_start(ap, fmt);
+	log_vmsg(LOG_ERROR, ret, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+__noreturn
+void log_bug_impl(const char *file, int line, const char *fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "pmtctl: BUG at %s:%d: ", file, line);
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fputc('\n', stderr);
+
+	exit(PMTCTL_EXIT_BUG);
+}
diff --git a/tools/arch/x86/pmtctl/lib/pmt_guid.c b/tools/arch/x86/pmtctl/lib/pmt_guid.c
new file mode 100644
index 000000000000..f4d74fc7a977
--- /dev/null
+++ b/tools/arch/x86/pmtctl/lib/pmt_guid.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/pmt_guid.h"
+
+/*
+ * Global intern registry for struct pmt_guid.
+ *
+ * Two sources feed this table:
+ *   1. Pre-registered blocks (e.g. builtin_guids[] from generated code,
+ *      or JSON-loaded pmt_guids.json from the runtime provider). Pointers
+ *      to those entries are stored verbatim and assumed to outlive the
+ *      registry's use.
+ *   2. Synthetic entries allocated on demand by pmt_guid_intern() for
+ *      GUIDs that no provider knows about. These have name = NULL and
+ *      description = NULL.
+ *
+ * A simple linear-scan array is used; the registry holds at most a few
+ * hundred entries on any realistic system.
+ */
+struct guid_slot {
+	const struct pmt_guid *entry; /* points into registered block or synth_storage */
+	struct pmt_guid       *synth; /* non-NULL iff this slot owns the entry */
+};
+
+static struct guid_slot *g_slots;
+static int               g_nslots;
+static int               g_cap;
+
+/*
+ * Heap blocks handed to pmt_guid_register_owned(). The registry frees
+ * them in pmt_guid_cleanup() after disposing of the slot array, so any
+ * pointers the slots held into these blocks are gone by then.
+ */
+static void **g_owned_blocks;
+static int    g_nowned;
+static int    g_owned_cap;
+
+static int reserve_one(void)
+{
+	struct guid_slot *tmp;
+	int new_cap;
+
+	if (g_nslots < g_cap)
+		return 0;
+
+	new_cap = g_cap ? g_cap * 2 : 32;
+	tmp = realloc(g_slots, (size_t)new_cap * sizeof(*tmp));
+	if (!tmp)
+		return -ENOMEM;
+
+	g_slots = tmp;
+	g_cap = new_cap;
+	return 0;
+}
+
+const struct pmt_guid *pmt_guid_lookup(uint32_t guid)
+{
+	for (int i = 0; i < g_nslots; i++) {
+		if (g_slots[i].entry && g_slots[i].entry->guid == guid)
+			return g_slots[i].entry;
+	}
+	return NULL;
+}
+
+int pmt_guid_register(const struct pmt_guid *entries, int count)
+{
+	if (!entries || count <= 0)
+		return -EINVAL;
+
+	for (int i = 0; i < count; i++) {
+		const struct pmt_guid *e = &entries[i];
+		bool already_known = false;
+		int ret;
+
+		/*
+		 * If a slot already exists for this GUID, upgrade it when the
+		 * existing entry is a synthetic placeholder (created earlier
+		 * by pmt_guid_intern() before any provider was loaded) and the
+		 * new entry carries real metadata. The synthetic struct must
+		 * be kept alive because earlier callers (e.g. devices) hold
+		 * pointers to it; patch its fields in place instead of
+		 * swapping the slot entry.
+		 */
+		for (int s = 0; s < g_nslots; s++) {
+			struct guid_slot *slot = &g_slots[s];
+
+			if (!slot->entry || slot->entry->guid != e->guid)
+				continue;
+
+			if (slot->synth && e->name && *e->name) {
+				slot->synth->name = e->name;
+				slot->synth->description = e->description;
+			}
+			already_known = true;
+			break;
+		}
+
+		if (already_known)
+			continue;
+
+		ret = reserve_one();
+		if (ret < 0)
+			return ret;
+
+		g_slots[g_nslots].entry = e;
+		g_slots[g_nslots].synth = NULL;
+		g_nslots++;
+	}
+
+	return 0;
+}
+
+int pmt_guid_register_owned(void *block, const struct pmt_guid *entries, int count)
+{
+	void **tmp;
+	int ret;
+
+	/*
+	 * Grow g_owned_blocks first, before registering anything. If this
+	 * fails, no entries have been added to g_slots yet, so the caller
+	 * can safely free(block) on error without leaving dangling slot
+	 * pointers behind.
+	 */
+	if (block && g_nowned == g_owned_cap) {
+		int new_cap = g_owned_cap ? g_owned_cap * 2 : 8;
+
+		tmp = realloc(g_owned_blocks, (size_t)new_cap * sizeof(*tmp));
+		if (!tmp)
+			return -ENOMEM;
+
+		g_owned_blocks = tmp;
+		g_owned_cap = new_cap;
+	}
+
+	ret = pmt_guid_register(entries, count);
+	if (ret < 0)
+		return ret;
+
+	if (block)
+		g_owned_blocks[g_nowned++] = block;
+
+	return 0;
+}
+
+const struct pmt_guid *pmt_guid_intern(uint32_t guid)
+{
+	const struct pmt_guid *existing;
+	struct pmt_guid *synth;
+	int ret;
+
+	existing = pmt_guid_lookup(guid);
+	if (existing)
+		return existing;
+
+	ret = reserve_one();
+	if (ret < 0)
+		return NULL;
+
+	synth = calloc(1, sizeof(*synth));
+	if (!synth)
+		return NULL;
+
+	synth->guid = guid;
+	synth->name = NULL;
+	synth->description = NULL;
+
+	g_slots[g_nslots].entry = synth;
+	g_slots[g_nslots].synth = synth;
+	g_nslots++;
+
+	return synth;
+}
+
+void pmt_guid_cleanup(void)
+{
+	for (int i = 0; i < g_nslots; i++)
+		free(g_slots[i].synth);
+
+	free(g_slots);
+	g_slots = NULL;
+	g_nslots = 0;
+	g_cap = 0;
+
+	/*
+	 * Owned blocks are freed last: slot entries may have pointed into
+	 * them, but those pointers are gone now that g_slots is released.
+	 */
+	for (int i = 0; i < g_nowned; i++)
+		free(g_owned_blocks[i]);
+
+	free(g_owned_blocks);
+	g_owned_blocks = NULL;
+	g_nowned = 0;
+	g_owned_cap = 0;
+}
-- 
2.43.0


  parent reply	other threads:[~2026-05-26  1:47 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-26  1:46 [PATCH 00/17] tools/arch/x86/pmtctl: Add Intel PMT command-line tool David E. Box
2026-05-26  1:46 ` [PATCH 01/17] tools/arch/x86/pmtctl: Add MAINTAINERS entry David E. Box
2026-05-26  1:47 ` [PATCH 02/17] tools/arch/x86/pmtctl: Add libpmtctl shared type enumerations David E. Box
2026-05-26  9:20   ` Ilpo Järvinen
2026-05-26  1:47 ` David E. Box [this message]
2026-05-26  9:59   ` [PATCH 03/17] tools/arch/x86/pmtctl: Add libpmtctl internal logging and utility functions Ilpo Järvinen
2026-05-26  1:47 ` [PATCH 04/17] tools/arch/x86/pmtctl: Add libpmtctl metric definition database David E. Box
2026-05-26 10:06   ` Ilpo Järvinen
2026-05-26  1:47 ` [PATCH 05/17] tools/arch/x86/pmtctl: Add libpmtctl device enumeration backend David E. Box
2026-05-26 10:35   ` Ilpo Järvinen
2026-05-26  1:47 ` [PATCH 06/17] tools/arch/x86/pmtctl: Add libpmtctl built-in metric provider David E. Box
2026-05-26  1:47 ` [PATCH 07/17] tools/arch/x86/pmtctl: Add libpmtctl JSON " David E. Box
2026-05-26 11:04   ` Ilpo Järvinen
2026-05-26  1:47 ` [PATCH 08/17] tools/arch/x86/pmtctl: Add libpmtctl public API and context David E. Box
2026-05-26 11:25   ` Ilpo Järvinen
2026-05-26 17:44     ` David Box
2026-05-26  1:47 ` [PATCH 09/17] tools/arch/x86/pmtctl: Add libpmtctl Makefile + pc + README David E. Box
2026-05-26  1:47 ` [PATCH 10/17] tools/arch/x86/pmtctl: Add libpmtctl usage sample David E. Box
2026-05-26  1:47 ` [PATCH 11/17] tools/arch/x86/pmtctl: Add libpmtctl built-in metric definition support David E. Box
2026-05-26  1:47 ` [PATCH 12/17] tools/arch/x86/pmtctl: Add pmtctl CLI entry point and pager David E. Box
2026-05-26  1:47 ` [PATCH 13/17] tools/arch/x86/pmtctl: Add pmtctl 'list' command David E. Box
2026-05-26  1:47 ` [PATCH 14/17] tools/arch/x86/pmtctl: Add pmtctl 'stat' command David E. Box
2026-05-26  1:47 ` [PATCH 15/17] tools/arch/x86/pmtctl: Add pmtxml2json conversion tool David E. Box
2026-05-26  1:47 ` [PATCH 16/17] tools/arch/x86/pmtctl: Add README.md David E. Box
2026-05-26  1:47 ` [PATCH 17/17] tools/arch/x86/pmtctl: Add man page David E. Box

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260526014719.2248380-4-david.e.box@linux.intel.com \
    --to=david.e.box@linux.intel.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox