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