From bcbe827824b82a6ee001a4b2aa8aba6f4f610f0a Mon Sep 17 00:00:00 2001 From: Kyle Singer Date: Wed, 9 Oct 2024 11:59:08 -0400 Subject: [PATCH] String cilkrts alert options (#31) * allow string specification to enable cilk alerts * remove commented out code * add back missing logic related to buffering cilkrts_alert * remove extra whitespace * fix some stupid midnight coding issues * strtok and lfind * fix overwrite of null terminator * strdup and strcasecmp and constness * remove unecessary include * consistent null check * remove old env string fixing logic * Documentation; use more portable malloc+strcpy compared to strdup * slighlty more comments * back to strdup --------- Co-authored-by: Kyle Singer --- runtime/debug.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++ runtime/debug.h | 1 + runtime/global.c | 2 +- 3 files changed, 153 insertions(+), 1 deletion(-) diff --git a/runtime/debug.c b/runtime/debug.c index f08e5414..316ce6ce 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -3,6 +3,7 @@ #include "global.h" #include +#include #include #include #include @@ -20,6 +21,156 @@ CHEETAH_INTERNAL unsigned int debug_level = 0; static size_t alert_log_size = 0, alert_log_offset = 0; static char *alert_log = NULL; +/** + * Represents a usable alert level with a human-readable + * name<\code> and the corresponding + * mask_value<\code> used by the runtime. + **/ +typedef struct __alert_level_t { + const char *name; + int mask_value; +} alert_level_t; + +/** + * A table relating a human-readable alert level name to + * the corresponding bitfield value used by the runtime. + **/ +static const alert_level_t alert_table[] = { + {"none", ALERT_NONE}, + {"fiber", ALERT_FIBER}, + {"memory", ALERT_MEMORY}, + {"sync", ALERT_SYNC}, + {"sched", ALERT_SCHED}, + {"steal", ALERT_STEAL}, + {"return", ALERT_RETURN}, + {"except", ALERT_EXCEPT}, + {"cframe", ALERT_CFRAME}, + {"reduce", ALERT_REDUCE}, + {"reduce_id", ALERT_REDUCE_ID}, + {"boot", ALERT_BOOT}, + {"start", ALERT_START}, + {"closure", ALERT_CLOSURE}, + {"nobuf", ALERT_NOBUF}, +}; + +/** + * Compare the name<\code> of the alert_level_t<\code> pointed to by + * left to the name<\code> of the alert_level_t<\code> + * pointed to by right<\code>, ignoring case. + * + * @param left alert_level_t<\code> to compare + * @param right alert_level_t<\code> to compare + * + * @return Ignoring case, returns negative if left->name < right->name, + * 0 if they are equal, or positive if left->name > right->name + **/ +static int alert_name_comparison(const void *left, const void *right) { + const alert_level_t *al_left = (const alert_level_t*)left; + const alert_level_t *al_right = (const alert_level_t*)right; + + // TODO: If Windows, use _stricmp + return strcasecmp(al_left->name, al_right->name); +} + +/** + * Parse an alert level represented by a string into the proper bitmask value. + * If the string is not represented in alert_table<\code>, then prints + * an error and returns ALERT_NONE. + * + * @param alert_str A C string to attempt to parse + * + * @return The bitmask corresponding to alert_str<\code>, if in + * alert_table<\code>, else ALERT_NONE. + **/ +static int parse_alert_level_str(const char *const alert_str) { + size_t table_size = sizeof(alert_table) / sizeof(alert_table[0]); + + const alert_level_t search_key = { .name = alert_str, .mask_value = ALERT_NONE }; + + // The table is small, and performance isn't critical for loading + // debug options, so use linear search (lfind) + alert_level_t *table_element = + (alert_level_t*)lfind(&search_key, alert_table, &table_size, + sizeof(search_key), alert_name_comparison + ); + + if (table_element != NULL) { + return table_element->mask_value; + } + + fprintf(stderr, "Invalid CILK_ALERT value: %s\n", alert_str); + + return ALERT_NONE; +} + +/** + * Parse a CSV line representing which alert levels should be enabled, and + * return the bitmask representing all of the passed in options. If the CSV line + * is a single number, then treat that number as the bitmask. + * alert_csv<\code> is copied, as strtok<\code> is used, and it + * modifies its arguments. + * + * @param alert_csv A C string that is either a comma-separated list of alert + * level names -or- a single number. + * + * @return The bitmask representing the passed in + * alert_csv<\code>. If alert_csv<\code> cannot + * be copied, then returns the current + * alert_level<\code> value. + **/ +static int parse_alert_level_csv(const char *const alert_csv) { + int new_alert_lvl = ALERT_NONE; + + size_t csv_len = strlen(alert_csv); + + // strtok modifies the passed in string, so copy alert_csv and use + // the copy instead + char *alert_csv_cpy = strdup(alert_csv); + if (!alert_csv_cpy) { + // Non-critical error, so just print a warning + fprintf(stderr, "Cilk: unable to copy CILK_ALERT settings (%s)\n", + strerror(errno) + ); + return alert_level; + } + + char *alert_str = strtok(alert_csv_cpy, ","); + + if (alert_str) { + if (strlen(alert_str) == csv_len) { + // Can be a number, as there is no other option in the string + char *tol_end = alert_csv_cpy; + new_alert_lvl = strtol(alert_csv_cpy, &tol_end, 0); + if (new_alert_lvl == 0 && (*tol_end != '\0' || tol_end == alert_csv_cpy)) { + new_alert_lvl |= parse_alert_level_str(alert_str); + } + } else { + for (; alert_str; alert_str = strtok(NULL, ",")) { + new_alert_lvl |= parse_alert_level_str(alert_str); + } + } + } + + free(alert_csv_cpy); + + return new_alert_lvl; +} + +/** + * Parse a CSV line representing which alert levels should be enabled, and + * and set the current alert_level<\code> bitamsk based on the result. + * If the passed in C string is NULL, then no change is made. + * + * @param alert_csv A C string that is either a comma-separated list of alert + * level names -or- a single number. + **/ +void set_alert_level_from_str(const char *const alert_csv) { + if (alert_csv) { + int new_alert_lvl = parse_alert_level_csv(alert_csv); + set_alert_level(new_alert_lvl); + } +} + void set_alert_level(unsigned int level) { alert_level = level; if (level == 0) { diff --git a/runtime/debug.h b/runtime/debug.h index 2666de75..a4d026b7 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -54,6 +54,7 @@ extern CHEETAH_INTERNAL unsigned int debug_level; // Unused: compiler inlines the stack frame creation // #define CILK_STACKFRAME_MAGIC 0xCAFEBABE +CHEETAH_INTERNAL void set_alert_level_from_str(const char *const); CHEETAH_INTERNAL void set_alert_level(unsigned int); CHEETAH_INTERNAL void set_debug_level(unsigned int); CHEETAH_INTERNAL void flush_alert_log(void); diff --git a/runtime/global.c b/runtime/global.c index d3341242..12415c28 100644 --- a/runtime/global.c +++ b/runtime/global.c @@ -41,7 +41,7 @@ unsigned __cilkrts_nproc = 0; static void set_alert_debug_level() { /* Only the bits also set in ALERT_LVL are used. */ - set_alert_level(env_get_int("CILK_ALERT")); + set_alert_level_from_str(getenv("CILK_ALERT")); /* Only the bits also set in DEBUG_LVL are used. */ set_debug_level(env_get_int("CILK_DEBUG")); }