[PATCH 2/3] Implement simple color output

JingPiao Chen chenjingpiao at gmail.com
Fri Mar 31 11:20:52 UTC 2017


Color output system call name, return value, error message,
fd, address and prefix pid.

* color.h: New file.
* Makefile.am (strace_SOURCES): Add color.h.
* defs.h (color_attr): New enum.
(color_tprintf, color_tprints): New prototype.
* syscall.c (trace_syscall_entering): Color output system call name.
(trace_syscall_exiting): Color output error msg, return value and fd.
* util.c (printfd): Color output fd.
(printaddr): Color output address.
* strace.1: Document -H option.
* strace.c (usage): Likewise.
(init): Handle -H option.
(color_tprints, color_tprintf): New function.
(printleader): Color output prefix pid.
(print_event_exit): Color output return value.
* NEWS: Mention this.
---
Hi, I implement simple color output, it only color output system call
name, return value, error message, fd, address and prefix pid, please
give me some feedback. Thank you.

 Makefile.am |  1 +
 NEWS        |  1 +
 color.h     | 29 +++++++++++++++++++++++++++++
 defs.h      | 14 ++++++++++++++
 strace.1    |  5 ++++-
 strace.c    | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 syscall.c   | 57 ++++++++++++++++++++++++++++++++++++---------------------
 util.c      |  9 +++++----
 8 files changed, 140 insertions(+), 31 deletions(-)
 create mode 100644 color.h

diff --git a/Makefile.am b/Makefile.am
index 8af709b..d299cbb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -96,6 +96,7 @@ strace_SOURCES =	\
 	chdir.c		\
 	chmod.c		\
 	clone.c		\
+	color.h		\
 	copy_file_range.c \
 	count.c		\
 	defs.h		\
diff --git a/NEWS b/NEWS
index 5e0941f..5390e8c 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Noteworthy changes in release ?.?? (????-??-??)
     powerpc, powerpc64, riscv, sh, sh64, sparc, sparc64, tile, x86, and xtensa
     architectures.
   * Implemented decoding of statx syscall.
+  * Implemented color output option.
   * Updated lists of ioctl commands from Linux 4.11.
 
 * Bug fixes
diff --git a/color.h b/color.h
new file mode 100644
index 0000000..8e58c90
--- /dev/null
+++ b/color.h
@@ -0,0 +1,29 @@
+#ifndef STRACE_COLOR_H
+#define STRACE_COLOR_H
+
+#define C_CLEAR		"\033[0m"
+#define C_BOLD		"\033[1m"
+#define C_HALFBRIGHT	"\033[2m"
+#define C_UNDERSCORE	"\033[4m"
+#define C_BLINK		"\033[5m"
+#define C_REVERSE	"\033[7m"
+
+#define C_BLACK		"\033[30m"
+#define C_RED		"\033[31m"
+#define C_GREEN		"\033[32m"
+#define C_BROWN		"\033[33m"
+#define C_BLUE		"\033[34m"
+#define C_MAGENTA	"\033[35m"
+#define C_CYAN		"\033[36m"
+#define C_GRAY		"\033[37m"
+
+#define C_DARK_GRAY	"\033[1;30m"
+#define C_BOLD_RED	"\033[1;31m"
+#define C_BOLD_GREEN	"\033[1;32m"
+#define C_BOLD_YELLOW	"\033[1;33m"
+#define C_BOLD_BLUE	"\033[1;34m"
+#define C_BOLD_MAGENTA	"\033[1;35m"
+#define C_BOLD_CYAN	"\033[1;36m"
+#define C_WHITE		"\033[1;37m"
+
+#endif /* !STRACE_COLOR_H*/
diff --git a/defs.h b/defs.h
index 793971e..834ddec 100644
--- a/defs.h
+++ b/defs.h
@@ -744,6 +744,20 @@ extern void tabto(void);
 extern void tprintf(const char *fmt, ...) ATTRIBUTE_FORMAT((printf, 1, 2));
 extern void tprints(const char *str);
 
+enum color_attr {
+	COLOR_SYS_NAME,
+	COLOR_RETURN_VALUE,
+	COLOR_ERROR_MSG,
+	COLOR_ADDRESS,
+	COLOR_PREFIX_PID,
+	COLOR_FD,
+	COLOR_CLEAR
+};
+
+extern void color_tprintf(enum color_attr attr, const char *fmt, ...)
+	ATTRIBUTE_FORMAT((printf, 2, 3));
+extern void color_tprints(enum color_attr attr, const char *str);
+
 #if SUPPORTED_PERSONALITIES > 1
 extern void set_personality(int personality);
 extern unsigned current_personality;
