diff --git a/man/man8/ucalls.8 b/man/man8/ucalls.8 index 3917af59fca5..cadb3e06dbd7 100644 --- a/man/man8/ucalls.8 +++ b/man/man8/ucalls.8 @@ -1,27 +1,29 @@ .TH ucalls 8 "2016-11-07" "USER COMMANDS" .SH NAME -ucalls, javacalls, pythoncalls, rubycalls, phpcalls \- Summarize method calls +ucalls, javacalls, perlcalls, phpcalls, pythoncalls, rubycalls \- Summarize method calls from high-level languages and Linux syscalls. .SH SYNOPSIS .B javacalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br +.B perlcalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] +.br +.B phpcalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] +.br .B pythoncalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B rubycalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br -.B phpcalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] -.br -.B ucalls [-l {java,python,ruby,php}] [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] +.B ucalls [-l {java,perl,php,python,ruby}] [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .SH DESCRIPTION -This tool summarizes method calls from high-level languages such as Python, -Java, Ruby, and PHP. It can also trace Linux system calls. Whenever a method is -invoked, ucalls records the call count and optionally the method's execution +This tool summarizes method calls from high-level languages such as Java, Perl, +PHP, Python, and Ruby. It can also trace Linux system calls. Whenever a method +is invoked, ucalls records the call count and optionally the method's execution time (latency) and displays a summary. This uses in-kernel eBPF maps to store per process summaries for efficiency. This tool relies on USDT probes embedded in many high-level languages, such as -Java, Python, Ruby, and PHP. It requires a runtime instrumented with these +Java, Perl, PHP, Python, and Ruby. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java, method probes are not enabled by default, and can be turned on by running the Java process with @@ -33,7 +35,7 @@ Since this uses BPF, only the root user can use this tool. CONFIG_BPF and bcc. .SH OPTIONS .TP -\-l {java,python,ruby,php} +\-l {java,perl,php,python,ruby} The language to trace. If not provided, only syscalls are traced (when the \-S option is used). .TP @@ -73,9 +75,9 @@ Trace only syscalls and print a summary after 10 seconds: .B ucalls -S 788 10 .SH OVERHEAD Tracing individual method calls will produce a considerable overhead in all -high-level languages. For languages with just-in-time compilation, such as -Java, the overhead can be more considerable than for interpreted languages. -On the other hand, syscall tracing will typically be tolerable for most +high-level languages. For languages with just-in-time compilation, such as +Java, the overhead can be more considerable than for interpreted languages. +On the other hand, syscall tracing will typically be tolerable for most processes, unless they have a very unusual rate of system calls. .SH SOURCE This is from bcc. diff --git a/man/man8/uflow.8 b/man/man8/uflow.8 index 63b7c7583671..b6d160e84a32 100644 --- a/man/man8/uflow.8 +++ b/man/man8/uflow.8 @@ -1,23 +1,25 @@ .TH uflow 8 "2016-11-07" "USER COMMANDS" .SH NAME -uflow, javaflow, pythonflow, rubyflow, phpflow \- Print a flow graph of method +uflow, javaflow, perlflow, phpflow, pythonflow, rubyflow \- Print a flow graph of method calls in high-level languages. .SH SYNOPSIS .B javaflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br +.B perlflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid +.br +.B phpflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid +.br .B pythonflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B rubyflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br -.B phpflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid -.br -.B uflow [-h] [-M METHOD] [-C CLAZZ] [-v] [-l {java,python,ruby,php}] pid +.B uflow [-h] [-M METHOD] [-C CLAZZ] [-v] [-l {java,perl,php,python,ruby}] pid .SH DESCRIPTION uflow traces method calls and prints them in a flow graph that can facilitate debugging and diagnostics by following the program's execution (method flow). This tool relies on USDT probes embedded in many high-level languages, such as -Java, Python, Ruby, and PHP. It requires a runtime instrumented with these +Java, Perl, PHP, Python, and Ruby. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java processes, the startup flag "-XX:+ExtendedDTraceProbes" is required. For PHP processes, the @@ -39,7 +41,7 @@ name interpretation strongly depends on the language. For example, in Java use \-v Print the resulting BPF program, for debugging purposes. .TP -{java,python,ruby,php} +{java,perl,php,python,ruby} The language to trace. .TP pid diff --git a/man/man8/ustat.8 b/man/man8/ustat.8 index 8eb231e4bad4..6dd6f1274e16 100644 --- a/man/man8/ustat.8 +++ b/man/man8/ustat.8 @@ -1,34 +1,37 @@ .TH ustat 8 "2016-11-07" "USER COMMANDS" .SH NAME -ustat, javastat, pythonstat, rubystat, nodestat, phpstat \- Activity stats from +ustat, javastat, nodestat, perlstat, phpstat, pythonstat, rubystat \- Activity stats from high-level languages. .SH SYNOPSIS .B javastat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br -.B pythonstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] -.br -.B rubystat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] -.br .B nodestat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br +.B perlstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] +.br .B phpstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br -.B ustat [-l {java,python,ruby,node,php}] [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] +.B pythonstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] +.br +.B rubystat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] +.br +.B ustat [-l {java,perl,python,ruby,node,php}] [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .SH DESCRIPTION This is "top" for high-level language events, such as garbage collections, exceptions, thread creations, object allocations, method calls, and more. The events are aggregated for each process and printed in a top-like table, which -can be sorted by various fields. +can be sorted by various fields. Not all language runtimes provide the same +set of details. This uses in-kernel eBPF maps to store per process summaries for efficiency. This tool relies on USDT probes embedded in many high-level languages, such as -Node, Java, Python, Ruby, and PHP. It requires a runtime instrumented with these -probes, which in some cases requires building from source with a USDT-specific -flag, such as "--enable-dtrace" or "--with-dtrace". For Java, some probes are -not enabled by default, and can be turned on by running the Java process with -the "-XX:+ExtendedDTraceProbes" flag. For PHP processes, the environment -variable USE_ZEND_DTRACE must be set to 1. +Java, Node, Perl, PHP, Python, and Ruby. It requires a runtime instrumented with +these probes, which in some cases requires building from source with a +USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java, +some probes are not enabled by default, and can be turned on by running the Java +process with the "-XX:+ExtendedDTraceProbes" flag. For PHP processes, the +environment variable USE_ZEND_DTRACE must be set to 1. Newly-created processes will only be traced at the next interval. If you run this tool with a short interval (say, 1-5 seconds), this should be virtually @@ -40,7 +43,7 @@ Since this uses BPF, only the root user can use this tool. CONFIG_BPF and bcc. .SH OPTIONS .TP -\-l {java,python,ruby,node,php} +\-l {java,node,perl,php,python,ruby} The language to trace. By default, all languages are traced. .TP \-C diff --git a/snapcraft/snapcraft.yaml b/snapcraft/snapcraft.yaml index d2b65dda0ac5..e4acdb27a102 100644 --- a/snapcraft/snapcraft.yaml +++ b/snapcraft/snapcraft.yaml @@ -180,6 +180,15 @@ apps: opensnoop: command: wrapper opensnoop aliases: [opensnoop] + perlcalls: + command: wrapper perlcalls + aliases: [perlcalls] + perlflow: + command: wrapper perlflow + aliases: [perlflow] + perlstat: + command: wrapper perlstat + aliases: [perlstat] phpcalls: command: wrapper phpcalls aliases: [phpcalls] diff --git a/src/cc/bcc_proc.c b/src/cc/bcc_proc.c index 6fe11a02738c..d694eb939833 100644 --- a/src/cc/bcc_proc.c +++ b/src/cc/bcc_proc.c @@ -406,9 +406,9 @@ void bcc_procutils_free(const char *ptr) { } /* Detects the following languages + C. */ -const char *languages[] = {"java", "python", "ruby", "php", "node"}; +const char *languages[] = {"java", "node", "perl", "php", "python", "ruby"}; const char *language_c = "c"; -const int nb_languages = 5; +const int nb_languages = 6; const char *bcc_procutils_language(int pid) { char procfilename[24], line[4096], pathname[32], *str; diff --git a/tests/python/test_utils.py b/tests/python/test_utils.py index 69dddf6cb247..54b97cfb4b75 100755 --- a/tests/python/test_utils.py +++ b/tests/python/test_utils.py @@ -15,7 +15,7 @@ def test_get_online_cpus(self): self.assertEqual(len(online_cpus), num_cores) def test_detect_language(self): - candidates = ["java", "ruby", "php", "node", "c", "python"] + candidates = ["c", "java", "perl", "php", "node", "ruby", "python"] language = detect_language(candidates, os.getpid()) self.assertEqual(language, "python") diff --git a/tools/lib/ucalls.py b/tools/lib/ucalls.py index 97d60f2293a9..b2d165c20018 100755 --- a/tools/lib/ucalls.py +++ b/tools/lib/ucalls.py @@ -4,7 +4,7 @@ # ucalls Summarize method calls in high-level languages and/or system calls. # For Linux, uses BCC, eBPF. # -# USAGE: ucalls [-l {java,python,ruby,php}] [-h] [-T TOP] [-L] [-S] [-v] [-m] +# USAGE: ucalls [-l {java,perl,php,python,ruby}] [-h] [-T TOP] [-L] [-S] [-v] [-m] # pid [interval] # # Copyright 2016 Sasha Goldshtein @@ -18,7 +18,7 @@ from time import sleep import os -languages = ["java", "python", "ruby", "php"] +languages = ["java", "perl", "php", "python", "ruby"] examples = """examples: ./ucalls -l java 185 # trace Java calls and print statistics on ^C @@ -69,6 +69,18 @@ read_method = "bpf_usdt_readarg(4, ctx, &method);" extra_message = ("If you do not see any results, make sure you ran java" " with option -XX:+ExtendedDTraceProbes") +elif language == "perl": + entry_probe = "sub__entry" + return_probe = "sub__return" + read_class = "bpf_usdt_readarg(2, ctx, &clazz);" # filename really + read_method = "bpf_usdt_readarg(1, ctx, &method);" +elif language == "php": + entry_probe = "function__entry" + return_probe = "function__return" + read_class = "bpf_usdt_readarg(4, ctx, &clazz);" + read_method = "bpf_usdt_readarg(1, ctx, &method);" + extra_message = ("If you do not see any results, make sure the environment" + " variable USE_ZEND_DTRACE is set to 1") elif language == "python": entry_probe = "function__entry" return_probe = "function__return" @@ -80,13 +92,6 @@ return_probe = "method__return" read_class = "bpf_usdt_readarg(1, ctx, &clazz);" read_method = "bpf_usdt_readarg(2, ctx, &method);" -elif language == "php": - entry_probe = "function__entry" - return_probe = "function__return" - read_class = "bpf_usdt_readarg(4, ctx, &clazz);" - read_method = "bpf_usdt_readarg(1, ctx, &method);" - extra_message = ("If you do not see any results, make sure the environment" - " variable USE_ZEND_DTRACE is set to 1") elif not language or language == "none": if not args.syscalls: print("Nothing to do; use -S to trace syscalls.") diff --git a/tools/lib/ucalls_example.txt b/tools/lib/ucalls_example.txt index fffc76f4ef4d..69d401a1ef2e 100644 --- a/tools/lib/ucalls_example.txt +++ b/tools/lib/ucalls_example.txt @@ -2,8 +2,9 @@ Demonstrations of ucalls. ucalls summarizes method calls in various high-level languages, including Java, -Python, Ruby, PHP, and Linux system calls. It displays statistics on the most -frequently called methods, as well as the latency (duration) of these methods. +Perl, PHP, Python, Ruby, and Linux system calls. It displays statistics on the +most frequently called methods, as well as the latency (duration) of these +methods. Through the syscalls support, ucalls can provide basic information on a process' interaction with the system including syscall counts and latencies. @@ -60,7 +61,7 @@ METHOD # CALLS USAGE message: # ./ucalls.py -h -usage: ucalls.py [-h] [-l {java,python,ruby,php,none}] [-T TOP] [-L] [-S] [-v] +usage: ucalls.py [-h] [-l {java,perl,php,python,ruby,none}] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] @@ -72,7 +73,7 @@ positional arguments: optional arguments: -h, --help show this help message and exit - -l {java,python,ruby,php,none}, --language {java,python,ruby,php,none} + -l {java,perl,php,python,ruby,none}, --language {java,perl,php,python,ruby,none} language to trace (if none, trace syscalls only) -T TOP, --top TOP number of most frequent/slow calls to print -L, --latency record method latency from enter to exit (except diff --git a/tools/lib/uflow.py b/tools/lib/uflow.py index 025a19370f5a..8419c8822bc2 100755 --- a/tools/lib/uflow.py +++ b/tools/lib/uflow.py @@ -4,7 +4,7 @@ # uflow Trace method execution flow in high-level languages. # For Linux, uses BCC, eBPF. # -# USAGE: uflow [-C CLASS] [-M METHOD] [-v] {java,python,ruby,php} pid +# USAGE: uflow [-C CLASS] [-M METHOD] [-v] {java,perl,php,python,ruby} pid # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") @@ -18,7 +18,7 @@ import time import os -languages = ["java", "python", "ruby", "php"] +languages = ["java", "perl", "php", "python", "ruby"] examples = """examples: ./uflow -l java 185 # trace Java method calls in process 185 @@ -127,6 +127,20 @@ def enable_probe(probe_name, func_name, read_class, read_method, is_return): enable_probe("method__return", "java_return", "bpf_usdt_readarg(2, ctx, &clazz);", "bpf_usdt_readarg(4, ctx, &method);", is_return=True) +elif language == "perl": + enable_probe("sub__entry", "perl_entry", + "bpf_usdt_readarg(2, ctx, &clazz);", + "bpf_usdt_readarg(1, ctx, &method);", is_return=False) + enable_probe("sub__return", "perl_return", + "bpf_usdt_readarg(2, ctx, &clazz);", + "bpf_usdt_readarg(1, ctx, &method);", is_return=True) +elif language == "php": + enable_probe("function__entry", "php_entry", + "bpf_usdt_readarg(4, ctx, &clazz);", + "bpf_usdt_readarg(1, ctx, &method);", is_return=False) + enable_probe("function__return", "php_return", + "bpf_usdt_readarg(4, ctx, &clazz);", + "bpf_usdt_readarg(1, ctx, &method);", is_return=True) elif language == "python": enable_probe("function__entry", "python_entry", "bpf_usdt_readarg(1, ctx, &clazz);", # filename really @@ -147,13 +161,6 @@ def enable_probe(probe_name, func_name, read_class, read_method, is_return): enable_probe("cmethod__return", "ruby_creturn", "bpf_usdt_readarg(1, ctx, &clazz);", "bpf_usdt_readarg(2, ctx, &method);", is_return=True) -elif language == "php": - enable_probe("function__entry", "php_entry", - "bpf_usdt_readarg(4, ctx, &clazz);", - "bpf_usdt_readarg(1, ctx, &method);", is_return=False) - enable_probe("function__return", "php_return", - "bpf_usdt_readarg(4, ctx, &clazz);", - "bpf_usdt_readarg(1, ctx, &method);", is_return=True) else: print("No language detected; use -l to trace a language.") exit(1) diff --git a/tools/lib/uflow_example.txt b/tools/lib/uflow_example.txt index 557d97209223..fae52f3a2c42 100644 --- a/tools/lib/uflow_example.txt +++ b/tools/lib/uflow_example.txt @@ -4,8 +4,8 @@ Demonstrations of uflow. uflow traces method entry and exit events and prints a visual flow graph that shows how methods are entered and exited, similar to a tracing debugger with breakpoints. This can be useful for understanding program flow in high-level -languages such as Java, Python, Ruby, and PHP, which provide USDT probes for -method invocations. +languages such as Java, Perl, PHP, Python, and Ruby, which provide USDT +probes for method invocations. For example, trace all Ruby method calls in a specific process: @@ -88,7 +88,7 @@ thread running on the same CPU. USAGE message: # ./uflow -h -usage: uflow.py [-h] [-l {java,python,ruby,php}] [-M METHOD] [-C CLAZZ] [-v] +usage: uflow.py [-h] [-l {java,perl,php,python,ruby}] [-M METHOD] [-C CLAZZ] [-v] pid Trace method execution flow in high-level languages. @@ -98,7 +98,7 @@ positional arguments: optional arguments: -h, --help show this help message and exit - -l {java,python,ruby,php}, --language {java,python,ruby,php} + -l {java,perl,php,python,ruby}, --language {java,perl,php,python,ruby} language to trace -M METHOD, --method METHOD trace only calls to methods starting with this prefix diff --git a/tools/lib/ustat.py b/tools/lib/ustat.py index 34cc019b777b..8b2f80fe4803 100755 --- a/tools/lib/ustat.py +++ b/tools/lib/ustat.py @@ -5,7 +5,7 @@ # method calls, class loads, garbage collections, and more. # For Linux, uses BCC, eBPF. # -# USAGE: ustat [-l {java,python,ruby,node,php}] [-C] +# USAGE: ustat [-l {java,node,perl,php,python,ruby}] [-C] # [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] # [interval [count]] # @@ -132,7 +132,7 @@ def _parse_args(self): formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-l", "--language", - choices=["java", "python", "ruby", "node", "php"], + choices=["java", "node", "perl", "php", "python", "ruby"], help="language to trace (default: all languages)") parser.add_argument("-C", "--noclear", action="store_true", help="don't clear the screen") @@ -151,18 +151,30 @@ def _parse_args(self): def _create_probes(self): probes_by_lang = { + "java": Probe("java", ["java"], { + "gc__begin": Category.GC, + "mem__pool__gc__begin": Category.GC, + "thread__start": Category.THREAD, + "class__loaded": Category.CLOAD, + "object__alloc": Category.OBJNEW, + "method__entry": Category.METHOD, + "ExceptionOccurred__entry": Category.EXCP + }), "node": Probe("node", ["node"], { "gc__start": Category.GC }), - "python": Probe("python", ["python"], { - "function__entry": Category.METHOD, - "gc__start": Category.GC + "perl": Probe("perl", ["perl"], { + "sub__entry": Category.METHOD }), "php": Probe("php", ["php"], { "function__entry": Category.METHOD, "compile__file__entry": Category.CLOAD, "exception__thrown": Category.EXCP }), + "python": Probe("python", ["python"], { + "function__entry": Category.METHOD, + "gc__start": Category.GC + }), "ruby": Probe("ruby", ["ruby", "irb"], { "method__entry": Category.METHOD, "cmethod__entry": Category.METHOD, @@ -176,15 +188,6 @@ def _create_probes(self): "load__entry": Category.CLOAD, "raise": Category.EXCP }), - "java": Probe("java", ["java"], { - "gc__begin": Category.GC, - "mem__pool__gc__begin": Category.GC, - "thread__start": Category.THREAD, - "class__loaded": Category.CLOAD, - "object__alloc": Category.OBJNEW, - "method__entry": Category.METHOD, - "ExceptionOccurred__entry": Category.EXCP - }) } if self.args.language: diff --git a/tools/lib/ustat_example.txt b/tools/lib/ustat_example.txt index 79f67fd95b5a..8a9ee87c46e3 100644 --- a/tools/lib/ustat_example.txt +++ b/tools/lib/ustat_example.txt @@ -4,7 +4,7 @@ Demonstrations of ustat. ustat is a "top"-like tool for monitoring events in high-level languages. It prints statistics about garbage collections, method calls, object allocations, and various other events for every process that it recognizes with a Java, -Python, Ruby, Node, or PHP runtime. +Node, Perl, PHP, Python, and Ruby runtime. For example: @@ -48,7 +48,7 @@ PID CMDLINE METHOD/s GC/s OBJNEW/s CLOAD/s EXC/s THR/s USAGE message: # ./ustat.py -h -usage: ustat.py [-h] [-l {java,python,ruby,node,php}] [-C] +usage: ustat.py [-h] [-l {java,node,perl,php,python,ruby}] [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval] [count] @@ -60,7 +60,7 @@ positional arguments: optional arguments: -h, --help show this help message and exit - -l {java,python,ruby,node,php}, --language {java,python,ruby,node,php} + -l {{java,node,perl,php,python,ruby}}, --language {java,node,perl,php,python,ruby} language to trace (default: all languages) -C, --noclear don't clear the screen -S {cload,excp,gc,method,objnew,thread}, --sort {cload,excp,gc,method,objnew,thread} diff --git a/tools/perlcalls.sh b/tools/perlcalls.sh new file mode 100755 index 000000000000..74c6b035f070 --- /dev/null +++ b/tools/perlcalls.sh @@ -0,0 +1,3 @@ +#!/bin/bash +lib=$(dirname $0)/lib +$lib/ucalls.py -l perl "$@" diff --git a/tools/perlcalls_example.txt b/tools/perlcalls_example.txt new file mode 120000 index 000000000000..22b0fb3ed178 --- /dev/null +++ b/tools/perlcalls_example.txt @@ -0,0 +1 @@ +lib/ucalls_example.txt \ No newline at end of file diff --git a/tools/perlflow.sh b/tools/perlflow.sh new file mode 100755 index 000000000000..4fd2397d37e8 --- /dev/null +++ b/tools/perlflow.sh @@ -0,0 +1,3 @@ +#!/bin/bash +lib=$(dirname $0)/lib +$lib/uflow.py -l perl "$@" diff --git a/tools/perlflow_example.txt b/tools/perlflow_example.txt new file mode 120000 index 000000000000..bc71efc6ff7d --- /dev/null +++ b/tools/perlflow_example.txt @@ -0,0 +1 @@ +lib/uflow_example.txt \ No newline at end of file diff --git a/tools/perlstat.sh b/tools/perlstat.sh new file mode 100755 index 000000000000..4bb417f2fdd7 --- /dev/null +++ b/tools/perlstat.sh @@ -0,0 +1,3 @@ +#!/bin/bash +lib=$(dirname $0)/lib +$lib/ustat.py -l perl "$@" diff --git a/tools/perlstat_example.txt b/tools/perlstat_example.txt new file mode 120000 index 000000000000..544e5ade4807 --- /dev/null +++ b/tools/perlstat_example.txt @@ -0,0 +1 @@ +lib/ustat_example.txt \ No newline at end of file