From f7d610985554e52c0ce303675786417c03b11e03 Mon Sep 17 00:00:00 2001 From: Andrew Adams Date: Mon, 24 Aug 2026 15:10:21 -0700 Subject: [PATCH 1/3] Only colorize the profiler report when writing to a terminal The report gated ANSI color escapes on the TERM environment variable alone. CI and other redirected environments commonly set TERM=xterm-256color while stdout is a pipe or file, so the escapes were written straight into the captured log as noise. Also require isatty(STDOUT_FILENO) (the report is printed via halide_print, whose default writes to stdout), and honor the NO_COLOR convention. The no-color path already emits plain box-drawing separators, so the table stays well-formed. Co-Authored-By: Claude Opus 4.8 --- src/runtime/profiler_common.cpp | 14 ++++++++++---- src/runtime/runtime_internal.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/runtime/profiler_common.cpp b/src/runtime/profiler_common.cpp index 2384c38f4aca..0d0d27e45593 100644 --- a/src/runtime/profiler_common.cpp +++ b/src/runtime/profiler_common.cpp @@ -513,11 +513,17 @@ ALWAYS_INLINE bool counter_is_approximate(const halide_profiler_func_stats *fs, WEAK void halide_profiler_report_unlocked(void *user_context, halide_profiler_state *s) { StringStreamPrinter<1024> sstr(user_context); - bool support_colors = false; + // Emit ANSI color escapes only when the report is going to an actual + // color-capable terminal. Checking TERM alone isn't enough: CI and other + // redirected environments often set TERM=xterm-256color while stdout is a + // pipe or file, which would splatter escape codes into the captured log. + // The report is printed via halide_print, whose default writes to stdout. + const char *no_color = getenv("NO_COLOR"); const char *term = getenv("TERM"); - if (term && (strstr(term, "color") || strstr(term, "xterm"))) { - support_colors = true; - } + bool support_colors = + !(no_color && no_color[0]) && + term && (strstr(term, "color") || strstr(term, "xterm")) && + isatty(STDOUT_FILENO); // Column-aligned rows are produced from `const char *` templates. A // run of an uppercase marker char is a slot — the marker picks the diff --git a/src/runtime/runtime_internal.h b/src/runtime/runtime_internal.h index fdf951c7dc87..11c63c94b274 100644 --- a/src/runtime/runtime_internal.h +++ b/src/runtime/runtime_internal.h @@ -121,6 +121,7 @@ int fclose(void *); int close(int); size_t fwrite(const void *, size_t, size_t, void *); ssize_t write(int fd, const void *buf, size_t bytes); +int isatty(int fd); int remove(const char *pathname); int ioctl(int fd, unsigned long request, ...); char *strncpy(char *dst, const char *src, size_t n); From b3a02cf9783d5c71b4e28fc9112464705a0d20b5 Mon Sep 17 00:00:00 2001 From: Andrew Adams Date: Mon, 31 Aug 2026 12:34:51 -0700 Subject: [PATCH 2/3] Profiler: honor HL_COLORS in the report color gate Match IRPrinter's HL_COLORS handling: if HL_COLORS is set it is an explicit override ("0" off, anything else on); otherwise fall back to honoring NO_COLOR and auto-detecting a color-capable terminal. Also add isatty to the list of posix calls that get an underscore prefix on Windows (_isatty). Co-Authored-By: Claude Opus 4.8 --- src/LLVM_Runtime_Linker.cpp | 2 +- src/runtime/profiler_common.cpp | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/LLVM_Runtime_Linker.cpp b/src/LLVM_Runtime_Linker.cpp index dc612a15ddd7..2f90ec424f8c 100644 --- a/src/LLVM_Runtime_Linker.cpp +++ b/src/LLVM_Runtime_Linker.cpp @@ -826,7 +826,7 @@ void add_underscore_to_posix_call(llvm::CallInst *call, llvm::Function *fn, llvm * of mcjit, so we just rewrite uses of these functions to include an * underscore. */ void add_underscores_to_posix_calls_on_windows(llvm::Module *m) { - string posix_fns[] = {"vsnprintf", "open", "close", "write", "fileno"}; + string posix_fns[] = {"vsnprintf", "open", "close", "write", "fileno", "isatty"}; string *posix_fns_begin = posix_fns; string *posix_fns_end = posix_fns + sizeof(posix_fns) / sizeof(posix_fns[0]); diff --git a/src/runtime/profiler_common.cpp b/src/runtime/profiler_common.cpp index 0d0d27e45593..40e105f812e3 100644 --- a/src/runtime/profiler_common.cpp +++ b/src/runtime/profiler_common.cpp @@ -513,17 +513,25 @@ ALWAYS_INLINE bool counter_is_approximate(const halide_profiler_func_stats *fs, WEAK void halide_profiler_report_unlocked(void *user_context, halide_profiler_state *s) { StringStreamPrinter<1024> sstr(user_context); - // Emit ANSI color escapes only when the report is going to an actual - // color-capable terminal. Checking TERM alone isn't enough: CI and other - // redirected environments often set TERM=xterm-256color while stdout is a - // pipe or file, which would splatter escape codes into the captured log. - // The report is printed via halide_print, whose default writes to stdout. - const char *no_color = getenv("NO_COLOR"); - const char *term = getenv("TERM"); - bool support_colors = - !(no_color && no_color[0]) && - term && (strstr(term, "color") || strstr(term, "xterm")) && - isatty(STDOUT_FILENO); + // Decide whether to emit ANSI color escapes. HL_COLORS, if set, is an + // explicit override matching IRPrinter: "0" forces colors off, anything + // else on. Otherwise honor NO_COLOR and auto-detect a color-capable + // terminal. Checking TERM alone isn't enough: CI and other redirected + // environments often set TERM=xterm-256color while stdout is a pipe or + // file, which would splatter escape codes into the captured log. The + // report is printed via halide_print, whose default writes to stdout. + bool support_colors; + const char *hl_colors = getenv("HL_COLORS"); + if (hl_colors) { + support_colors = atoi(hl_colors) != 0; + } else { + const char *no_color = getenv("NO_COLOR"); + const char *term = getenv("TERM"); + support_colors = + !(no_color && no_color[0]) && + term && (strstr(term, "color") || strstr(term, "xterm")) && + isatty(STDOUT_FILENO); + } // Column-aligned rows are produced from `const char *` templates. A // run of an uppercase marker char is a slot — the marker picks the From 26df0921e6ce4bdaab4a80ecdb54688763679f04 Mon Sep 17 00:00:00 2001 From: Andrew Adams Date: Tue, 1 Sep 2026 11:42:15 -0700 Subject: [PATCH 3/3] IRPrinter: respect NO_COLOR in the color gate Match the profiler report's logic exactly: HL_COLORS is an explicit override (atoi() != 0); otherwise honor NO_COLOR in addition to detecting a color-capable terminal. Previously NO_COLOR was ignored here. Co-Authored-By: Claude Opus 4.8 --- src/IRPrinter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/IRPrinter.cpp b/src/IRPrinter.cpp index bb5938fbd745..3a4caa581a66 100644 --- a/src/IRPrinter.cpp +++ b/src/IRPrinter.cpp @@ -561,7 +561,10 @@ IRPrinter::IRPrinter(ostream &s) int val = std::atoi(opt); use_colors = val != 0; } else { - use_colors = supports_ansi(stream); + // Respect NO_COLOR in addition to whether we're writing to a + // terminal, matching the profiler report's color gate. + const char *no_color = getenv("NO_COLOR"); + use_colors = !(no_color && no_color[0]) && supports_ansi(stream); } if (use_colors) { ansi = true;