Add -d (duration) option to argdist, funclatency and syscount (#1768)

* Add -d (duration) option to argdist, funclatency and syscount

* Add -d option to man pages and _example.txt
diff --git a/man/man8/argdist.8 b/man/man8/argdist.8
index b0a539f..4116cd4 100644
--- a/man/man8/argdist.8
+++ b/man/man8/argdist.8
@@ -2,7 +2,7 @@
 .SH NAME
 argdist \- Trace a function and display a histogram or frequency count of its parameter values. Uses Linux eBPF/bcc.
 .SH SYNOPSIS
-.B argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-n COUNT] [-v] [-T TOP] [-H specifier] [-C specifier] [-I header]
+.B argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-d DURATION] [-n COUNT] [-v] [-T TOP] [-H specifier] [-C specifier] [-I header]
 .SH DESCRIPTION
 argdist attaches to function entry and exit points, collects specified parameter
 values, and stores them in a histogram or a frequency collection that counts
@@ -27,6 +27,9 @@
 \-i INTERVAL
 Print the collected data every INTERVAL seconds. The default is 1 second.
 .TP
+\-d DURATION
+Total duration of trace in seconds.
+.TP
 \-n NUMBER
 Print the collected data COUNT times and then exit.
 .TP
diff --git a/man/man8/funclatency.8 b/man/man8/funclatency.8
index 7b7771b..b82626c 100644
--- a/man/man8/funclatency.8
+++ b/man/man8/funclatency.8
@@ -2,7 +2,7 @@
 .SH NAME
 funclatency \- Time functions and print latency as a histogram.
 .SH SYNOPSIS
-.B funclatency [\-h] [\-p PID] [\-i INTERVAL] [\-T] [\-u] [\-m] [\-F] [\-r] [\-v] pattern
+.B funclatency [\-h] [\-p PID] [\-i INTERVAL] [\-d DURATION] [\-T] [\-u] [\-m] [\-F] [\-r] [\-v] pattern
 .SH DESCRIPTION
 This tool traces function calls and times their duration (latency), and
 shows the latency distribution as a histogram. The time is measured from when
@@ -37,6 +37,9 @@
 \-i INTERVAL
 Print output every interval seconds.
 .TP
+\-d DURATION
+Total duration of trace, in seconds.
+.TP
 \-T
 Include timestamps on output.
 .TP
@@ -72,6 +75,10 @@
 #
 .B funclatency \-m do_nanosleep
 .TP
+Time libc open(), and print output every 2 seconds, for duration 10 seconds:
+#
+.B funclatency \-i 2 -d 10 c:read
+.TP
 Time vfs_read(), and print output every 5 seconds, with timestamps:
 #
 .B funclatency \-mTi 5 vfs_read
diff --git a/man/man8/syscount.8 b/man/man8/syscount.8
index 2a47240..d13793b 100644
--- a/man/man8/syscount.8
+++ b/man/man8/syscount.8
@@ -2,7 +2,7 @@
 .SH NAME
 syscount \- Summarize syscall counts and latencies.
 .SH SYNOPSIS
-.B syscount [-h] [-p PID] [-i INTERVAL] [-T TOP] [-x] [-e ERRNO] [-L] [-m] [-P] [-l]
+.B syscount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T TOP] [-x] [-e ERRNO] [-L] [-m] [-P] [-l]
 .SH DESCRIPTION
 This tool traces syscall entry and exit tracepoints and summarizes either the
 number of syscalls of each type, or the number of syscalls per process. It can
@@ -23,6 +23,9 @@
 \-i INTERVAL
 Print the summary at the specified interval (in seconds).
 .TP
+\-d DURATION
+Total duration of trace (in seconds).
+.TP
 \-T TOP
 Print only this many entries. Default: 10.
 .TP
