diff mbox series

[RFC,v1,4/7] bootstage: add support for reporting in InfluxDB v2 line format

Message ID 20250411153040.1772000-5-jerome.forissier@linaro.org
State New
Headers show
Series Bootstage reports for CI | expand

Commit Message

Jerome Forissier April 11, 2025, 3:29 p.m. UTC
Add a new Kconfig symbol: BOOTSTAGE_REPORT_INFLUXDB to report the
bootstage timing information in InfluxDB v2 line protocol format in
addition to the human-readable text format. InfluxDB provides an easy
way to record boot statistics during CI in order to detect performance
regressions.

[1] https://docs.influxdata.com/influxdb/v2/reference/syntax/line-protocol/

Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---

 boot/Kconfig       |  10 ++++
 common/bootstage.c | 133 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+)
diff mbox series

Patch

diff --git a/boot/Kconfig b/boot/Kconfig
index 5e90e20141a..6249f3bb0c9 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1174,6 +1174,16 @@  config BOOTSTAGE_REPORT_JSON
 	  Enable output of the boot time report in JSON format in addition to
 	  the human-readable text format.
 
+config BOOTSTAGE_REPORT_INFLUXDB
+	bool "Display boot timing report in InfluxDB v2 line protocol"
+	depends on BOOTSTAGE_REPORT
+	help
+	  Enable output of the boot time report in InfluxDB v2 line protocol
+	  format in addition to the human-readable text format.
+	  The report may be uploaded to the InfluxDB Cloud via an HTTPS POST.
+	  See https://docs.influxdata.com/influxdb/v2/reference/syntax/line-protocol/
+	  and https://docs.influxdata.com/influxdb/v2/write-data/developer-tools/api/.
+
 config BOOTSTAGE_RECORD_COUNT
 	int "Number of boot stage records to store"
 	depends on BOOTSTAGE
diff --git a/common/bootstage.c b/common/bootstage.c
index 1ab55ecbe8f..9009882ef73 100644
--- a/common/bootstage.c
+++ b/common/bootstage.c
@@ -497,6 +497,137 @@  static void bootstage_report_json(void)
 	puts("=== End JSON bootstage report ===\n");
 }
 
+/**
+ * puts_influxdb_escape() - Print a string, escaping the characters that have a
+ * special meaning in the InfluxDB v2 line protocol
+ *
+ * @str: the string to print
+ */
+static void puts_influxdb_escape(const char *str)
+{
+	const char *p = str;
+
+	while (p && *p) {
+		if (*p == ' ' || *p == ',' || *p == '=')
+			putc('\\');
+		putc(*p);
+		p++;
+	}
+}
+
+/**
+ * print_time_record_influxdb() - print a time entry in InfluxDB v2 line
+ * protocolformat for a bootstage record or a couple of bootstage records.
+ *
+ * The function prints [,]key_name=value
+ *
+ * - If @rec->start_us is non-zero, it means @rec holds accumulated time. In
+ *   this case, key_name is the unique record name and value is @rec->time_us.
+ * - Otherwise, @rec represents a boot stage with an associated timestamp. The
+ *   key name is obtained by concatenating the previous record name and the
+ *   current record name, separated by a tilda. The value is the elapsed time
+ *   between the two stages, that is: @rec->time_us - @prev->time_us.
+ *
+ * @rec: the record to print
+ * @prev: the previous timestamp record (used as a reference when @rec is a
+ * timestamp)
+ * @is_first: true if this is the first reported data (won't print a
+ * continuation comma first ',')
+ * Returns @rec if it is a timestamp, @prev otherwise
+ */
+static struct bootstage_record *
+print_time_record_influxdb(struct bootstage_record *rec,
+			   struct bootstage_record *prev, bool is_first)
+{
+	char buf1[24];
+	char buf2[24];
+
+	if (!is_first)
+		puts(",");
+	if (rec->start_us) {
+		/* An "Accumulated time" entry in the text report */
+		printf("%s=%lu",
+		       get_unique_record_name(buf1, sizeof(buf1), rec),
+		       rec->time_us);
+		return prev;
+	}
+
+	/* Elapsed time between two consecutive stages */
+	printf("%s~%s=%lu",
+	       get_unique_record_name(buf1, sizeof(buf1), prev),
+	       get_unique_record_name(buf2, sizeof(buf2), rec),
+	       rec->time_us - prev->time_us);
+
+	return rec;
+}
+
+/**
+ * print_env_influxdb() - print an environment variable in InfluxDB v2 line
+ * protocol format
+ *
+ * @env: the variable to print
+ * @cont: true if a continuation comma ', ' should be printed afterwards
+ */
+static void print_env_influxdb(const char *env, bool cont)
+{
+	char *val = env_get(env);
+
+	puts("env_");
+	puts(env);
+	puts("=\"");
+	if (val)
+		puts_influxdb_escape(val);
+	puts("\"");
+	if (cont)
+		puts(",");
+}
+
+/**
+ * bootstage_report_influxdb() - print the InfluxDB bootstage report
+ */
+static void bootstage_report_influxdb(void)
+{
+	struct bootstage_data *data = gd->bootstage;
+	struct bootstage_record *prev = data->record;
+	struct bootstage_record *rec = data->record;
+	struct bootstage_record *boot_end = NULL;
+	bool is_first = true;
+	int i;
+
+	puts("=== Begin InfluxDB v2 bootstage report ===\n");
+	puts("u-boot_bootstage_report,");
+	puts("u_boot_version=\"");
+	puts_influxdb_escape(PLAIN_VERSION);
+	puts("\",");
+	puts("u_boot_date=\"");
+	puts_influxdb_escape(U_BOOT_DATE);
+	puts("\",");
+	puts("u_boot_time=\"");
+	puts_influxdb_escape(U_BOOT_TIME);
+	puts("\",");
+	puts("u_boot_tz=\"");
+	puts_influxdb_escape(U_BOOT_TZ);
+	puts("\",");
+	print_env_influxdb("arch", 1);
+	print_env_influxdb("board", 1);
+	print_env_influxdb("board_name", 1);
+	print_env_influxdb("cpu", 1);
+	print_env_influxdb("vendor", 0);
+	puts(" ");
+	for (i = 1, rec++; i < data->rec_count; i++, rec++) {
+		if (rec->id) {
+			if (!rec->start_us)
+				boot_end = rec;
+			prev = print_time_record_influxdb(rec, prev, is_first);
+			is_first = false;
+		}
+	}
+	if (boot_end)
+		printf(",total=%ld", boot_end->time_us);
+	puts("\n");
+	puts("=== End InfluxDB v2 bootstage report ===\n");
+}
+
 /**
  * bootstage_report() - print the bootstage report(s)
  */
@@ -505,6 +636,8 @@  void bootstage_report(void)
 	bootstage_report_text();
 	if (CONFIG_IS_ENABLED(BOOTSTAGE_REPORT_JSON))
 		bootstage_report_json();
+	if (CONFIG_IS_ENABLED(BOOTSTAGE_REPORT_INFLUXDB))
+		bootstage_report_influxdb();
 }
 
 /**