• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /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}
285