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