diff options
author | Mark Salyzyn <salyzyn@google.com> | 2016-05-12 10:48:25 -0700 |
---|---|---|
committer | Mark Salyzyn <salyzyn@google.com> | 2016-05-13 09:33:59 -0700 |
commit | 9dc32a9fb62f75184f881e8c68400dbe6422ba2c (patch) | |
tree | 6f005f26147d6a517441486e3dcab138a9173eee /tools | |
parent | 2597f3521efd5e6f999ad9aa1e0e3dd6304ba3e4 (diff) | |
download | extras-9dc32a9fb62f75184f881e8c68400dbe6422ba2c.tar.gz |
lockdep_chains: graph visualization
Create the system/extras/tools directory for simple analysis helpers.
Publish a host tool helper script that aids visualization of kernel
runtime collected lock dependencies. Helpful to look for circular
locks, or to get a handle on locking order used in a driver.
Bug: 28310683
Change-Id: Ib52c23f4cdefb37882232eff11b943eeb1b2abfa
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/graph_lockdep_chains | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/tools/graph_lockdep_chains b/tools/graph_lockdep_chains new file mode 100755 index 00000000..c0c11f45 --- /dev/null +++ b/tools/graph_lockdep_chains @@ -0,0 +1,284 @@ +#! /bin/sh +progname="${0##*/}" +progname="${progname%.sh}" + +usage() { + echo "Host side filter pipeline tool to convert kernel /proc/lockdep_chains via" + echo "graphviz into dependency chart for visualization. Watch out for any up-arrows" + echo "as they signify a circular dependency." + echo + echo "Usage: ${progname} [flags...] [regex...] < input-file > output-file" + echo + echo "flags:" + echo " --format={png|ps|svg|fig|imap|cmapx} | -T<format>" + echo " Output format, default png" + echo " --debug | -d" + echo " Leave intermediate files /tmp/${progname}.*" + echo " --verbose | -v" + echo " Do not strip address from lockname" + echo " --focus | -f" + echo " Show only primary references for regex matches" + echo " --cluster" + echo " Cluster the primary references for regex matches" + echo " --serial=<serial> | -s <serial>" + echo " Input from 'adb -s <serial> shell su 0 cat /proc/lockdep_chains'" + echo " --input=<filename> | -i <filename>" + echo " Input lockdeps from filename, otherwise from standard in" + echo " --output=<filename> | -o <filename>" + echo " Output formatted graph to filename, otherwise to standard out" + echo + echo "Chart is best viewed in portrait. ps or pdf formats tend to pixelate. png tends" + echo "to hit a bug in cairo rendering at scale. Not having a set of regex matches for" + echo "locknames will probably give you what you deserve ..." + echo + echo "Kernel Prerequisite to get /proc/lockdep_chains:" + echo " CONFIG_PROVE_LOCKING=y" + echo " CONFIG_LOCK_STAT=y" + echo " CONFIG_DEBUG_LOCKDEP=y" +} + +rm -f /tmp/${progname}.* + +# Indent rules and strip out address (may be overridden below) +beautify() { + sed 's/^./ &/ + s/"[[][0-9a-f]*[]] /"/g' +} + +input="cat -" +output="cat -" + +dot_format="-Tpng" +filter= +debug= +focus= +cluster= + +while [ ${#} -gt 0 ]; do + case ${1} in + + -T | --format) + dot_format="-T${2}" + shift + ;; + + -T*) + dot_format="${1}" + ;; + + --format=*) + dot_format="-T${1#--format=}" + ;; + + --debug | -d) + debug=1 + ;; + + --verbose | -v) + # indent, but do _not_ strip out addresses + beautify() { + sed 's/^./ &/' + } + ;; + + --focus | -f | --primary) # reserving --primary + focus=1 + ;; + + --secondary) # reserving --secondary + focus= + ;; + + --cluster) # reserve -c for dot (configure plugins) + cluster=1 + ;; + + --serial | -s) + if [ "${input}" != "cat -" ]; then + usage >&2 + echo "ERROR: --input or --serial can only be specified once" >&2 + exit 1 + fi + input="adb -s ${2} shell su 0 cat /proc/lockdep_chains" + shift + ;; + + --serial=*) + input="adb -s ${1#--serial=} shell su 0 cat /proc/lockdep_chains" + ;; + + --input | -i) + if [ "${input}" != "cat -" ]; then + usage >&2 + echo "ERROR: --input or --serial can only be specified once" >&2 + exit 1 + fi + input="cat ${2}" + shift + ;; + + --input=*) + if [ "${input}" != "cat -" ]; then + usage >&2 + echo "ERROR: --input or --serial can only be specified once" >&2 + exit 1 + fi + input="cat ${1#--input=}" + ;; + + --output | -o) + if [ "${output}" != "cat -" ]; then + usage >&2 + echo "ERROR: --output can only be specified once" >&2 + exit 1 + fi + output="cat - > ${2}" # run through eval + shift + ;; + + --output=*) + if [ "${output}" != "cat -" ]; then + usage >&2 + echo "ERROR: --output can only be specified once" >&2 + exit 1 + fi + output="cat - > ${1#--output=}" # run through eval + ;; + + --help | -h | -\?) + usage + exit + ;; + + *) + # Everything else is a filter, which will also hide bad option flags, + # which is an as-designed price we pay to allow "->rwlock" for instance. + if [ X"${1}" = X"${1#* }" ]; then + if [ -z "${filter}" ]; then + filter="${1}" + else + filter="${filter}|${1}" + fi + else + if [ -z "${filter}" ]; then + filter=" ${1}" + else + filter="${filter}| ${1}" + fi + fi + ;; + + esac + shift +done + +if [ -z "${filter}" ]; then + echo "WARNING: no regex specified will give you what you deserve!" >&2 +fi +if [ -n "${focus}" -a -z "${filter}" ]; then + echo "WARNING: --focus without regex, ignored" >&2 +fi +if [ -n "${cluster}" -a -z "${filter}" ]; then + echo "WARNING: --cluster without regex, ignored" >&2 +fi +if [ -n "${cluster}" -a -n "${focus}" -a -n "${filter}" ]; then + echo "WARNING: orthogonal options --cluster & --focus, ignoring --cluster" >&2 + cluster= +fi + +# convert to dot digraph series +${input} | + sed '/^all lock chains:$/d + / [&]__lockdep_no_validate__$/d + /irq_context: 0/d + s/irq_context: [1-9]/irq_context/ + s/..*/"&" ->/ + s/^$/;/' | + sed ': loop + N + s/ ->\n;$/ ;/ + t + s/ ->\n/ -> / + b loop' > /tmp/${progname}.formed + +if [ ! -s /tmp/${progname}.formed ]; then + echo "ERROR: no input" >&2 + if [ -z "${debug}" ]; then + rm -f /tmp/${progname}.* + fi + exit 2 +fi + +if [ -n "${filter}" ]; then + grep "${filter}" /tmp/${progname}.formed | + sed 's/ ;// + s/ -> /|/g' | + tr '|' '\n' | + sort -u > /tmp/${progname}.symbols +fi + +( + echo 'digraph G {' + ( + echo 'remincross="true";' + echo 'concentrate="true";' + echo + + if [ -s /tmp/${progname}.symbols ]; then + if [ -n "${cluster}" ]; then + echo 'subgraph cluster_symbols {' + ( + grep "${filter}" /tmp/${progname}.symbols | + sed 's/.*/& [shape=box] ;/' + grep -v "${filter}" /tmp/${progname}.symbols | + sed 's/.*/& [shape=diamond] ;/' + ) | beautify + echo '}' + else + grep "${filter}" /tmp/${progname}.symbols | + sed 's/.*/& [shape=box] ;/' + grep -v "${filter}" /tmp/${progname}.symbols | + sed 's/.*/& [shape=diamond] ;/' + fi + + echo + fi + ) | beautify + + if [ -s /tmp/${progname}.symbols ]; then + if [ -z "${focus}" ]; then + # Secondary relationships + fgrep -f /tmp/${progname}.symbols /tmp/${progname}.formed + else + # Focus only on primary relationships + grep "${filter}" /tmp/${progname}.formed + fi + else + cat /tmp/${progname}.formed + fi | + # optimize int A -> B ; single references + sed 's/\("[^"]*"\) -> \("[^"]*"\) ->/\1 -> \2 ;|\2 ->/g' | + sed 's/\("[^"]*"\) -> \("[^"]*"\) ->/\1 -> \2 ;|\2 ->/g' | + tr '|' '\n' | + beautify | + grep ' -> ' | + sort -u | + if [ -s /tmp/${progname}.symbols ]; then + beautify < /tmp/${progname}.symbols | + sed 's/^ */ /' > /tmp/${progname}.short + tee /tmp/${progname}.split | + fgrep -f /tmp/${progname}.short | + sed 's/ ;$/ [color=red] ;/' + fgrep -v -f /tmp/${progname}.short /tmp/${progname}.split + rm -f /tmp/${progname}.short /tmp/${progname}.split + else + cat - + fi + + echo '}' +) | + tee /tmp/${progname}.input | + if dot ${dot_format} && [ -z "${debug}" ]; then + rm -f /tmp/${progname}.* + fi | + eval ${output} |