af2e7f
From cf758158efa2832b38a49770f3489f408cb93b41 Mon Sep 17 00:00:00 2001
af2e7f
From: Matthew Almond <malmond@meta.com>
af2e7f
Date: Fri, 6 Dec 2024 08:00:31 +0100
af2e7f
Subject: [PATCH] Measure plugin
af2e7f
af2e7f
---
af2e7f
 macros.in              |   1 +
af2e7f
 plugins/CMakeLists.txt |   1 +
af2e7f
 plugins/measure.c      | 232 +++++++++++++++++++++++++++++++++++++++++
af2e7f
 3 files changed, 234 insertions(+)
af2e7f
 create mode 100644 plugins/measure.c
af2e7f
af2e7f
diff --git a/macros.in b/macros.in
af2e7f
index df7defe..1af187a 100644
af2e7f
--- a/macros.in
af2e7f
+++ b/macros.in
af2e7f
@@ -1189,6 +1189,7 @@ Supplements:   (%{name} = %{version}-%{release} and langpacks-%{1})\
af2e7f
 # Transaction plugin macros
af2e7f
 %__plugindir		%{_libdir}/rpm-plugins
af2e7f
 %__transaction_reflink		%{__plugindir}/reflink.so
af2e7f
+%__transaction_measure		%{__plugindir}/measure.so
af2e7f
 %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so
af2e7f
 %__transaction_selinux		%{__plugindir}/selinux.so
af2e7f
 %__transaction_syslog		%{__plugindir}/syslog.so
af2e7f
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
af2e7f
index ed63a43..cda8f96 100644
af2e7f
--- a/plugins/CMakeLists.txt
af2e7f
+++ b/plugins/CMakeLists.txt
af2e7f
@@ -1,6 +1,7 @@
af2e7f
 add_library(prioreset MODULE prioreset.c)
af2e7f
 add_library(syslog MODULE syslog.c)
af2e7f
 add_library(reflink MODULE reflink.c)
af2e7f
+add_library(measure MODULE measure.c)
af2e7f
 
af2e7f
 if(WITH_SELINUX)
af2e7f
 	add_library(selinux MODULE selinux.c)
