Add Perl support for ucalls / uflow / ustat (#1959)

Sort language entries while at it.
diff --git a/tools/lib/ucalls.py b/tools/lib/ucalls.py
index 97d60f2..b2d165c 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 fffc76f..69d401a 100644
--- a/tools/lib/ucalls_example.txt
+++ b/tools/lib/ucalls_example.txt
@@ -2,8 +2,9 @@
 
 
 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 @@
 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 @@
 
 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 025a193..8419c88 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 @@
     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 @@
     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 557d972..fae52f3 100644
--- a/tools/lib/uflow_example.txt
+++ b/tools/lib/uflow_example.txt
@@ -4,8 +4,8 @@
 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 @@
 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 @@
 
 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 34cc019..8b2f80f 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 @@
             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 _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 @@
                     "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 79f67fd..8a9ee87 100644
--- a/tools/lib/ustat_example.txt
+++ b/tools/lib/ustat_example.txt
@@ -4,7 +4,7 @@
 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 @@
 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 @@
 
 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 0000000..74c6b03
--- /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 0000000..22b0fb3
--- /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 0000000..4fd2397
--- /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 0000000..bc71efc
--- /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 0000000..4bb417f
--- /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 0000000..544e5ad
--- /dev/null
+++ b/tools/perlstat_example.txt
@@ -0,0 +1 @@
+lib/ustat_example.txt
\ No newline at end of file