[PATCH] Raise strace awareness

Eugene Syromyatnikov evgsyr at gmail.com
Wed Apr 1 14:03:42 UTC 2020


After der Strauss awareness has been raised sufficiently[1][2], it is
time for den Strauss itself to raise the awareness about strace,
and to do so, the most modern and contemporary method has been elected:
displaying tips, tricks and tweaks on each run.

[1] https://lists.strace.io/pipermail/strace-devel/2019-April/008701.html
[2] https://gitlab.com/strace/strace/commit/e8194a46d619

* strace.c (init) <enum>: Add GETOPT_TIPS.
<longopts>: Add  "no-tips" option.
(init) <case GETOPT_TIPS>: Set show_tips to false.
(terminate): Call print_totd.
(usage): Document --no-tips.
* strace.1.in (.SS Miscellaneous): Likewise.
* straus.c (STRAUS_BODY_LINES, MAX_TIP_LINES): New enums.
(tips_tricks_tweaks, tip_top, tip_bottom, tip_left, tip_right): New
static constants.
(show_tips): New variable.
(print_totd): New function.
* straus.h (show_tips, print_totd): New declarations.
* tests/bexecve.test: Supply --no-tips to strace invocations.
* tests/clone_ptrace.test: Likewise.
* tests/fflush.test: Likewise.
* tests/first_exec_failure.test: Likewise.
* tests/options-syntax.test: Likewise.

Suggested-by: Elvira Khabirova <lineprinter0 at gmail.com>
---
 strace.1.in                   |   3 +
 strace.c                      |   9 ++
 straus.c                      | 272 ++++++++++++++++++++++++++++++++++++++++++
 straus.h                      |   2 +
 tests/bexecve.test            |   8 +-
 tests/clone_ptrace.test       |   2 +-
 tests/fflush.test             |   2 +-
 tests/first_exec_failure.test |   2 +-
 tests/options-syntax.test     |   3 +-
 9 files changed, 295 insertions(+), 8 deletions(-)

diff --git a/strace.1.in b/strace.1.in
index 97ab3ae..5ad8d2c 100644
--- a/strace.1.in
+++ b/strace.1.in
@@ -1347,6 +1347,9 @@ option.
 .B \-\-help
 Print the help summary.
 .TP
+.B \-\-no\-tips
+Do not show strace tips, tricks, and tweaks.
+.TP
 .B \-\-seccomp\-bpf
 Enable (experimental) usage of seccomp-bpf (see
 .BR seccomp (2))
diff --git a/strace.c b/strace.c
index db6cb5f..18b8fed 100644
--- a/strace.c
+++ b/strace.c
@@ -436,6 +436,7 @@ Tampering:\n\
 Miscellaneous:\n\
   -d, --debug    enable debug output to stderr\n\
   -h, --help     print help message\n\
+  --no-tips      do not show strace tips, tricks, and tweaks\n\
   --seccomp-bpf  enable seccomp-bpf filtering\n\
   -V, --version  print version\n\
 "
@@ -1903,6 +1904,7 @@ init(int argc, char *argv[])
 		GETOPT_FOLLOWFORKS,
 		GETOPT_OUTPUT_SEPARATELY,
 		GETOPT_TS,
+		GETOPT_TIPS,
 
 		GETOPT_QUAL_TRACE,
 		GETOPT_QUAL_ABBREV,
@@ -1957,6 +1959,7 @@ init(int argc, char *argv[])
 		{ "failed-only",	no_argument,	   0, 'Z' },
 		{ "failing-only",	no_argument,	   0, 'Z' },
 		{ "seccomp-bpf",	no_argument,	   0, GETOPT_SECCOMP },
+		{ "no-tips",		no_argument,	   0, GETOPT_TIPS },
 
 		{ "trace",	required_argument, 0, GETOPT_QUAL_TRACE },
 		{ "abbrev",	required_argument, 0, GETOPT_QUAL_ABBREV },
@@ -2169,6 +2172,9 @@ init(int argc, char *argv[])
 		case GETOPT_SECCOMP:
 			seccomp_filtering = true;
 			break;
+		case GETOPT_TIPS:
+			show_tips = false;
+			break;
 		case GETOPT_QUAL_TRACE:
 			qualify_trace(optarg);
 			break;
@@ -3535,6 +3541,7 @@ terminate(void)
 		exit_code &= 0xff;
 		signal(exit_code, SIG_DFL);
 		GCOV_DUMP;
