1 #include <linux/kernel.h> 2 #include "cache.h" 3 #include "color.h" 4 #include <math.h> 5 6 int perf_use_color_default = -1; 7 8 static int parse_color(const char *name, int len) 9 { 10 static const char * const color_names[] = { 11 "normal", "black", "red", "green", "yellow", 12 "blue", "magenta", "cyan", "white" 13 }; 14 char *end; 15 int i; 16 17 for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) { 18 const char *str = color_names[i]; 19 if (!strncasecmp(name, str, len) && !str[len]) 20 return i - 1; 21 } 22 i = strtol(name, &end, 10); 23 if (end - name == len && i >= -1 && i <= 255) 24 return i; 25 return -2; 26 } 27 28 static int parse_attr(const char *name, int len) 29 { 30 static const int attr_values[] = { 1, 2, 4, 5, 7 }; 31 static const char * const attr_names[] = { 32 "bold", "dim", "ul", "blink", "reverse" 33 }; 34 unsigned int i; 35 36 for (i = 0; i < ARRAY_SIZE(attr_names); i++) { 37 const char *str = attr_names[i]; 38 if (!strncasecmp(name, str, len) && !str[len]) 39 return attr_values[i]; 40 } 41 return -1; 42 } 43 44 void color_parse(const char *value, const char *var, char *dst) 45 { 46 color_parse_mem(value, strlen(value), var, dst); 47 } 48 49 void color_parse_mem(const char *value, int value_len, const char *var, 50 char *dst) 51 { 52 const char *ptr = value; 53 int len = value_len; 54 int attr = -1; 55 int fg = -2; 56 int bg = -2; 57 58 if (!strncasecmp(value, "reset", len)) { 59 strcpy(dst, PERF_COLOR_RESET); 60 return; 61 } 62 63 /* [fg [bg]] [attr] */ 64 while (len > 0) { 65 const char *word = ptr; 66 int val, wordlen = 0; 67 68 while (len > 0 && !isspace(word[wordlen])) { 69 wordlen++; 70 len--; 71 } 72 73 ptr = word + wordlen; 74 while (len > 0 && isspace(*ptr)) { 75 ptr++; 76 len--; 77 } 78 79 val = parse_color(word, wordlen); 80 if (val >= -1) { 81 if (fg == -2) { 82 fg = val; 83 continue; 84 } 85 if (bg == -2) { 86 bg = val; 87 continue; 88 } 89 goto bad; 90 } 91 val = parse_attr(word, wordlen); 92 if (val < 0 || attr != -1) 93 goto bad; 94 attr = val; 95 } 96 97 if (attr >= 0 || fg >= 0 || bg >= 0) { 98 int sep = 0; 99 100 *dst++ = '\033'; 101 *dst++ = '['; 102 if (attr >= 0) { 103 *dst++ = '0' + attr; 104 sep++; 105 } 106 if (fg >= 0) { 107 if (sep++) 108 *dst++ = ';'; 109 if (fg < 8) { 110 *dst++ = '3'; 111 *dst++ = '0' + fg; 112 } else { 113 dst += sprintf(dst, "38;5;%d", fg); 114 } 115 } 116 if (bg >= 0) { 117 if (sep++) 118 *dst++ = ';'; 119 if (bg < 8) { 120 *dst++ = '4'; 121 *dst++ = '0' + bg; 122 } else { 123 dst += sprintf(dst, "48;5;%d", bg); 124 } 125 } 126 *dst++ = 'm'; 127 } 128 *dst = 0; 129 return; 130 bad: 131 die("bad color value '%.*s' for variable '%s'", value_len, value, var); 132 } 133 134 int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) 135 { 136 if (value) { 137 if (!strcasecmp(value, "never")) 138 return 0; 139 if (!strcasecmp(value, "always")) 140 return 1; 141 if (!strcasecmp(value, "auto")) 142 goto auto_color; 143 } 144 145 /* Missing or explicit false to turn off colorization */ 146 if (!perf_config_bool(var, value)) 147 return 0; 148 149 /* any normal truth value defaults to 'auto' */ 150 auto_color: 151 if (stdout_is_tty < 0) 152 stdout_is_tty = isatty(1); 153 if (stdout_is_tty || (pager_in_use() && pager_use_color)) { 154 char *term = getenv("TERM"); 155 if (term && strcmp(term, "dumb")) 156 return 1; 157 } 158 return 0; 159 } 160 161 int perf_color_default_config(const char *var, const char *value, void *cb) 162 { 163 if (!strcmp(var, "color.ui")) { 164 perf_use_color_default = perf_config_colorbool(var, value, -1); 165 return 0; 166 } 167 168 return perf_default_config(var, value, cb); 169 } 170 171 static int __color_vsnprintf(char *bf, size_t size, const char *color, 172 const char *fmt, va_list args, const char *trail) 173 { 174 int r = 0; 175 176 /* 177 * Auto-detect: 178 */ 179 if (perf_use_color_default < 0) { 180 if (isatty(1) || pager_in_use()) 181 perf_use_color_default = 1; 182 else 183 perf_use_color_default = 0; 184 } 185 186 if (perf_use_color_default && *color) 187 r += scnprintf(bf, size, "%s", color); 188 r += vscnprintf(bf + r, size - r, fmt, args); 189 if (perf_use_color_default && *color) 190 r += scnprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); 191 if (trail) 192 r += scnprintf(bf + r, size - r, "%s", trail); 193 return r; 194 } 195 196 static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, 197 va_list args, const char *trail) 198 { 199 int r = 0; 200 201 /* 202 * Auto-detect: 203 */ 204 if (perf_use_color_default < 0) { 205 if (isatty(fileno(fp)) || pager_in_use()) 206 perf_use_color_default = 1; 207 else 208 perf_use_color_default = 0; 209 } 210 211 if (perf_use_color_default && *color) 212 r += fprintf(fp, "%s", color); 213 r += vfprintf(fp, fmt, args); 214 if (perf_use_color_default && *color) 215 r += fprintf(fp, "%s", PERF_COLOR_RESET); 216 if (trail) 217 r += fprintf(fp, "%s", trail); 218 return r; 219 } 220 221 int color_vsnprintf(char *bf, size_t size, const char *color, 222 const char *fmt, va_list args) 223 { 224 return __color_vsnprintf(bf, size, color, fmt, args, NULL); 225 } 226 227 int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args) 228 { 229 return __color_vfprintf(fp, color, fmt, args, NULL); 230 } 231 232 int color_snprintf(char *bf, size_t size, const char *color, 233 const char *fmt, ...) 234 { 235 va_list args; 236 int r; 237 238 va_start(args, fmt); 239 r = color_vsnprintf(bf, size, color, fmt, args); 240 va_end(args); 241 return r; 242 } 243 244 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) 245 { 246 va_list args; 247 int r; 248 249 va_start(args, fmt); 250 r = color_vfprintf(fp, color, fmt, args); 251 va_end(args); 252 return r; 253 } 254 255 int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) 256 { 257 va_list args; 258 int r; 259 va_start(args, fmt); 260 r = __color_vfprintf(fp, color, fmt, args, "\n"); 261 va_end(args); 262 return r; 263 } 264 265 /* 266 * This function splits the buffer by newlines and colors the lines individually. 267 * 268 * Returns 0 on success. 269 */ 270 int color_fwrite_lines(FILE *fp, const char *color, 271 size_t count, const char *buf) 272 { 273 if (!*color) 274 return fwrite(buf, count, 1, fp) != 1; 275 276 while (count) { 277 char *p = memchr(buf, '\n', count); 278 279 if (p != buf && (fputs(color, fp) < 0 || 280 fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 || 281 fputs(PERF_COLOR_RESET, fp) < 0)) 282 return -1; 283 if (!p) 284 return 0; 285 if (fputc('\n', fp) < 0) 286 return -1; 287 count -= p + 1 - buf; 288 buf = p + 1; 289 } 290 return 0; 291 } 292 293 const char *get_percent_color(double percent) 294 { 295 const char *color = PERF_COLOR_NORMAL; 296 297 /* 298 * We color high-overhead entries in red, mid-overhead 299 * entries in green - and keep the low overhead places 300 * normal: 301 */ 302 if (fabs(percent) >= MIN_RED) 303 color = PERF_COLOR_RED; 304 else { 305 if (fabs(percent) > MIN_GREEN) 306 color = PERF_COLOR_GREEN; 307 } 308 return color; 309 } 310 311 int percent_color_fprintf(FILE *fp, const char *fmt, double percent) 312 { 313 int r; 314 const char *color; 315 316 color = get_percent_color(percent); 317 r = color_fprintf(fp, color, fmt, percent); 318 319 return r; 320 } 321 322 int value_color_snprintf(char *bf, size_t size, const char *fmt, double value) 323 { 324 const char *color = get_percent_color(value); 325 return color_snprintf(bf, size, color, fmt, value); 326 } 327 328 int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) 329 { 330 va_list args; 331 double percent; 332 333 va_start(args, fmt); 334 percent = va_arg(args, double); 335 va_end(args); 336 return value_color_snprintf(bf, size, fmt, percent); 337 } 338