blob: c0c11f459c4644aaf110aebb48330cf89abb633b [file] [log] [blame]
Mark Salyzyn9dc32a92016-05-12 10:48:25 -07001#! /bin/sh
2progname="${0##*/}"
3progname="${progname%.sh}"
4
5usage() {
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
40rm -f /tmp/${progname}.*
41
42# Indent rules and strip out address (may be overridden below)
43beautify() {
44 sed 's/^./ &/
45 s/"[[][0-9a-f]*[]] /"/g'
46}
47
48input="cat -"
49output="cat -"
50
51dot_format="-Tpng"
52filter=
53debug=
54focus=
55cluster=
56
57while [ ${#} -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
173done
174
175if [ -z "${filter}" ]; then
176 echo "WARNING: no regex specified will give you what you deserve!" >&2
177fi
178if [ -n "${focus}" -a -z "${filter}" ]; then
179 echo "WARNING: --focus without regex, ignored" >&2
180fi
181if [ -n "${cluster}" -a -z "${filter}" ]; then
182 echo "WARNING: --cluster without regex, ignored" >&2
183fi
184if [ -n "${cluster}" -a -n "${focus}" -a -n "${filter}" ]; then
185 echo "WARNING: orthogonal options --cluster & --focus, ignoring --cluster" >&2
186 cluster=
187fi
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
204if [ ! -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
210fi
211
212if [ -n "${filter}" ]; then
213 grep "${filter}" /tmp/${progname}.formed |
214 sed 's/ ;//
215 s/ -> /|/g' |
216 tr '|' '\n' |
217 sort -u > /tmp/${progname}.symbols
218fi
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}