+		print_totd();
 		raise(exit_code);
 
 		/* Unblock the signal.  */
@@ -3548,6 +3555,8 @@ terminate(void)
 		   Exit with 128 + signo then.  */
 		exit_code += 128;
 	}
+
+	print_totd();
 	exit(exit_code);
 }
 
diff --git a/straus.c b/straus.c
index 0c815a2..6edc583 100644
--- a/straus.c
+++ b/straus.c
@@ -9,6 +9,10 @@
 
 #include "defs.h"
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
 #include "straus.h"
 
 static const char *straus[] = {
@@ -61,6 +65,234 @@ static const char *straus[] = {
 
 const size_t straus_lines = ARRAY_SIZE(straus);
 
+enum {
+	STRAUS_BODY_LINES = 37,
+	MAX_TIP_LINES = 13,
+};
+
+static const char *tips_tricks_tweaks[][MAX_TIP_LINES] = {
+	{ "strace has an extensive manual page",
+	  "that covers all the possible options",
+	  "and contains several useful invocation",
+	  "examples." },
+	{ "You can use -o|COMMAND to redirect strace's",
+	  "output to COMMAND.  This may be useful",
+	  "in cases when there is redirection in place",
+	  "for the traced program." },
+	{ "It's possible to display timestamps",
+	  " produced by -r, -t, and -T options",
+	  "with nanosecond precision using their",
+	  "long variants: --relative-timestamps=ns,",
+	  "--absolute-timestamps=ns, and",
+	  "--syscall-times=ns, respectively.", "",
+	  "Why microseconds are the default?",
+	  "To preserve the historic output format",
+	  "which was limited by struct timeval",
+	  "precision." },
+	{ "A particular quote from a particular novel",
+	  "by Arthur C. Clarke is printed if an attempt",
+	  "is made to attach to a particular process." },
+	{ "It's possible to tune the environment",
+	  "of the traced process using the -E/--env",
+	  "option." },
+#if ENABLE_STACKTRACE
+	{ "You can print a stack trace for each traced",
+	  "call by specifying -k/--stack-traces option.",
+#if USE_DEMANGLE
+	  "It can even demangle symbol names.",
+#endif
+	  },
+#else
+	{ "We would like to tell you that you could",
+	  "specify -k/--stack-traces option to print",
+	  "stack traces for each traced system call,",
+	  "but unfortunately you can't, this strace",
+	  "binary is built without stack tracing",
+	  "support." },
+#endif
+	{ "Have you ever hit by accidental overwrite",
+	  "of the output file specified in the -o",
+	  "option?  Specify -A/--output-append-mode",
+	  "as well, and this problem will never annoy",
+	  "you again!" },
+	{ "strace is about as old as the Linux kernel.",
+	  "It has been originally written for SunOS",
+	  "by Paul Kranenburg in 1991." },
+	{ "strace is able to decode netlink messages.",
+	  "It does so automatically for IO performed",
+	  "on netlink sockets.  Try it yourself:", "",
+	  "    strace -e%network ip a" },
+	{ "Filtered syscalls, errors, and signals can",
+	  "be specified either by name or by number,",
+	  "for example:", "",
+	  "    strace --trace=0,1,2 --signal=2,15 true" },
+	{ "It is possible to specify -r and -t options",
+	  "simultaneously since strace 4.22." },
+	{ "Strace can print only successful syscall",
+	  "invocations when supplied with",
+	  "-z/--successful-only option.  There's also",
+	  "a possibility to filter calls with other",
+	  "statuses, please refer to -e status option",
+	  "documentation." },
+	{ "If you trace a process that uses KVM",
+	  "subsystem, --kvm=vcpu option may be of use:",
+	  "it prints KVM VCPU exit reason.  It requires",
+	  "Linux 4.16+, however." },
+	{ "It is possible to get strace out of your way",
+	  "(in terms of parent/child relationships and",
+	  "signal communication) with -D/--daemonize",
+	  "option.  Another option that may be of use",
+	  "in this case is -I/--interruptible, it",
+	  "restricts the set of signals that interrupt",
+	  "strace." },
+	{ "If strace is too talky to your taste, you",
+	  "can silence it with -qqq option." },
+	{ "strace prints file paths along with file",
+	  "descriptor numbers when it is invoked with",
+	  "-y/--decode-fds option.",
+	  "When -yy (or --decode-fds=all) is provided,",
+	  "it also prints protocol-specific information",
+	  "for sockets and device numbers for character",
+	  "and block device files." },
+	{ "You can control what columns are shown",
+	  "in the call summary table produced by -c/-C",
+	  "options with -U/--summary-columns option.",
+	  "It is a way to print minimum/maximum call",
+	  "duration, for example." },
+	{ "If you feel that syscall duration shown",
+	  "in the call summary table (-c/-C option)",
+	  "is not right, you can try to use -w option",
+	  "(that collects wall clock time instead),",
+	  "maybe that is what you are looking for." },
+	{ "strace understands -z option since 2002,",
+	  "but it wasn't documented because its",
+	  "implementation was broken.  Only 17 years",
+	  "later, in strace 5.2, it was properly",
+	  "implemented and documented." },
+	{ "If you feel that strace is too slow, you may",
+	  "want to try --seccomp-bpf option, maybe you",
+	  "will feel better." },
+	{ "-v is a shorthand for -e abbrev=none and not",
+	  " for -e verbose=all. It is idiosyncratic,",
+	  "but it is the historic behaviour." },
+	{ "strace uses netlink for printing",
+	  "protocol-specific information about socket",
+	  "descriptors (-yy option)." },
+	{ "strace is able to tamper with tracees'",
+	  "execution by injecting an arbitrary return",
+	  "or error value instead of syscall execution,",
+	  "for example:", "",
+	  "    strace --inject=unlink:retval=0", "",
+	  "would prevent execution of unlink calls, but",
+	  "would make the traced process think that",
+	  "they have succeeded." },
+	{ "strace's tampering capabilities include",
+	  "injection of arbitrary return/error values,",
+	  "injection of a signal, injection of a delay",
+	  "before or after syscall execution." },
+	{ "If you want to see numerical values of named",
+	  "constants, there is an option for that:",
+	  "-X/--const-print-style.  When -Xraw",
+	  "(or --const-print-style=raw) is provided,",
+	  "strace prints just the numerical value",
+	  "of an argument; with -Xverbose, it prints",
+	  "values in both numerical and symbolic form." },
+	{ "getpid syscall is present on all",
+	  "architectures except on Alpha, where getxpid",
+	  "syscall (that returns a pair of PID and PPID",
+	  "in a pair of registers) is used instead.",
+	  "Other two examples of syscalls that utilise",
+	  "two registers for their return walues are",
+	  "getxuid and getxgid the return a pair",
+	  "of real and effective UIDs/GIDs." },
+	{ "There are three syscalls that implement",
+	  "generic \"open file\" task: open, openat,",
+	  "and openat2. On some (newly supported)",
+	  "architectures, open syscall is not present.",
+	  "How to write a robust filtering expression",
+	  "in this case?  With conditional syntax,",
+	  "for example:", "",
+	  "    strace --trace=?open,?openat,?openat2", "",
+	  "You may wont to escape question marks, since",
+	  "your shell may interpret them as a path glob",
+	  "expression." },
+	{ "It is possible to use regular expressions",
+	  "for syscall names in the -e trace",
+	  "expression, for example:", "",
+	  "    strace -e trace=/^sched_.*", "",
+	  "will trace all scheduling-related syscalls." },
+	{ "IA-64 (Itanium) uses syscall numbers",
+	  "beginning from 1024, because numbers",
+	  "beginning from 0 were used for i386 compat",
+	  "layer.  Other example of a sparse syscall",
+	  "table is MIPS, with parts of it beginning",
+	  "at index 0 (SVR4 ABI), 1000 (SysV ABI), 2000",
+	  "(BSD 4.3 ABI), 3000 (POSIX ABI), 4000 (Linux",
+	  "O32 ABI), 5000 (Linux N64 ABI), and 6000",
+	  "(Linux N32 ABI)." },
+	{ "Der Strauss, the strace's project mascot,",
+	  "was conceived in 2017.  It is a brainchild",
+	  "of Vitaly Chaykovsky." },
+	{ "Medicinal effects of strace can be achieved",
+	  "by invoking it with the following options:", "",
+	  "    strace -DDDqqq -enone --signal=none" },
+	{ "Your Internet connection is working fine.",
+	  "No one replies to your e-mails because",
+	  "there is no one left." },
+	{ "It is possible to print absolute timestamps",
+	  "with nanosecond precision using",
+	  "--absolute-timestamps=ns option." },
+	{ "You smell something not unlike rotting",
+	  "meat every time you run me.  I cannot",
+	  "promise that the smell will ever go away,",
+	  "or that will ever find its source.", "",
+	  "But I can promise that you will learn",
+	  "to love it." },
+	{ "Historically, supplying -o option to strace",
+	  "led to silencing of messages about tracee",
+	  "attach/detach and personality changes.",
+	  "It can be now overridden with --quiet=none",
+	  "option." },
+	{ "If you open your computer, inside you",
+	  "will find a cloth doll, soft and worn.",
+	  "You will suddenly remember owning this doll",
+	  "as a child, loving and cherishing it.", "",
+	  "Do not question the truth of this memory." },
+	{ "You can avoid tracing of \"other programs\"",
+	  "that are executed by the traced program",
+	  "with -b execve option." },
+	{ "Touch your monitor.  It's warm, like flesh.",
+	  "But it's not flesh.", "Not yet." },
+	{ "-F option used to be a separate option",
+	  "for following vfork calls." },
+	{ "It is possible to provide multiple PIDs",
+	  "to a single -p option with whitespace",
+	  "or comma as accepted delimiter, in order",
+	  "to support usage like", "",
+	  "    strace -p \"`pidof PROG`\"",
+	  "or",
+	  "    strace -p \"`pgrep PROG`\"", "",
+	  "pidof uses space as a delimiter, pgrep uses",
+	  "newline." },
+};
+
+static const char tip_top[] =
+	"  ______________________________________________    ";
+static const char top_bottom[] =
+	" \\______________________________________________/   ";
+static const char *tip_left[] = { " / ", " | "};
+static const char *tip_right[] = {
+	" \\   ",
+	" |   ",
+	" \\   ",
+	"  \\  ",
+	"  _\\ ",
+	" /   ",
+	" |   ", };
+
+bool show_tips = true;
+
+
 void
 print_straus(size_t verbosity)
 {
@@ -72,3 +304,43 @@ print_straus(size_t verbosity)
 	for (size_t i = 0; i < verbosity; i++)
 		puts(straus[i]);
 }
+
+void
+print_totd(void)
+{
+	static bool printed = false;
+	const int w = (int) (sizeof(tip_top) - 1 - strlen(tip_left[0])
+				- strlen(tip_right[0]));
+	struct timeval tv;
+	size_t id;
+	size_t i;
+
+	if (printed || !show_tips)
+		return;
+
+	gettimeofday(&tv, NULL);
+	srand(tv.tv_sec ^ tv.tv_usec);
+	id = rand() % ARRAY_SIZE(tips_tricks_tweaks);
+
+	fprintf(stderr, "%s%s\n", tip_top, straus[1]);
+	fprintf(stderr, "%s%-*s%s%s\n",
+		tip_left[0], w, "", tip_right[0], straus[2]);
+	for (i = 0; (i < MAX_TIP_LINES) && (tips_tricks_tweaks[id][i] ||
+					    (i < (ARRAY_SIZE(tip_right) - 1)));
+	     i++) {
+		fprintf(stderr, "%s%-*.*s%s%s\n",
+			tip_left[MIN(i + 1, ARRAY_SIZE(tip_left) - 1)],
+			w, w, tips_tricks_tweaks[id][i] ?: "",
+			tip_right[MIN(i + 1, ARRAY_SIZE(tip_right) - 1)],
+			straus[MIN(3 + i, STRAUS_BODY_LINES)]);
+	}
+	fprintf(stderr, "%s%s\n",
+		top_bottom, straus[MIN(3 + i, STRAUS_BODY_LINES)]);
+	fprintf(stderr, "%*s%*s%*s%s\n",
+		(int) strlen(tip_left[0]), "",
+		w, "",
+		(int) strlen(tip_right[0]), "",
+		straus[MIN(4 + i, STRAUS_BODY_LINES)]);
+
+	printed = true;
+}
diff --git a/straus.h b/straus.h
index 04864b5..3347677 100644
--- a/straus.h
+++ b/straus.h
@@ -13,7 +13,9 @@
 enum { STRAUS_START_VERBOSITY = 5 };
 
 extern const size_t straus_lines;
+extern bool show_tips;
 
 extern void print_straus(size_t verbosity);
+extern void print_totd(void);
 
 #endif /* STRACE_STRAUS_H */
diff --git a/tests/bexecve.test b/tests/bexecve.test
index 4ed894e..18d917c 100755
--- a/tests/bexecve.test
+++ b/tests/bexecve.test
@@ -16,14 +16,14 @@ run_strace_redir()
 	$STRACE "$@" 2> "$LOG"
 }
 
-run_strace_redir --quiet=personality -enone ../set_ptracer_any true ||
+run_strace_redir --quiet=personality --no-tips -enone ../set_ptracer_any true ||
 	dump_log_and_fail_with "$STRACE $args: unexpected exit status"
 
-run_strace_redir --quiet=personality -enone ../set_ptracer_any false
+run_strace_redir --quiet=personality --no-tips -enone ../set_ptracer_any false
 [ $? -eq 1 ] ||
 	dump_log_and_fail_with "$STRACE $args: unexpected exit status"
 
-run_strace_redir --quiet=personality -bexecve -enone ../set_ptracer_any false ||
+run_strace_redir --quiet=personality --no-tips -bexecve -enone ../set_ptracer_any false ||
 	dump_log_and_fail_with "$STRACE $args: unexpected exit status"
 
 pattern_detached='[^:]*strace: Process [1-9][0-9]* detached'
@@ -35,7 +35,7 @@ if LC_ALL=C grep -E -v -x "$pattern_detached" "$LOG" > /dev/null; then
 	dump_log_and_fail_with "$STRACE $args: unexpected output"
 fi
 
-run_strace_redir --quiet=personality --seccomp-bpf -bexecve -enone ../set_ptracer_any false ||
+run_strace_redir --quiet=personality --no-tips --seccomp-bpf -bexecve -enone ../set_ptracer_any false ||
 	dump_log_and_fail_with "$STRACE $args: unexpected exit status"
 
 pattern_seccomp='[^:]*strace: --seccomp-bpf is not enabled because it is not compatible with -b'
diff --git a/tests/clone_ptrace.test b/tests/clone_ptrace.test
index 583c44d..9c77d05 100755
--- a/tests/clone_ptrace.test
+++ b/tests/clone_ptrace.test
@@ -13,7 +13,7 @@ run_prog > /dev/null 3>&1
 args="-e trace=none ${1:---quiet=personality} $args"
 > "$LOG" || fail_ "failed to write $LOG"
 
-$STRACE -o "$LOG" $args > "$EXP" 2> "$OUT"-err 3> "$EXP"-err || {
+$STRACE --no-tips -o "$LOG" $args > "$EXP" 2> "$OUT"-err 3> "$EXP"-err || {
 	msg="$STRACE $args failed with code $?"
 	cat "$OUT"-err "$LOG" >&2
 	fail_ "$msg"
diff --git a/tests/fflush.test b/tests/fflush.test
index b018649..f6eea93 100755
--- a/tests/fflush.test
+++ b/tests/fflush.test
@@ -10,7 +10,7 @@
 . "${srcdir=.}/init.sh"
 
 run_prog > /dev/null
-args="-o /dev/full -e trace=none $args"
+args="-o /dev/full -e trace=none --no-tips $args"
 
 $STRACE $args > "$EXP" 2> "$LOG" || {
 	msg="$STRACE $args failed with code $?"
diff --git a/tests/first_exec_failure.test b/tests/first_exec_failure.test
index 175b084..b2bd8d1 100755
--- a/tests/first_exec_failure.test
+++ b/tests/first_exec_failure.test
@@ -9,7 +9,7 @@
 
 . "${srcdir=.}/init.sh"
 
-$STRACE / 2> "$LOG" &&
+$STRACE --no-tips / 2> "$LOG" &&
 	dump_log_and_fail_with \
 		"$STRACE / failed to handle the error properly"
 
diff --git a/tests/options-syntax.test b/tests/options-syntax.test
index aebec27..a6487d5 100755
--- a/tests/options-syntax.test
+++ b/tests/options-syntax.test
@@ -311,12 +311,13 @@ args='-p 2147483647'
 $STRACE $args 2> "$LOG" &&
 	dump_log_and_fail_with \
 		"strace $args failed to handle the error properly"
+head -n1 < "$LOG" > "$OUT"
 
 for cmd in PTRACE_SEIZE PTRACE_ATTACH; do
 	cat > "$EXP" << __EOF__
 $STRACE_EXE: attach: ptrace($cmd, 2147483647): No such process
 __EOF__
-	diff -- "$EXP" "$LOG" ||
+	diff -- "$EXP" "$OUT" ||
 		continue
 	args=
 	break
-- 
2.10.2



More information about the Strace-devel mailing list