summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorMark Salyzyn <salyzyn@google.com>2016-05-12 10:48:25 -0700
committerMark Salyzyn <salyzyn@google.com>2016-05-13 09:33:59 -0700
commit9dc32a9fb62f75184f881e8c68400dbe6422ba2c (patch)
tree6f005f26147d6a517441486e3dcab138a9173eee /tools
parent2597f3521efd5e6f999ad9aa1e0e3dd6304ba3e4 (diff)
downloadextras-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-xtools/graph_lockdep_chains284
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}