af2e7f
diff --git a/plugins/measure.c b/plugins/measure.c
af2e7f
new file mode 100644
af2e7f
index 0000000..593e463
af2e7f
--- /dev/null
af2e7f
+++ b/plugins/measure.c
af2e7f
@@ -0,0 +1,232 @@
af2e7f
+#include "system.h"
af2e7f
+#include "time.h"
af2e7f
+
af2e7f
+#include <rpm/rpmlog.h>
af2e7f
+#include <rpm/rpmmacro.h>
af2e7f
+#include <rpm/rpmts.h>
af2e7f
+#include <rpm/rpmlib.h>
af2e7f
+#include <rpm/rpmstring.h>
af2e7f
+
af2e7f
+#include "rpmplugin.h"
af2e7f
+
af2e7f
+struct measurestat {
af2e7f
+    /* We're counting psm not packages because packages often run psm_pre/post
af2e7f
+       more than once and we want to accumulate the time
af2e7f
+    */
af2e7f
+    unsigned int psm_count;
af2e7f
+    unsigned int scriptlet_count;
af2e7f
+    struct timespec plugin_start;
af2e7f
+    struct timespec psm_start;
af2e7f
+    struct timespec scriptlet_start;
af2e7f
+};
af2e7f
+
af2e7f
+static rpmRC push(const char *format, const char *value, const char *formatted)
af2e7f
+{
af2e7f
+    char *key = NULL;
af2e7f
+    rpmRC rc = RPMRC_FAIL;
af2e7f
+    if (formatted == NULL) {
af2e7f
+        /* yes we're okay with discarding const here */
af2e7f
+        key = (char *)format;
af2e7f
+    } else {
af2e7f
+        if (rasprintf(&key, format, formatted) == -1) {
af2e7f
+            rpmlog(
af2e7f
+                RPMLOG_ERR,
af2e7f
+                _("measure: Failed to allocate formatted key %s, %s\n"),
af2e7f
+                format,
af2e7f
+                formatted
af2e7f
+            );
af2e7f
+            goto exit;
af2e7f
+        }
af2e7f
+    }
af2e7f
+    if (rpmPushMacro(NULL, key, NULL, value, RMIL_GLOBAL)) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to set %s\n"), key);
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    rc = RPMRC_OK;
af2e7f
+exit:
af2e7f
+    if (formatted != NULL) {
af2e7f
+        free(key);
af2e7f
+    }
af2e7f
+    return rc;
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC diff_ms(char **ms, struct timespec *start, struct timespec *end)
af2e7f
+{
af2e7f
+    if (rasprintf(ms, "%ld", (
af2e7f
+        (end->tv_sec - start->tv_sec) * 1000 +
af2e7f
+        (end->tv_nsec - start->tv_nsec) / 1000000
af2e7f
+    )) == -1) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted ms\n"));
af2e7f
+        return RPMRC_FAIL;
af2e7f
+    }
af2e7f
+    return RPMRC_OK;
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC measure_init(rpmPlugin plugin, rpmts ts)
af2e7f
+{
af2e7f
+    rpmRC rc = RPMRC_FAIL;
af2e7f
+    struct measurestat *state = rcalloc(1, sizeof(*state));
af2e7f
+    if (clock_gettime(CLOCK_MONOTONIC, &state->plugin_start)) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to get plugin_start time\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    state->psm_count = 0;
af2e7f
+    state->scriptlet_count = 0;
af2e7f
+    rpmPluginSetData(plugin, state);
af2e7f
+    rc = RPMRC_OK;
af2e7f
+exit:
af2e7f
+    return rc;
af2e7f
+}
af2e7f
+
af2e7f
+static void measure_cleanup(rpmPlugin plugin)
af2e7f
+{
af2e7f
+    struct measurestat *state = rpmPluginGetData(plugin);
af2e7f
+    free(state);
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC measure_tsm_post(rpmPlugin plugin, rpmts ts, int res)
af2e7f
+{
af2e7f
+    struct measurestat *state = rpmPluginGetData(plugin);
af2e7f
+    char *psm_count = NULL, *scriptlet_count = NULL;
af2e7f
+    rpmRC rc = RPMRC_FAIL;
af2e7f
+
af2e7f
+    if (rasprintf(&psm_count, "%d", state->psm_count) == -1) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted psm_count\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (rasprintf(&scriptlet_count, "%d", state->scriptlet_count) == -1) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted scriptlet_count\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("_measure_plugin_psm_count", psm_count, NULL) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("_measure_plugin_scriptlet_count", scriptlet_count, NULL) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    rc = RPMRC_OK;
af2e7f
+exit:
af2e7f
+    free(psm_count);
af2e7f
+    free(scriptlet_count);
af2e7f
+    return rc;
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC measure_psm_pre(rpmPlugin plugin, rpmte te)
af2e7f
+{
af2e7f
+    struct measurestat *state = rpmPluginGetData(plugin);
af2e7f
+    rpmRC rc = RPMRC_FAIL;
af2e7f
+
af2e7f
+    if (clock_gettime(CLOCK_MONOTONIC, &state->psm_start)) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to get psm_start time\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    rc = RPMRC_OK;
af2e7f
+exit:
af2e7f
+    return rc;
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC measure_psm_post(rpmPlugin plugin, rpmte te, int res)
af2e7f
+{
af2e7f
+    struct measurestat *state = rpmPluginGetData(plugin);
af2e7f
+    struct timespec end;
af2e7f
+    char *offset = NULL, *duration = NULL, *prefix = NULL;
af2e7f
+    Header h = rpmteHeader(te);
af2e7f
+    rpmRC rc = RPMRC_FAIL;
af2e7f
+
af2e7f
+    if (clock_gettime(CLOCK_MONOTONIC, &end)) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to get psm end time\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (rasprintf(&prefix, "_measure_plugin_package_%u", state->psm_count) == -1) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate prefix\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (diff_ms(&offset, &state->plugin_start, &state->psm_start) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (diff_ms(&duration, &state->psm_start, &end) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_nevra", rpmteNEVRA(te), prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_compressor", headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR), prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_offset", offset, prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_ms", duration, prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    state->psm_count += 1;
af2e7f
+    rc = RPMRC_OK;
af2e7f
+exit:
af2e7f
+    headerFree(h);
af2e7f
+    free(prefix);
af2e7f
+    free(duration);
af2e7f
+    free(offset);
af2e7f
+    return rc;
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC measure_scriptlet_pre(rpmPlugin plugin,
af2e7f
+					const char *s_name, int type)
af2e7f
+{
af2e7f
+    struct measurestat *state = rpmPluginGetData(plugin);
af2e7f
+    if (clock_gettime(CLOCK_MONOTONIC, &state->scriptlet_start)) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to get scriptlet_start time\n"));
af2e7f
+        return RPMRC_FAIL;
af2e7f
+    }
af2e7f
+    return RPMRC_OK;
af2e7f
+}
af2e7f
+
af2e7f
+static rpmRC measure_scriptlet_post(rpmPlugin plugin,
af2e7f
+					const char *s_name, int type, int res)
af2e7f
+{
af2e7f
+    struct measurestat *state = rpmPluginGetData(plugin);
af2e7f
+    struct timespec end;
af2e7f
+    char *offset = NULL, *duration = NULL, *prefix = NULL;
af2e7f
+    rpmRC rc = RPMRC_FAIL;
af2e7f
+
af2e7f
+    if (clock_gettime(CLOCK_MONOTONIC, &end)) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to get end time\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+
af2e7f
+    if (rasprintf(&prefix, "_measure_plugin_scriptlet_%d", state->scriptlet_count) == -1) {
af2e7f
+        rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted prefix\n"));
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (diff_ms(&offset, &state->plugin_start, &state->scriptlet_start) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (diff_ms(&duration, &state->scriptlet_start, &end) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_name", s_name, prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_offset", offset, prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    if (push("%s_ms", duration, prefix) != RPMRC_OK) {
af2e7f
+        goto exit;
af2e7f
+    }
af2e7f
+    state->scriptlet_count += 1;
af2e7f
+    rc = RPMRC_OK;
af2e7f
+exit:
af2e7f
+    free(prefix);
af2e7f
+    free(duration);
af2e7f
+    free(offset);
af2e7f
+    return rc;
af2e7f
+}
af2e7f
+
af2e7f
+struct rpmPluginHooks_s measure_hooks = {
af2e7f
+    .init = measure_init,
af2e7f
+    .cleanup = measure_cleanup,
af2e7f
+    .tsm_post = measure_tsm_post,
af2e7f
+    .psm_pre = measure_psm_pre,
af2e7f
+    .psm_post = measure_psm_post,
af2e7f
+    .scriptlet_pre = measure_scriptlet_pre,
af2e7f
+    .scriptlet_post = measure_scriptlet_post,
af2e7f
+};
af2e7f
-- 
af2e7f
2.47.1
af2e7f