| 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. |
| |
| |
| For example, trace all Ruby method calls in a specific process: |
| |
| # ./uflow -l ruby 27245 |
| Tracing method calls in ruby process 27245... Ctrl-C to quit. |
| CPU PID TID TIME(us) METHOD |
| 3 27245 27245 4.536 <- IO.gets |
| 3 27245 27245 4.536 <- IRB::StdioInputMethod.gets |
| 3 27245 27245 4.536 -> IRB::Context.verbose? |
| 3 27245 27245 4.536 -> NilClass.nil? |
| 3 27245 27245 4.536 <- NilClass.nil? |
| 3 27245 27245 4.536 -> IO.tty? |
| 3 27245 27245 4.536 <- IO.tty? |
| 3 27245 27245 4.536 -> Kernel.kind_of? |
| 3 27245 27245 4.536 <- Kernel.kind_of? |
| 3 27245 27245 4.536 <- IRB::Context.verbose? |
| 3 27245 27245 4.536 <- IRB::Irb.signal_status |
| 3 27245 27245 4.536 -> String.chars |
| 3 27245 27245 4.536 <- String.chars |
| ^C |
| |
| In the preceding output, indentation indicates the depth of the flow graph, |
| and the <- and -> arrows indicate the direction of the event (exit or entry). |
| |
| Often, the amount of output can be overwhelming. You can filter specific |
| classes or methods. For example, trace only methods from the Thread class: |
| |
| # ./uflow -C java/lang/Thread $(pidof java) |
| Tracing method calls in java process 27722... Ctrl-C to quit. |
| CPU PID TID TIME(us) METHOD |
| 3 27722 27731 3.144 -> java/lang/Thread.<init> |
| 3 27722 27731 3.144 -> java/lang/Thread.init |
| 3 27722 27731 3.144 -> java/lang/Thread.init |
| 3 27722 27731 3.144 -> java/lang/Thread.currentThread |
| 3 27722 27731 3.144 <- java/lang/Thread.currentThread |
| 3 27722 27731 3.144 -> java/lang/Thread.getThreadGroup |
| 3 27722 27731 3.144 <- java/lang/Thread.getThreadGroup |
| 3 27722 27731 3.144 -> java/lang/ThreadGroup.checkAccess |
| 3 27722 27731 3.144 <- java/lang/ThreadGroup.checkAccess |
| 3 27722 27731 3.144 -> java/lang/ThreadGroup.addUnstarted |
| 3 27722 27731 3.144 <- java/lang/ThreadGroup.addUnstarted |
| 3 27722 27731 3.145 -> java/lang/Thread.isDaemon |
| 3 27722 27731 3.145 <- java/lang/Thread.isDaemon |
| 3 27722 27731 3.145 -> java/lang/Thread.getPriority |
| 3 27722 27731 3.145 <- java/lang/Thread.getPriority |
| 3 27722 27731 3.145 -> java/lang/Thread.getContextClassLoader |
| 3 27722 27731 3.145 <- java/lang/Thread.getContextClassLoader |
| 3 27722 27731 3.145 -> java/lang/Thread.setPriority |
| 3 27722 27731 3.145 -> java/lang/Thread.checkAccess |
| 3 27722 27731 3.145 <- java/lang/Thread.checkAccess |
| 3 27722 27731 3.145 -> java/lang/Thread.getThreadGroup |
| 3 27722 27731 3.145 <- java/lang/Thread.getThreadGroup |
| 3 27722 27731 3.145 -> java/lang/ThreadGroup.getMaxPriority |
| 3 27722 27731 3.145 <- java/lang/ThreadGroup.getMaxPriority |
| 3 27722 27731 3.145 -> java/lang/Thread.setPriority0 |
| 3 27722 27731 3.145 <- java/lang/Thread.setPriority0 |
| 3 27722 27731 3.145 <- java/lang/Thread.setPriority |
| 3 27722 27731 3.145 -> java/lang/Thread.nextThreadID |
| 3 27722 27731 3.145 <- java/lang/Thread.nextThreadID |
| 3 27722 27731 3.145 <- java/lang/Thread.init |
| 3 27722 27731 3.145 <- java/lang/Thread.init |
| 3 27722 27731 3.145 <- java/lang/Thread.<init> |
| 3 27722 27731 3.145 -> java/lang/Thread.start |
| 3 27722 27731 3.145 -> java/lang/ThreadGroup.add |
| 3 27722 27731 3.145 <- java/lang/ThreadGroup.add |
| 3 27722 27731 3.145 -> java/lang/Thread.start0 |
| 3 27722 27731 3.145 <- java/lang/Thread.start0 |
| 3 27722 27731 3.146 <- java/lang/Thread.start |
| 2 27722 27742 3.146 -> java/lang/Thread.run |
| ^C |
| |
| The reason that the CPU number is printed in the first column is that events |
| from different threads can be reordered when running on different CPUs, and |
| produce non-sensible output. By looking for changes in the CPU column, you can |
| easily see if the events you're following make sense and belong to the same |
| thread running on the same CPU. |
| |
| |
| USAGE message: |
| |
| # ./uflow -h |
| usage: uflow.py [-h] [-l {java,python,ruby,php}] [-M METHOD] [-C CLAZZ] [-v] |
| pid |
| |
| Trace method execution flow in high-level languages. |
| |
| positional arguments: |
| pid process id to attach to |
| |
| optional arguments: |
| -h, --help show this help message and exit |
| -l {java,python,ruby,php}, --language {java,python,ruby,php} |
| language to trace |
| -M METHOD, --method METHOD |
| trace only calls to methods starting with this prefix |
| -C CLAZZ, --class CLAZZ |
| trace only calls to classes starting with this prefix |
| -v, --verbose verbose mode: print the BPF program (for debugging |
| purposes) |
| |
| examples: |
| ./uflow -l java 185 # trace Java method calls in process 185 |
| ./uflow -l ruby 134 # trace Ruby method calls in process 134 |
| ./uflow -M indexOf -l java 185 # trace only 'indexOf'-prefixed methods |
| ./uflow -C '<stdin>' -l python 180 # trace only REPL-defined methods |