diff --git a/tools/argdist.py b/tools/argdist.py
index 9724073..dfb06b4 100755
--- a/tools/argdist.py
+++ b/tools/argdist.py
@@ -610,7 +610,9 @@
                   type=int,
                   help="maximum string size to read from char* arguments")
                 parser.add_argument("-i", "--interval", default=1, type=int,
-                  help="output interval, in seconds")
+                  help="output interval, in seconds (default 1 second)")
+                parser.add_argument("-d", "--duration", type=int,
+                  help="total duration of trace, in seconds")
                 parser.add_argument("-n", "--number", type=int, dest="count",
                   help="number of outputs")
                 parser.add_argument("-v", "--verbose", action="store_true",
@@ -684,9 +686,11 @@
 
         def _main_loop(self):
                 count_so_far = 0
+                seconds = 0
                 while True:
                         try:
                                 sleep(self.args.interval)
+                                seconds += self.args.interval
                         except KeyboardInterrupt:
                                 exit()
                         print("[%s]" % strftime("%H:%M:%S"))
@@ -696,6 +700,9 @@
                         if self.args.count is not None and \
                            count_so_far >= self.args.count:
                                 exit()
+                        if self.args.duration and \
+                           seconds >= self.args.duration:
+                                exit()
 
         def run(self):
                 try:
diff --git a/tools/argdist_example.txt b/tools/argdist_example.txt
index dec9e6f..7098e56 100644
--- a/tools/argdist_example.txt
+++ b/tools/argdist_example.txt
@@ -348,7 +348,9 @@
   -z STRING_SIZE, --string-size STRING_SIZE
                         maximum string size to read from char* arguments
   -i INTERVAL, --interval INTERVAL
-                        output interval, in seconds
+                        output interval, in seconds (default 1 second)
+  -d DURATION, --duration DURATION
+			total duration of trace, in seconds
   -n COUNT, --number COUNT
                         number of outputs
   -v, --verbose         print resulting BPF program code before executing
diff --git a/tools/funclatency.py b/tools/funclatency.py
index fe06d1b..3f08a7e 100755
--- a/tools/funclatency.py
+++ b/tools/funclatency.py
@@ -35,6 +35,7 @@
     ./funclatency c:read            # time the read() C library function
     ./funclatency -u vfs_read       # time vfs_read(), in microseconds
     ./funclatency -m do_nanosleep   # time do_nanosleep(), in milliseconds
+    ./funclatency -i 2 -d 10 c:open # output every 2 seconds, for duration 10s
     ./funclatency -mTi 5 vfs_read   # output every 5 seconds, with timestamps
     ./funclatency -p 181 vfs_read   # time process 181 only
     ./funclatency 'vfs_fstat*'      # time both vfs_fstat() and vfs_fstatat()
@@ -47,8 +48,10 @@
     epilog=examples)
 parser.add_argument("-p", "--pid", type=int,
     help="trace this PID only")
