Home | History | Annotate | Download | only in tools
      1 #! /bin/sh
      2 progname="${0##*/}"
      3 progname="${progname%.sh}"
      4 
      5 usage() {
      6   echo "Host side filter pipeline tool to convert kernel /proc/lockdep_chains via"
      7   echo "graphviz into dependency chart for visualization. Watch out for any up-arrows"
      8   echo "as they signify a circular dependency."
      9   echo
     10   echo "Usage: ${progname} [flags...] [regex...] < input-file > output-file"
     11   echo
     12   echo "flags:"
     13   echo "       --format={png|ps|svg|fig|imap|cmapx} | -T<format>"
     14   echo "           Output format, default png"
     15   echo "       --debug | -d"
     16   echo "           Leave intermediate files /tmp/${progname}.*"
     17   echo "       --verbose | -v"
     18   echo "           Do not strip address from lockname"
     19   echo "       --focus | -f"
     20   echo "           Show only primary references for regex matches"
     21   echo "       --cluster"
     22   echo "           Cluster the primary references for regex matches"
     23   echo "       --serial=<serial> | -s <serial>"
     24   echo "           Input from 'adb -s <serial> shell su 0 cat /proc/lockdep_chains'"
     25   echo "       --input=<filename> | -i <filename>"
     26   echo "           Input lockdeps from filename, otherwise from standard in"
     27   echo "       --output=<filename> | -o <filename>"
     28   echo "           Output formatted graph to filename, otherwise to standard out"
     29   echo
     30   echo "Chart is best viewed in portrait. ps or pdf formats tend to pixelate. png tends"
     31   echo "to hit a bug in cairo rendering at scale. Not having a set of regex matches for"
     32   echo "locknames will probably give you what you deserve ..."
     33   echo
     34   echo "Kernel Prerequisite to get /proc/lockdep_chains:"
     35   echo "       CONFIG_PROVE_LOCKING=y"
     36   echo "       CONFIG_LOCK_STAT=y"
     37   echo "       CONFIG_DEBUG_LOCKDEP=y"
     38 }
     39 
     40 rm -f /tmp/${progname}.*
     41 
     42 # Indent rules and strip out address (may be overridden below)
     43 beautify() {
     44   sed 's/^./    &/
     45        s/"[[][0-9a-f]*[]] /"/g'
     46 }
     47 
     48 input="cat -"
     49 output="cat -"
     50 
     51 dot_format="-Tpng"
     52 filter=
     53 debug=
     54 focus=
     55 cluster=
     56 
     57 while [ ${#} -gt 0 ]; do
     58   case ${1} in
     59 
     60     -T | --format)
     61       dot_format="-T${2}"
     62       shift
     63       ;;
     64 
     65     -T*)
     66       dot_format="${1}"
     67       ;;
     68 
     69     --format=*)
     70       dot_format="-T${1#--format=}"
     71       ;;
     72 
     73     --debug | -d)
     74       debug=1
     75       ;;
     76 
     77     --verbose | -v)
     78       # indent, but do _not_ strip out addresses
     79       beautify() {
     80         sed 's/^./    &/'
     81       }
     82       ;;
     83 
     84     --focus | -f | --primary) # reserving --primary
     85       focus=1
     86       ;;
     87 
     88     --secondary) # reserving --secondary
     89       focus=
     90       ;;
     91 
     92     --cluster) # reserve -c for dot (configure plugins)
     93       cluster=1
     94       ;;
     95 
     96     --serial | -s)
     97       if [ "${input}" != "cat -" ]; then
     98         usage >&2
     99         echo "ERROR: --input or --serial can only be specified once" >&2
    100         exit 1
    101       fi
    102       input="adb -s ${2} shell su 0 cat /proc/lockdep_chains"
    103       shift
    104       ;;
    105 
    106     --serial=*)
    107       input="adb -s ${1#--serial=} shell su 0 cat /proc/lockdep_chains"
    108       ;;
    109 
    110     --input | -i)
    111       if [ "${input}" != "cat -" ]; then
    112         usage >&2
    113         echo "ERROR: --input or --serial can only be specified once" >&2
    114         exit 1
    115       fi
    116       input="cat ${2}"
    117       shift
    118       ;;
    119 
    120     --input=*)
    121       if [ "${input}" != "cat -" ]; then
    122         usage >&2
    123         echo "ERROR: --input or --serial can only be specified once" >&2
    124         exit 1
    125       fi
    126       input="cat ${1#--input=}"
    127       ;;
    128 
    129     --output | -o)
    130       if [ "${output}" != "cat -" ]; then
    131         usage >&2
    132         echo "ERROR: --output can only be specified once" >&2
    133         exit 1
    134       fi
    135       output="cat - > ${2}" # run through eval
    136       shift
    137       ;;
    138 
    139     --output=*)
    140       if [ "${output}" != "cat -" ]; then
    141         usage >&2
    142         echo "ERROR: --output can only be specified once" >&2
    143         exit 1
    144       fi
    145       output="cat - > ${1#--output=}" # run through eval
    146       ;;
    147 
    148     --help | -h | -\?)
    149       usage
    150       exit
    151       ;;
    152 
    153     *)
    154       # Everything else is a filter, which will also hide bad option flags,
    155       # which is an as-designed price we pay to allow "->rwlock" for instance.
    156       if [ X"${1}" = X"${1#* }" ]; then
    157         if [ -z "${filter}" ]; then
    158           filter="${1}"
    159         else
    160           filter="${filter}|${1}"
    161         fi
    162       else
    163         if [ -z "${filter}" ]; then
    164           filter=" ${1}"
    165         else
    166           filter="${filter}| ${1}"
    167         fi
    168       fi
    169       ;;
    170 
    171   esac
    172   shift
    173 done
    174 
    175 if [ -z "${filter}" ]; then
    176   echo "WARNING: no regex specified will give you what you deserve!" >&2
    177 fi
    178 if [ -n "${focus}" -a -z "${filter}" ]; then
    179   echo "WARNING: --focus without regex, ignored" >&2
    180 fi
    181 if [ -n "${cluster}" -a -z "${filter}" ]; then
    182   echo "WARNING: --cluster without regex, ignored" >&2
    183 fi
    184 if [ -n "${cluster}" -a -n "${focus}" -a -n "${filter}" ]; then
    185   echo "WARNING: orthogonal options --cluster & --focus, ignoring --cluster" >&2
    186   cluster=
    187 fi
    188 
    189 # convert to dot digraph series
    190 ${input} |
    191   sed '/^all lock chains:$/d
    192        / [&]__lockdep_no_validate__$/d
    193        /irq_context: 0/d
    194        s/irq_context: [1-9]/irq_context/
    195        s/..*/"&" ->/
    196        s/^$/;/' |
    197     sed ': loop
    198          N
    199          s/ ->\n;$/ ;/
    200          t
    201          s/ ->\n/ -> /
    202          b loop' > /tmp/${progname}.formed
    203 
    204 if [ ! -s /tmp/${progname}.formed ]; then
    205   echo "ERROR: no input" >&2
    206   if [ -z "${debug}" ]; then
    207     rm -f /tmp/${progname}.*
    208   fi
    209   exit 2
    210 fi
    211 
    212 if [ -n "${filter}" ]; then
    213   grep "${filter}" /tmp/${progname}.formed |
    214     sed 's/ ;//
    215          s/ -> /|/g' |
    216       tr '|' '\n' |
    217         sort -u > /tmp/${progname}.symbols
    218 fi
    219 
    220 (
    221   echo 'digraph G {'
    222   (
    223     echo 'remincross="true";'
    224     echo 'concentrate="true";'
    225     echo
    226 
    227     if [ -s /tmp/${progname}.symbols ]; then
    228       if [ -n "${cluster}" ]; then
    229         echo 'subgraph cluster_symbols {'
    230         (
    231           grep "${filter}" /tmp/${progname}.symbols |
    232             sed 's/.*/& [shape=box] ;/'
    233           grep -v "${filter}" /tmp/${progname}.symbols |
    234             sed 's/.*/& [shape=diamond] ;/'
    235         ) | beautify
    236         echo '}'
    237       else
    238         grep "${filter}" /tmp/${progname}.symbols |
    239           sed 's/.*/& [shape=box] ;/'
    240         grep -v "${filter}" /tmp/${progname}.symbols |
    241           sed 's/.*/& [shape=diamond] ;/'
    242       fi
    243 
    244       echo
    245     fi
    246   ) | beautify
    247 
    248   if [ -s /tmp/${progname}.symbols ]; then
    249     if [ -z "${focus}" ]; then
    250       # Secondary relationships
    251       fgrep -f /tmp/${progname}.symbols /tmp/${progname}.formed
    252     else
    253       # Focus only on primary relationships
    254       grep "${filter}" /tmp/${progname}.formed
    255     fi
    256   else
    257     cat /tmp/${progname}.formed
    258   fi |
    259     # optimize int A -> B ; single references
    260     sed 's/\("[^"]*"\) -> \("[^"]*"\) ->/\1 -> \2 ;|\2 ->/g' |
    261       sed 's/\("[^"]*"\) -> \("[^"]*"\) ->/\1 -> \2 ;|\2 ->/g' |
    262         tr '|' '\n' |
    263           beautify |
    264             grep ' -> ' |
    265               sort -u |
    266                 if [ -s /tmp/${progname}.symbols ]; then
    267                   beautify < /tmp/${progname}.symbols |
    268                     sed 's/^  */ /' > /tmp/${progname}.short
    269                   tee /tmp/${progname}.split |
    270                     fgrep -f /tmp/${progname}.short |
    271                       sed 's/ ;$/ [color=red] ;/'
    272                   fgrep -v -f /tmp/${progname}.short /tmp/${progname}.split
    273                   rm -f /tmp/${progname}.short /tmp/${progname}.split
    274                 else
    275                   cat -
    276                 fi
    277 
    278   echo '}'
    279 ) |
    280   tee /tmp/${progname}.input |
    281     if dot ${dot_format} && [ -z "${debug}" ]; then
    282       rm -f /tmp/${progname}.*
    283     fi |
    284       eval ${output}
    285