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.foriss...@linaro.org>
---

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

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();
 }
 
 /**
-- 
2.43.0

Reply via email to