From: Pádraig Brady Date: Fri, 14 Nov 2025 16:26:43 +0000 (+0000) Subject: tail: add --debug to report the --follow implementation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d611bcbffff10b09fb98923ed1645620c2086e75;p=thirdparty%2Fcoreutils.git tail: add --debug to report the --follow implementation * doc/coreutils.texi (tail invocation): Describe --debug. * src/tail.c (tail_forever, tail_forever_inotify): Output which --follow implementation is being used. * tests/tail/debug.sh: Add a new test. * tests/local.mk: Reference the new test. * NEWS: Mention the new feature. --- diff --git a/NEWS b/NEWS index b344452dba..d3909147ad 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,11 @@ GNU coreutils NEWS -*- outline -*- indicate the line count acceleration being used. [bug introduced in coreutils-9.0] +** New Features + + 'tail' now accepts the --debug option, which is currently used to + detail the --follow implementation being used. + * Noteworthy changes in release 9.9 (2025-11-10) [stable] diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 618889e402..214b21b563 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -3209,6 +3209,11 @@ byte @var{num} from the start of each file. For example to skip the first byte use @code{tail -c +2}, while to skip all but the last byte use @code{tail -c 1}. @multiplierSuffixes{num} +@item --debug +@opindex --debug +Output extra information to standard error, +like the --follow implementation being used. + @item -f @itemx --follow[=@var{how}] @opindex -f diff --git a/src/tail.c b/src/tail.c index c7779c77df..bfe6efe6c3 100644 --- a/src/tail.c +++ b/src/tail.c @@ -233,6 +233,9 @@ static bool presume_input_pipe; /* If nonzero then don't use inotify even if available. */ static bool disable_inotify; +/* Annotate the output with extra info to aid the user. */ +static bool debug; + /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum @@ -242,12 +245,14 @@ enum PID_OPTION, PRESUME_INPUT_PIPE_OPTION, LONG_FOLLOW_OPTION, - DISABLE_INOTIFY_OPTION + DISABLE_INOTIFY_OPTION, + DEBUG_PROGRAM_OPTION, }; static struct option const long_options[] = { {"bytes", required_argument, nullptr, 'c'}, + {"debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION}, {"follow", optional_argument, nullptr, LONG_FOLLOW_OPTION}, {"lines", required_argument, nullptr, 'n'}, {"max-unchanged-stats", required_argument, nullptr, @@ -290,6 +295,9 @@ With more than one FILE, precede each with a header giving the file name.\n\ fputs (_("\ -c, --bytes=[+]NUM output the last NUM bytes; or use -c +NUM to\n\ output starting with byte NUM of each file\n\ +"), stdout); + fputs (_("\ + --debug indicate which --follow implementation is used\n\ "), stdout); fputs (_("\ -f, --follow[={name|descriptor}]\n\ @@ -1154,12 +1162,22 @@ tail_forever (struct File_spec *f, int n_files, double sleep_interval) { int last = n_files - 1; + static bool debugged; + while (true) { /* Use blocking I/O as an optimization, when it's easy. */ bool blocking = (!nbpids && follow_mode == Follow_descriptor && n_files == 1 && 0 <= f[0].fd && !S_ISREG (f[0].mode)); + if (debug && !debugged) + { + debugged = true; + error (0, 0, "%s", blocking + ? _("using blocking mode") + : _("using polling mode")); + } + bool any_input = false; for (int i = 0; i < n_files; i++) @@ -1594,6 +1612,9 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files, } } + if (debug) + error (0, 0, "%s", _("using notification mode")); + evlen += sizeof (struct inotify_event) + 1; evbuf = ximalloc (evlen); @@ -2196,6 +2217,10 @@ parse_options (int argc, char **argv, 0, XTOINT_MAX_QUIET); break; + case DEBUG_PROGRAM_OPTION: + debug = true; + break; + case DISABLE_INOTIFY_OPTION: disable_inotify = true; break; diff --git a/tests/local.mk b/tests/local.mk index cab2dae74f..26d140dcc8 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -758,6 +758,7 @@ all_tests = \ tests/rmdir/t-slash.sh \ tests/tail/assert-2.sh \ tests/tail/big-4gb.sh \ + tests/tail/debug.sh \ tests/tail/flush-initial.sh \ tests/tail/follow-name.sh \ tests/tail/follow-stdin.sh \ diff --git a/tests/tail/debug.sh b/tests/tail/debug.sh new file mode 100755 index 0000000000..3d6a14c424 --- /dev/null +++ b/tests/tail/debug.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# Test --debug output + +# Copyright (C) 2025 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ tail + +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +cleanup_fail_ () +{ + warn_ $1 + cleanup_ + fail=1 +} + +# $check_re - string to be found +# $check_f - file to be searched +check_tail_output_ () +{ + local delay="$1" + grep "$check_re" $check_f > /dev/null || + { sleep $delay ; return 1; } +} + +grep_timeout_ () +{ + check_re="$1" + check_f="$2" + retry_delay_ check_tail_output_ .1 5 +} + + +timeout 10 tail --debug -f /dev/null 2>debug.out & pid=$! +grep_timeout_ 'tail: using blocking mode' 'debug.out' || fail=1 +cleanup_ + +timeout 10 tail --debug -F /dev/null 2>debug.out & pid=$! +grep_timeout_ 'tail: using polling mode' 'debug.out' || fail=1 +cleanup_ + +touch file.debug +require_strace_ 'inotify_add_watch' +returns_ 124 timeout .1 strace -e inotify_add_watch -o strace.out \ + tail -F file.debug || fail=1 +if grep 'inotify' strace.out; then + timeout 10 tail --debug -n0 -f file.debug 2>debug.out & pid=$! + grep_timeout_ 'tail: using notification mode' 'debug.out' || fail=1 + cleanup_ + + timeout 10 tail --debug ---disable-inotify -f file.debug 2>debug.out & pid=$! + grep_timeout_ 'tail: using polling mode' 'debug.out' || fail=1 + cleanup_ +fi + +Exit $fail