You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
7.3 KiB
285 lines
7.3 KiB
#! /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}
|