1#! /bin/sh
2# SPDX-License-Identifier: GPL-2.0
3#
4# (c) 2019, Google
5progname="${0##*/}"
6
7# Add instrumentation to module_init and probe functions.
8
9USAGE="USAGE: ${progname} [dir|file]
10
11Add debug instrumentation to module_init and probe functions."
12
13if [ X"--help" = X"${1}" -o X"{-h}" = X"${1}" ]; then
14  echo "${USAGE}" >&2
15  exit
16fi
17DIR=.
18if [ 1 = ${#} ]; then
19  DIR=${1}
20  shift
21fi
22if [ 0 != ${#} ]; then
23  echo "Unexpected Argument: ${*}" >&2
24  echo >&2
25  echo "${USAGE}" >&2
26  exit 1
27fi
28
29# A _real_ embedded tab character
30TAB="`echo | tr '\n' '\t'`"
31# A _real_ embedded escape character
32ESCAPE="`echo | tr '\n' '\033'`"
33# Colours
34RED="${ESCAPE}[38;5;196m"
35ORANGE="${ESCAPE}[38;5;255:165:0m"
36BLUE="${ESCAPE}[35m"
37NORMAL="${ESCAPE}[0m"
38
39TMP=`mktemp -d`
40
41# ${file} ${call} and ${symbol} input
42SYMBOLS=" "
43add_debug_printk()
44{
45  message="printk(KERN_WARNING \"${file##*/}:"
46  if [ -z "${call#select}" ]; then
47    # We need uniqueness for discovery, so instead of truth of __func__ to
48    # cover any of our mistakes(!), we select the expectation.
49    message+="${symbol}: ENTER\\\\n\");"
50  else
51    message+="%s:${call}: ENTER\\\\n\", __func__);"
52  fi
53  if grep "${TAB}${message%%\\*}" ${file} >/dev/null; then
54    if [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then
55      echo INFO: already ${file} instrumented ${call#select} ${symbol} >&2
56      SYMBOLS="${SYMBOLS}${symbol}:${call} "
57    fi
58    return
59  fi
60  if [ "device_initcall" = "${call}" -o "module_init" = "${call}" -o "module_exit" = "${call}" ] &&
61     grep "${TAB}printk(KERN_WARNING \"${file##*/}:%s:\(builtin\|module\)_[a-z]*_driver: ENTER" ${file} >/dev/null; then
62    if [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then
63      echo INFO: maybe already ${file} instrumented ${call#select} ${symbol} >&2
64    fi
65    return
66  fi
67  sed "/^\(static \)\{0,1\}.* ${symbol}(/ {
68         N
69         s/^\(static \)\{0,1\}\(.* ${symbol}(\)\(void\)\{0,1\}) {[ ${TAB}]*}\(\n\)/\1\2\3)\4{\4${TAB}${message}\4}\4/
70         t exit
71         N
72         /\/[*][^*]*\$/ {
73           : comment_loop
74           N
75           /\/[*].*[*]\// b comment_done
76           b comment_loop
77           : comment_done
78           /\/[*].*[*]\/\$/ N
79         }
80         /printk(KERN_WARNING \"${file##*/}:/ b exit
81         s/\(\n${TAB}\)\(kfree(\|DBG(\|return[ ${TAB}]\|if (\|platform_driver_\|class_destroy(\|pr_info(\|[a-z_0-9]*_register\(_drivers\|_simple\)\{0,1\}(\|[a-z_0-9]*_\(un\|de\)register\(_drivers\|_simple\)\{0,1\}(\|[a-z+_0-9]*_driver(\)/\1${message}&/
82         t exit
83         N
84         /printk(KERN_WARNING \"${file##*/}:/ b exit
85         s/\(\n${TAB}\)\(DBG(\|return[ ${TAB}]\|if (\|platform_driver_\|pr_info(\|[a-z_0-9]*_register\(_drivers\|_simple\)\{0,1\}(\|[a-z_0-9]*_\(un\|de\)register\(_drivers\|_simple\)\{0,1\}(\)/\1${message}&/
86         t exit
87         s/\(static void .*\)\(\n${TAB}\)\([a-z].*\)\2}\$/\1\2${message}\2\3\2}/
88         t exit
89         N
90         /printk(KERN_WARNING \"${file##*/}:/ b exit
91         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
92         t exit
93         N
94         /printk(KERN_WARNING \"${file##*/}:/ b exit
95         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
96         t exit
97         N
98         /printk(KERN_WARNING \"${file##*/}:/ b exit
99         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
100         t exit
101         N
102         /printk(KERN_WARNING \"${file##*/}:/ b exit
103         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
104         t exit
105         N
106         /printk(KERN_WARNING \"${file##*/}:/ b exit
107         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
108         t exit
109         N
110         /printk(KERN_WARNING \"${file##*/}:/ b exit
111         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
112         t exit
113         N
114         /printk(KERN_WARNING \"${file##*/}:/ b exit
115         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
116         t exit
117         N
118         /printk(KERN_WARNING \"${file##*/}:/ b exit
119         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
120         t exit
121         N
122         /printk(KERN_WARNING \"${file##*/}:/ b exit
123         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
124         t exit
125         N
126         /printk(KERN_WARNING \"${file##*/}:/ b exit
127         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
128         t exit
129         N
130         /printk(KERN_WARNING \"${file##*/}:/ b exit
131         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
132         t exit
133         N
134         /printk(KERN_WARNING \"${file##*/}:/ b exit
135         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
136         t exit
137         N
138         /printk(KERN_WARNING \"${file##*/}:/ b exit
139         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
140         t exit
141         N
142         /printk(KERN_WARNING \"${file##*/}:/ b exit
143         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
144         t exit
145         N
146         /printk(KERN_WARNING \"${file##*/}:/ b exit
147         s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/
148         : exit
149       }
150       s/^builtin_\([a-z]*\)_driver(\(${symbol}\));\$/static int __init \2_init(void)\n{\n${TAB}${message}\n${TAB}return \1_driver_register(\&\2);\n}\ndevice_initcall(\2_init);/
151       s/^module_\([a-z]*\)_driver(\(${symbol}\));\$/static int __init \2_init(void)\n{\n${TAB}${message}\n${TAB}return \1_driver_register(\&\2);\n}\nmodule_init(\2_init);\n\nstatic void __exit \2_exit(void)\n{\n${TAB}${message}\n${TAB}\1_driver_unregister(\&\2);\n}\nmodule_exit(\2_exit);/
152       " ${file} > ${TMP}/${file##*/} &&
153    ! cmp -s ${file} ${TMP}/${file##*/} &&
154    cp ${TMP}/${file##*/} ${file} &&
155    (
156      echo INFO: modified ${file} to instrument ${call#select} ${symbol} >&2
157      echo ${symbol}
158    ) ||
159    (
160      files=`grep -lr "^\(static \)\{0,1\}[^ ${TAB}][ %{TAB}_a-zA-Z0-9]* ${symbol}(" ${DIR} |
161               sed -e '/[.]h$/d' -e 's@^[.]/@@'`
162      if [ X"${file}" != X"${files}" ]; then
163        echo "${ORANGE}WARNING${NORMAL}: ${symbol} is in ${files}" >&2
164        if [ 1 -eq `echo ${files} | wc -w` ]; then
165          file=${files}
166          add_debug_printk
167          exit
168        fi
169      fi
170      if [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then
171        echo "${RED}ERROR${NORMAL}: failed to modify ${file} to instrument" ${call#select} ${symbol} >&2
172      fi
173      false
174    )
175    ret=${?}
176    rm -f ${TMP}/${file##*/}
177    if [ 0 -eq ${ret} ]; then
178      SYMBOLS="${SYMBOLS}${symbol}:${call} "
179    fi
180    return ${ret}
181}
182
183# module_init, module_exit, late_initcall ...
184(
185  grep -Hr '^\(builtin_[a-z]*_driver\|module_[a-z]*_driver\|module_\(init\|exit\)\|[a-z]*_initcall\)(' ${DIR} |
186    sed -n 's@^[.]/@@
187            s/^\([^:]*\):\(builtin_[a-z]*_driver\|module_[a-z]*_driver\|module_init\|module_exit\|[a-z]*_initcall\)(\([^)]*\));$/\1 \2 \3/p' |
188    sort -u
189  find ${DIR} -name '*.c' |
190    while read file; do
191      sed -n "/.* [a-z_0-9]*(void)\$/ {
192                : loop
193                N
194                /^[^\n]* [a-z_0-9]*(void)\n.*\n}\$/ {
195                  /.*platform_\(driver\|device\)_\(un\)\{0,1\}register([&][^)]*).*/ {
196                    s@^[^\n]* \([a-z_0-9]*\)(void)\n.*@${file} select \1@p
197                    b exit
198                  }
199                  /.*[ ${TAB}]_[a-z]_\(un\)\{0,1\}register_driver([&][^, ${TAB})]*).*/ {
200                    s@^[^\n]* \([a-z_0-9]*\)(void)\n.*@${file} select \1@p
201                    b exit
202                  }
203                  /.*i2c_\(add\|del\)_driver([&][^)]*).*/ {
204                    s@^[^\n]* \([a-z_0-9]*\)(void)\n.*@${file} select \1@p
205                  }
206                  b exit
207                }
208                b loop
209              }
210              : exit" ${file}
211    done |
212      sort -u
213) |
214  while read file call symbol; do
215    if ! add_debug_printk && [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then
216      echo FAILED
217    fi
218
219    if [ "${call}" != "${call#builtin_*_driver}" -o "${call}" != "${call#module_*_driver}" ]; then
220      symbol=${symbol}_init
221    fi
222    call=probe
223    sed -n "/\(static \)\{0,1\}.* ${symbol}(/,/^}/ {
224              s/.*platform_\(driver\|device\)_register([&]\([^)]*\)).*/struct platform_\1 \2/p
225              s/.*[ ${TAB}]\(_[a-z]\)_register_driver([&]\([^, ${TAB})]*\)).*/struct \1_driver \2/p
226              s/.*i2c_add_driver([&]\([^)]*\)).*/struct i2c_driver \1/p
227            }
228            s/\(builtin\|module\)_\([a-z]*_driver\)(\([^)]*\));/struct \2 \3/p" ${file} |
229      sort -u |
230      while read struct; do
231        sed -n "/\(static \)\{0,1\}${struct} = {/,/^};/ {
232                  s/^[ ${TAB}]*[.]probe[ ${TAB}]*=[ ${TAB}]*\([^ ${TAB}]*\),\$/\1/p
233                }" ${file} |
234          sort -u |
235          while read symbol; do
236            if ! add_debug_printk && [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call} }" ]; then
237              echo FAILED
238            fi
239          done
240      done
241  done |
242    sort -u |
243    tee ${TMP}/list |
244    grep '^FAILED' >/dev/null
245ret=${?}
246SYMBOLS=`grep -v '^FAILED$' ${TMP}/list`
247
248rm -rf ${TMP}
249
250if [ 0 -ne ${ret} ]; then
251  echo "DONE" >&2
252
253  AUTHOR_NAME="`git config user.name`"
254  AUTHOR_EMAIL="`git config user.email`"
255  if [ -z "${AUTHOR_EMAIL}" ]; then
256    AUTHOR_EMAIL="`${USER}@google.com`"
257  fi
258  if [ -z "${AUTHOR_NAME}" ]; then
259    convert_ldap_to_name() {
260      echo "${1##*/}" |
261        sed "s@[-_]@ @g
262             s@   *@ @g" |
263        sed 's@\(^\| \)a@\1A@g
264             s@\(^\| \)b@\1B@g
265             s@\(^\| \)c@\1C@g
266             s@\(^\| \)d@\1D@g
267             s@\(^\| \)e@\1E@g
268             s@\(^\| \)f@\1F@g
269             s@\(^\| \)g@\1G@g
270             s@\(^\| \)h@\1H@g
271             s@\(^\| \)i@\1I@g
272             s@\(^\| \)j@\1J@g
273             s@\(^\| \)k@\1K@g
274             s@\(^\| \)l@\1L@g
275             s@\(^\| \)m@\1M@g
276             s@\(^\| \)n@\1N@g
277             s@\(^\| \)o@\1O@g
278             s@\(^\| \)p@\1P@g
279             s@\(^\| \)q@\1Q@g
280             s@\(^\| \)r@\1R@g
281             s@\(^\| \)s@\1S@g
282             s@\(^\| \)t@\1T@g
283             s@\(^\| \)u@\1U@g
284             s@\(^\| \|Mc\)v@\1V@g
285             s@\(^\| \)w@\1W@g
286             s@\(^\| \)x@\1X@g
287             s@\(^\| \)y@\1Y@g
288             s@\(^\| \)z@\1Z@g'
289    }
290    AUTHOR_NAME="`convert_ldap_to_name ${USER}`"
291  fi
292
293  git commit -a -m "GKI: DEBUG: add instrumentation to init and probe functions
294
295Automatically generated by ${0}
296
297The following symbols are instrumented:
298`echo \"${SYMBOLS}\" |
299   sed 's/^/ - /'`
300
301Signed-off-by: ${AUTHOR_NAME} <${AUTHOR_EMAIL}>
302Test: compile" ||
303    echo "${RED}ERROR${NORMAL}: failed to commit changes to git" >&2
304  exit
305fi
306echo "${RED}FAILED${NORMAL}: could not complete task" >&2
307exit 1
308