From ca33c4c20399d9283bab30516146bbbeea15d2cd Mon Sep 17 00:00:00 2001 From: Matthew Almond Date: Aug 04 2021 00:01:54 +0000 Subject: Measure plugin This plugin captures some rough timing numbers for package installation time. It uses rpm macros for RPC (yes, really). This has been in use for a while at Facebook internally, and has been included to allow us to use the CentOS Hyperscale SIG packaging infra. This plugin may be revisited in the future. --- diff --git a/SOURCES/measure.patch b/SOURCES/measure.patch new file mode 100644 index 0000000..b0c580f --- /dev/null +++ b/SOURCES/measure.patch @@ -0,0 +1,279 @@ +From 517a37ae7beeb77e2b4870be11611f82c1200b3c Mon Sep 17 00:00:00 2001 +From: Matthew Almond +Date: Thu, 11 Jun 2020 13:01:04 -0700 +Subject: [PATCH 2/2] Measure plugin + +--- + macros.in | 1 + + plugins/Makefile.am | 4 + + plugins/measure.c | 231 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 236 insertions(+) + create mode 100644 plugins/measure.c + +diff --git a/macros.in b/macros.in +index 3cc8a3555..c8a087959 100644 +--- a/macros.in ++++ b/macros.in +@@ -1173,6 +1173,7 @@ package or when debugging this package.\ + # Transaction plugin macros + %__plugindir %{_libdir}/rpm-plugins + %__transaction_reflink %{__plugindir}/reflink.so ++%__transaction_measure %{__plugindir}/measure.so + %__transaction_systemd_inhibit %{__plugindir}/systemd_inhibit.so + %__transaction_selinux %{__plugindir}/selinux.so + %__transaction_syslog %{__plugindir}/syslog.so +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index 06393ce8d..daed6423c 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -29,6 +29,10 @@ systemd_inhibit_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/ + plugins_LTLIBRARIES += systemd_inhibit.la + endif + ++measure_la_SOURCES = measure.c ++measure_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la ++plugins_LTLIBRARIES += measure.la ++ + prioreset_la_SOURCES = prioreset.c + prioreset_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la + plugins_LTLIBRARIES += prioreset.la +diff --git a/plugins/measure.c b/plugins/measure.c +new file mode 100644 +index 000000000..2cdfc885e +--- /dev/null ++++ b/plugins/measure.c +@@ -0,0 +1,231 @@ ++#include "system.h" ++#include "time.h" ++ ++#include ++#include ++#include ++#include "lib/rpmlib.h" ++ ++#include "lib/rpmplugin.h" ++ ++struct measurestat { ++ /* We're counting psm not packages because packages often run psm_pre/post ++ more than once and we want to accumulate the time ++ */ ++ unsigned int psm_count; ++ unsigned int scriptlet_count; ++ struct timespec plugin_start; ++ struct timespec psm_start; ++ struct timespec scriptlet_start; ++}; ++ ++static rpmRC push(const char *format, const char *value, const char *formatted) ++{ ++ char *key = NULL; ++ rpmRC rc = RPMRC_FAIL; ++ if (formatted == NULL) { ++ /* yes we're okay with discarding const here */ ++ key = (char *)format; ++ } else { ++ if (rasprintf(&key, format, formatted) == -1) { ++ rpmlog( ++ RPMLOG_ERR, ++ _("measure: Failed to allocate formatted key %s, %s\n"), ++ format, ++ formatted ++ ); ++ goto exit; ++ } ++ } ++ if (rpmPushMacro(NULL, key, NULL, value, RMIL_GLOBAL)) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to set %s\n"), key); ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ if (formatted != NULL) { ++ free(key); ++ } ++ return rc; ++} ++ ++static rpmRC diff_ms(char **ms, struct timespec *start, struct timespec *end) ++{ ++ if (rasprintf(ms, "%ld", ( ++ (end->tv_sec - start->tv_sec) * 1000 + ++ (end->tv_nsec - start->tv_nsec) / 1000000 ++ )) == -1) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted ms\n")); ++ return RPMRC_FAIL; ++ } ++ return RPMRC_OK; ++} ++ ++static rpmRC measure_init(rpmPlugin plugin, rpmts ts) ++{ ++ rpmRC rc = RPMRC_FAIL; ++ struct measurestat *state = rcalloc(1, sizeof(*state)); ++ if (clock_gettime(CLOCK_MONOTONIC, &state->plugin_start)) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to get plugin_start time\n")); ++ goto exit; ++ } ++ state->psm_count = 0; ++ state->scriptlet_count = 0; ++ rpmPluginSetData(plugin, state); ++ rc = RPMRC_OK; ++exit: ++ return rc; ++} ++ ++static void measure_cleanup(rpmPlugin plugin) ++{ ++ struct measurestat *state = rpmPluginGetData(plugin); ++ free(state); ++} ++ ++static rpmRC measure_tsm_post(rpmPlugin plugin, rpmts ts, int res) ++{ ++ struct measurestat *state = rpmPluginGetData(plugin); ++ char *psm_count = NULL, *scriptlet_count = NULL; ++ rpmRC rc = RPMRC_FAIL; ++ ++ if (rasprintf(&psm_count, "%d", state->psm_count) == -1) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted psm_count\n")); ++ goto exit; ++ } ++ if (rasprintf(&scriptlet_count, "%d", state->scriptlet_count) == -1) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted scriptlet_count\n")); ++ goto exit; ++ } ++ if (push("_measure_plugin_psm_count", psm_count, NULL) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("_measure_plugin_scriptlet_count", scriptlet_count, NULL) != RPMRC_OK) { ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ free(psm_count); ++ free(scriptlet_count); ++ return rc; ++} ++ ++static rpmRC measure_psm_pre(rpmPlugin plugin, rpmte te) ++{ ++ struct measurestat *state = rpmPluginGetData(plugin); ++ rpmRC rc = RPMRC_FAIL; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &state->psm_start)) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to get psm_start time\n")); ++ goto exit; ++ } ++ rc = RPMRC_OK; ++exit: ++ return rc; ++} ++ ++static rpmRC measure_psm_post(rpmPlugin plugin, rpmte te, int res) ++{ ++ struct measurestat *state = rpmPluginGetData(plugin); ++ struct timespec end; ++ char *offset = NULL, *duration = NULL, *prefix = NULL; ++ Header h = rpmteHeader(te); ++ rpmRC rc = RPMRC_FAIL; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &end)) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to get psm end time\n")); ++ goto exit; ++ } ++ if (rasprintf(&prefix, "_measure_plugin_package_%u", state->psm_count) == -1) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to allocate prefix\n")); ++ goto exit; ++ } ++ if (diff_ms(&offset, &state->plugin_start, &state->psm_start) != RPMRC_OK) { ++ goto exit; ++ } ++ if (diff_ms(&duration, &state->psm_start, &end) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_nevra", rpmteNEVRA(te), prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_compressor", headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR), prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_offset", offset, prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_ms", duration, prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ state->psm_count += 1; ++ rc = RPMRC_OK; ++exit: ++ headerFree(h); ++ free(prefix); ++ free(duration); ++ free(offset); ++ return rc; ++} ++ ++static rpmRC measure_scriptlet_pre(rpmPlugin plugin, ++ const char *s_name, int type) ++{ ++ struct measurestat *state = rpmPluginGetData(plugin); ++ if (clock_gettime(CLOCK_MONOTONIC, &state->scriptlet_start)) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to get scriptlet_start time\n")); ++ return RPMRC_FAIL; ++ } ++ return RPMRC_OK; ++} ++ ++static rpmRC measure_scriptlet_post(rpmPlugin plugin, ++ const char *s_name, int type, int res) ++{ ++ struct measurestat *state = rpmPluginGetData(plugin); ++ struct timespec end; ++ char *offset = NULL, *duration = NULL, *prefix = NULL; ++ rpmRC rc = RPMRC_FAIL; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &end)) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to get end time\n")); ++ goto exit; ++ } ++ ++ if (rasprintf(&prefix, "_measure_plugin_scriptlet_%d", state->scriptlet_count) == -1) { ++ rpmlog(RPMLOG_ERR, _("measure: Failed to allocate formatted prefix\n")); ++ goto exit; ++ } ++ if (diff_ms(&offset, &state->plugin_start, &state->scriptlet_start) != RPMRC_OK) { ++ goto exit; ++ } ++ if (diff_ms(&duration, &state->scriptlet_start, &end) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_name", s_name, prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_offset", offset, prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ if (push("%s_ms", duration, prefix) != RPMRC_OK) { ++ goto exit; ++ } ++ state->scriptlet_count += 1; ++ rc = RPMRC_OK; ++exit: ++ free(prefix); ++ free(duration); ++ free(offset); ++ return rc; ++} ++ ++struct rpmPluginHooks_s measure_hooks = { ++ .init = measure_init, ++ .cleanup = measure_cleanup, ++ .tsm_post = measure_tsm_post, ++ .psm_pre = measure_psm_pre, ++ .psm_post = measure_psm_post, ++ .scriptlet_pre = measure_scriptlet_pre, ++ .scriptlet_post = measure_scriptlet_post, ++}; +-- +2.24.1 + diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec index bf701e7..5a527c6 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -32,7 +32,7 @@ %global rpmver 4.14.3 #global snapver rc2 -%global rel 15.3 +%global rel 15.4 %global srcver %{version}%{?snapver:-%{snapver}} %global srcdir %{?snapver:testing}%{!?snapver:%{name}-%(echo %{version} | cut -d'.' -f1-2).x} @@ -183,6 +183,7 @@ Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch Provides: rpm(pr1381) Patch9991: https://github.com/rpm-software-management/rpm/pull/1470.patch Provides: rpm(pr1470) +Patch9999: measure.patch # Partially GPL/LGPL dual-licensed and some bits with BSD # SourceLicense: (GPLv2+ and LGPLv2+ with exceptions) and BSD @@ -479,6 +480,14 @@ Requires: rpm-libs%{_isa} = %{version}-%{release} %description plugin-reflink %{summary}. +%package plugin-measure +Summary: Rpm plugin for measure +Group: System Environment/Base +Requires: rpm-libs%{_isa} = %{version}-%{release} + +%description plugin-measure +Adds measure support + %endif # with plugins %prep @@ -698,6 +707,9 @@ make check || cat tests/rpmtests.log %files plugin-reflink %{_bindir}/rpm2extents %{_libdir}/rpm-plugins/reflink.so + +%files plugin-measure +%{_libdir}/rpm-plugins/measure.so %endif # with plugins %files build-libs @@ -760,6 +772,9 @@ make check || cat tests/rpmtests.log %doc doc/librpm/html/* %changelog +* Tue Aug 03 2021 Matthew Almond - 4.14.3-15.4 +- Add measure plugin + * Tue Aug 03 2021 Matthew Almond - 4.14.3-15.3 - Move rpm2extents to plugin package