diff --git a/strace.1 b/strace.1
index 9b69ec2..525e13d 100644
--- a/strace.1
+++ b/strace.1
@@ -44,7 +44,7 @@
 strace \- trace system calls and signals
 .SH SYNOPSIS
 .B strace
-[\fB-CdffhikqrtttTvVxxy\fR]
+[\fB-CdffhHikqrtttTvVxxy\fR]
 [\fB-I\fIn\fR]
 [\fB-b\fIexecve\fR]
 [\fB-e\fIexpr\fR]...
@@ -208,6 +208,9 @@ Here the second argument represents the full set of all signals.
 .BI "\-a " column
 Align return values in a specific column (default column 40).
 .TP
+.B \-H
+Color output syscall name, return value, error message, fd, address and prefix pid.
+.TP
 .B \-i
 Print the instruction pointer at the time of the system call.
 .TP
diff --git a/strace.c b/strace.c
index 2b50cc0..9c4cbc5 100644
--- a/strace.c
+++ b/strace.c
@@ -45,6 +45,7 @@
 #endif
 #include <asm/unistd.h>
 
+#include "color.h"
 #include "scno.h"
 #include "ptrace.h"
 #include "printsiginfo.h"
@@ -144,6 +145,7 @@ static char *acolumn_spaces;
 static char *outfname = NULL;
 /* If -ff, points to stderr. Else, it's our common output log */
 static FILE *shared_log;
+static bool color_enabled = false;
 
 struct tcb *printing_tcp = NULL;
 static struct tcb *current_tcp;
