| Mark Salyzyn | 9dc32a9 | 2016-05-12 10:48:25 -0700 | [diff] [blame] | 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} |