-parser.add_argument("-i", "--interval", default=99999999,
-    help="summary interval, seconds")
+parser.add_argument("-i", "--interval", type=int,
+    help="summary interval, in seconds")
+parser.add_argument("-d", "--duration", type=int,
+    help="total duration of trace, in seconds")
 parser.add_argument("-T", "--timestamp", action="store_true",
     help="include timestamp on output")
 parser.add_argument("-u", "--microseconds", action="store_true",
@@ -66,6 +69,10 @@
 parser.add_argument("--ebpf", action="store_true",
     help=argparse.SUPPRESS)
 args = parser.parse_args()
+if args.duration and not args.interval:
+    args.interval = args.duration
+if not args.interval:
+    args.interval = 99999999
 
 def bail(error):
     print("Error: " + error)
@@ -226,14 +233,18 @@
         return "%s [%d]" % (BPF.sym(key[0], key[1]), key[1])
 
 exiting = 0 if args.interval else 1
+seconds = 0
 dist = b.get_table("dist")
 while (1):
     try:
-        sleep(int(args.interval))
+        sleep(args.interval)
+        seconds += args.interval
     except KeyboardInterrupt:
         exiting = 1
         # as cleanup can take many seconds, trap Ctrl-C:
         signal.signal(signal.SIGINT, signal_ignore)
+    if args.duration and seconds >= args.duration:
+        exiting = 1
 
     print()
     if args.timestamp:
diff --git a/tools/funclatency_example.txt b/tools/funclatency_example.txt
index ee63a5b..d8217a2 100644
--- a/tools/funclatency_example.txt
+++ b/tools/funclatency_example.txt
@@ -343,7 +343,9 @@
   -h, --help            show this help message and exit
   -p PID, --pid PID     trace this PID only
   -i INTERVAL, --interval INTERVAL
-                        summary interval, seconds
+                        summary interval, in seconds
+  -d DURATION, --duration DURATION
+			total duration of trace, in seconds
   -T, --timestamp       include timestamp on output
   -u, --microseconds    microsecond histogram
   -m, --milliseconds    millisecond histogram
@@ -357,6 +359,7 @@
     ./funclatency c:read            # time the read() C library function
     ./funclatency -u vfs_read       # time vfs_read(), in microseconds
     ./funclatency -m do_nanosleep   # time do_nanosleep(), in milliseconds
+    ./funclatency -i 2 -d 10 c:open # output every 2 seconds, for duration 10s
     ./funclatency -mTi 5 vfs_read   # output every 5 seconds, with timestamps
     ./funclatency -p 181 vfs_read   # time process 181 only
     ./funclatency 'vfs_fstat*'      # time both vfs_fstat() and vfs_fstatat()
diff --git a/tools/syscount.py b/tools/syscount.py
index e219e91..a23abde 100755
--- a/tools/syscount.py
+++ b/tools/syscount.py
@@ -17,6 +17,7 @@
 import itertools
 import subprocess
 import sys
+import signal
 import platform
 
 if sys.version_info.major < 3:
@@ -370,6 +371,9 @@
     else:
         raise Exception("ausyscall: command not found")
 
+# signal handler
+def signal_ignore(signal, frame):
+    print()
 
 def handle_errno(errstr):
     try:
@@ -388,6 +392,8 @@
 parser.add_argument("-p", "--pid", type=int, help="trace only this pid")
 parser.add_argument("-i", "--interval", type=int,
     help="print summary at this interval (seconds)")
+parser.add_argument("-d", "--duration", type=int,
+    help="total duration of trace, in seconds")
 parser.add_argument("-T", "--top", type=int, default=10,
     help="print only the top syscalls by count or latency")
 parser.add_argument("-x", "--failures", action="store_true",
@@ -405,6 +411,10 @@
 parser.add_argument("--ebpf", action="store_true",
     help=argparse.SUPPRESS)
 args = parser.parse_args()
+if args.duration and not args.interval:
+    args.interval = args.duration
+if not args.interval:
+    args.interval = 99999999
 
 if args.list:
     for grp in izip_longest(*(iter(sorted(syscalls.values())),) * 4):
@@ -545,11 +555,20 @@
 
 print("Tracing %ssyscalls, printing top %d... Ctrl+C to quit." %
       ("failed " if args.failures else "", args.top))
+exiting = 0 if args.interval else 1
+seconds = 0
 while True:
     try:
-        sleep(args.interval or 999999999)
-        print_stats()
+        sleep(args.interval)
+        seconds += args.interval
     except KeyboardInterrupt:
-        if not args.interval:
-            print_stats()
-        break
+        exiting = 1
+        signal.signal(signal.SIGINT, signal_ignore)
+    if args.duration and seconds >= args.duration:
+        exiting = 1
+
+    print_stats()
+
+    if exiting:
+        print("Detaching...")
+        exit()
diff --git a/tools/syscount_example.txt b/tools/syscount_example.txt
index be6d3e6..aad51c4 100644
--- a/tools/syscount_example.txt
+++ b/tools/syscount_example.txt
@@ -151,6 +151,8 @@
   -p PID, --pid PID     trace only this pid
   -i INTERVAL, --interval INTERVAL
                         print summary at this interval (seconds)
+  -d DURATION, --duration DURATION
+			total duration of trace, in seconds
   -T TOP, --top TOP     print only the top syscalls by count or latency
   -x, --failures        trace only failed syscalls (return < 0)
   -e ERRNO, --errno ERRNO