@@ -200,7 +202,7 @@ static void
 usage(void)
 {
 	printf("\
-usage: strace [-CdffhiqrtttTvVwxxy] [-I n] [-e expr]...\n\
+usage: strace [-CdffhHiqrtttTvVwxxy] [-I n] [-e expr]...\n\
               [-a column] [-o file] [-s strsize] [-P path]...\n\
               -p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]\n\
    or: strace -c[dfw] [-I n] [-e expr]... [-O overhead] [-S sortby]\n\
@@ -208,6 +210,7 @@ usage: strace [-CdffhiqrtttTvVwxxy] [-I n] [-e expr]...\n\
 \n\
 Output format:\n\
   -a column      alignment COLUMN for printing syscall results (default %d)\n\
+  -H             color output syscall name, return value, error message, fd, etc.\n\
   -i             print instruction pointer at time of syscall\n\
 "
 #ifdef USE_LIBUNWIND
@@ -592,6 +595,44 @@ tprintf(const char *fmt, ...)
 	va_end(args);
 }
 
+static const char *colors[] = {
+	[COLOR_SYS_NAME]	= C_BROWN,
+	[COLOR_RETURN_VALUE]	= C_CYAN,
+	[COLOR_ERROR_MSG]	= C_RED,
+	[COLOR_ADDRESS]		= C_MAGENTA,
+	[COLOR_PREFIX_PID]	= C_GRAY,
+	[COLOR_FD]		= C_BLUE,
+	[COLOR_CLEAR]		= C_CLEAR
+};
+
+void color_tprints(enum color_attr attr, const char *str)
+{
+	if (color_enabled && isatty(fileno(current_tcp->outf))) {
+		fprintf(current_tcp->outf, "%s", colors[attr]);
+		tprints(str);
+		fprintf(current_tcp->outf, "%s", colors[COLOR_CLEAR]);
+	} else {
+		tprints(str);
+	}
+}
+
+void color_tprintf(enum color_attr attr, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if (color_enabled && isatty(fileno(current_tcp->outf))) {
+		fprintf(current_tcp->outf, "%s", colors[attr]);
+		vtprintf(fmt, args);
+		fprintf(current_tcp->outf, "%s", colors[COLOR_CLEAR]);
+	} else {
+		vtprintf(fmt, args);
+	}
+
+	va_end(args);
+}
+
 #ifndef HAVE_FPUTS_UNLOCKED
 # define fputs_unlocked fputs
 #endif
@@ -651,9 +692,9 @@ printleader(struct tcb *tcp)
 	current_tcp->curcol = 0;
 
 	if (print_pid_pfx)
-		tprintf("%-5d ", tcp->pid);
+		color_tprintf(COLOR_PREFIX_PID, "%-5d ", tcp->pid);
 	else if (nprocs > 1 && !outfname)
-		tprintf("[pid %5u] ", tcp->pid);
+		color_tprintf(COLOR_PREFIX_PID, "[pid %5u] ", tcp->pid);
 
 	if (tflag) {
 		char str[sizeof("HH:MM:SS")];
@@ -1612,7 +1653,7 @@ init(int argc, char *argv[])
 #endif
 	qualify("signal=all");
 	while ((c = getopt(argc, argv,
-		"+b:cCdfFhiqrtTvVwxyz"
+		"+b:cCdfFhHiqrtTvVwxyz"
 #ifdef USE_LIBUNWIND
 		"k"
 #endif
@@ -1735,6 +1776,9 @@ init(int argc, char *argv[])
 			if (opt_intr <= 0)
 				error_opt_arg(c, optarg);
 			break;
+		case 'H':
+			color_enabled = true;
+			break;
 		default:
 			error_msg_and_help(NULL);
 			break;
@@ -2222,7 +2266,8 @@ print_event_exit(struct tcb *tcp)
 	}
 	tprints(") ");
 	tabto();
-	tprints("= ?\n");
+	tprints("= ");
+	color_tprints(COLOR_RETURN_VALUE, "?\n");
 	line_ended();
 }
 
diff --git a/syscall.c b/syscall.c
index 569055f..142e325 100644
--- a/syscall.c
+++ b/syscall.c
@@ -650,7 +650,9 @@ trace_syscall_entering(struct tcb *tcp, unsigned int *sig)
 
 	if (res != 1) {
 		printleader(tcp);
-		tprintf("%s(", scno_good == 1 ? tcp->s_ent->sys_name : "????");
+		color_tprintf(COLOR_SYS_NAME, "%s", scno_good == 1 ?
+			      tcp->s_ent->sys_name : "????");
+		tprints("(");
 		/*
 		 * " <unavailable>" will be added later by the code which
 		 * detects ptrace errors.
@@ -724,7 +726,8 @@ trace_syscall_entering(struct tcb *tcp, unsigned int *sig)
 #endif
 
 	printleader(tcp);
-	tprintf("%s(", tcp->s_ent->sys_name);
+	color_tprintf(COLOR_SYS_NAME, "%s", tcp->s_ent->sys_name);
+	tprints("(");
 	if (tcp->qual_flg & QUAL_RAW)
 		res = printargs(tcp);
 	else
@@ -804,7 +807,9 @@ trace_syscall_exiting(struct tcb *tcp)
 		/* There was error in one of prior ptrace ops */
 		tprints(") ");
 		tabto();
-		tprints("= ? <unavailable>\n");
+		tprints("= ");
+		color_tprints(COLOR_RETURN_VALUE, "?");
+		tprints(" <unavailable>\n");
 		line_ended();
 		tcp->flags &= ~(TCB_INSYSCALL | TCB_TAMPERED);
 		tcp->sys_func_rval = 0;
@@ -837,11 +842,14 @@ trace_syscall_exiting(struct tcb *tcp)
 	tabto();
 	u_error = tcp->u_error;
 
+	tprints("= ");
 	if (tcp->qual_flg & QUAL_RAW) {
 		if (u_error) {
-			tprintf("= -1 (errno %lu)", u_error);
+			color_tprintf(COLOR_ERROR_MSG,
+				      "-1 (errno %lu)", u_error);
 		} else {
-			tprintf("= %#" PRI_klx, tcp->u_rval);
+			color_tprintf(COLOR_RETURN_VALUE,
+				      "%#" PRI_klx, tcp->u_rval);
 		}
 		if (syscall_tampered(tcp))
 			tprints(" (INJECTED)");
@@ -869,13 +877,15 @@ trace_syscall_exiting(struct tcb *tcp)
 			 * The system call will be restarted with the same arguments
 			 * if SA_RESTART is set; otherwise, it will fail with EINTR.
 			 */
-			tprints("= ? ERESTARTSYS (To be restarted if SA_RESTART is set)");
+			color_tprints(COLOR_ERROR_MSG,
+				      "? ERESTARTSYS (To be restarted if SA_RESTART is set)");
 			break;
 		case ERESTARTNOINTR:
 			/* Rare. For example, fork() returns this if interrupted.
 			 * SA_RESTART is ignored (assumed set): the restart is unconditional.
 			 */
-			tprints("= ? ERESTARTNOINTR (To be restarted)");
+			color_tprints(COLOR_ERROR_MSG,
+				      "? ERESTARTNOINTR (To be restarted)");
 			break;
 		case ERESTARTNOHAND:
 			/* pause(), rt_sigsuspend() etc use this code.
@@ -885,7 +895,8 @@ trace_syscall_exiting(struct tcb *tcp)
 			 * after SIG_IGN or SIG_DFL signal it will restart
 			 * (thus the name "restart only if has no handler").
 			 */
-			tprints("= ? ERESTARTNOHAND (To be restarted if no handler)");
+			color_tprints(COLOR_ERROR_MSG,
+				      "? ERESTARTNOHAND (To be restarted if no handler)");
 			break;
 		case ERESTART_RESTARTBLOCK:
 			/* Syscalls like nanosleep(), poll() which can't be
@@ -899,15 +910,16 @@ trace_syscall_exiting(struct tcb *tcp)
 			 * which in turn saves another such restart block,
 			 * old data is lost and restart becomes impossible)
 			 */
-			tprints("= ? ERESTART_RESTARTBLOCK (Interrupted by signal)");
+			color_tprints(COLOR_ERROR_MSG,
+				      "? ERESTART_RESTARTBLOCK (Interrupted by signal)");
 			break;
 		default:
 			u_error_str = err_name(u_error);
 			if (u_error_str)
-				tprintf("= -1 %s (%s)",
+				color_tprintf(COLOR_ERROR_MSG, "-1 %s (%s)",
 					u_error_str, strerror(u_error));
 			else
-				tprintf("= -1 %lu (%s)",
+				color_tprintf(COLOR_ERROR_MSG, "-1 %lu (%s)",
 					u_error, strerror(u_error));
 			break;
 		}
@@ -918,45 +930,48 @@ trace_syscall_exiting(struct tcb *tcp)
 	}
 	else {
 		if (sys_res & RVAL_NONE)
-			tprints("= ?");
+			color_tprints(COLOR_RETURN_VALUE, "?");
 		else {
 			switch (sys_res & RVAL_MASK) {
 			case RVAL_HEX:
 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
 				if (current_wordsize < sizeof(tcp->u_rval)) {
-					tprintf("= %#x",
+					color_tprintf(COLOR_RETURN_VALUE, "%#x",
 						(unsigned int) tcp->u_rval);
 				} else
 #endif
 				{
-					tprintf("= %#" PRI_klx, tcp->u_rval);
+					color_tprintf(COLOR_RETURN_VALUE,
+						      "%#" PRI_klx, tcp->u_rval);
 				}
 				break;
 			case RVAL_OCTAL:
-				tprints("= ");
-				print_numeric_long_umask(tcp->u_rval);
+				color_tprintf(COLOR_RETURN_VALUE,
+					      "%#03lo", tcp->u_rval);
 				break;
 			case RVAL_UDECIMAL:
 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
 				if (current_wordsize < sizeof(tcp->u_rval)) {
-					tprintf("= %u",
+					color_tprintf(COLOR_RETURN_VALUE, "%u",
 						(unsigned int) tcp->u_rval);
 				} else
 #endif
 				{
-					tprintf("= %" PRI_klu, tcp->u_rval);
+					color_tprintf(COLOR_RETURN_VALUE,
+						      "%" PRI_klu, tcp->u_rval);
 				}
 				break;
 			case RVAL_DECIMAL:
-				tprintf("= %" PRI_kld, tcp->u_rval);
+				color_tprintf(COLOR_RETURN_VALUE,
+					      "%" PRI_kld, tcp->u_rval);
 				break;
 			case RVAL_FD:
 				if (show_fd_path) {
-					tprints("= ");
 					printfd(tcp, tcp->u_rval);
 				}
 				else
-					tprintf("= %" PRI_kld, tcp->u_rval);
+					color_tprintf(COLOR_FD,
+						      "%" PRI_kld, tcp->u_rval);
 				break;
 			default:
 				error_msg("invalid rval format");
diff --git a/util.c b/util.c
index f05d4ef..7d78121 100644
--- a/util.c
+++ b/util.c
@@ -453,9 +453,9 @@ void
 printaddr(const kernel_ulong_t addr)
 {
 	if (!addr)
-		tprints("NULL");
+		color_tprints(COLOR_ADDRESS, "NULL");
 	else
-		tprintf("%#" PRI_klx, addr);
+		color_tprintf(COLOR_ADDRESS, "%#" PRI_klx, addr);
 }
 
 #define DEF_PRINTNUM(name, type) \
@@ -602,7 +602,8 @@ printfd(struct tcb *tcp, int fd)
 		const size_t socket_prefix_len = sizeof(socket_prefix) - 1;
 		const size_t path_len = strlen(path);
 
-		tprintf("%d<", fd);
+		color_tprintf(COLOR_FD, "%d", fd);
+		tprints("<");
 		if (show_fd_path > 1 &&
 		    strncmp(path, socket_prefix, socket_prefix_len) == 0 &&
 		    path[path_len - 1] == ']') {
@@ -621,7 +622,7 @@ printfd(struct tcb *tcp, int fd)
 		}
 		tprints(">");
 	} else
-		tprintf("%d", fd);
+		color_tprintf(COLOR_FD, "%d", fd);
 }
 
 /*
-- 
2.7.4





More information about the Strace-devel mailing list