diff options
author | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-02-12 22:22:55 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-02-12 22:22:55 +0000 |
commit | b2106dcb0b3186405174c107e6e8bbf783693be0 (patch) | |
tree | 4e000be0865a5c9c573ed62be838002ad6aec6cb | |
parent | a5006dc4f1dac9fb23e16275db8b9cb08a76da1f (diff) | |
parent | 7c43ab3ab06fe9c9490193062d1241c03a5fcb30 (diff) | |
download | build-b2106dcb0b3186405174c107e6e8bbf783693be0.tar.gz |
Merge "GKI: helpful modularization scripts" am: 5ffa3b2a33 am: f381234993 am: 7c43ab3ab0
Change-Id: Ibc64ca34005681323078380fabacc222ef1cc6e6
-rw-r--r-- | gki/README.md | 132 | ||||
-rwxr-xr-x | gki/add_EXPORT_SYMBOL_GPL | 389 | ||||
-rwxr-xr-x | gki/add_MODULE_LICENSE | 112 | ||||
-rwxr-xr-x | gki/device_snapshot | 675 | ||||
-rwxr-xr-x | gki/find_circular | 170 | ||||
-rwxr-xr-x | gki/instrument_module_init | 307 |
6 files changed, 1785 insertions, 0 deletions
diff --git a/gki/README.md b/gki/README.md new file mode 100644 index 00000000..e848894d --- /dev/null +++ b/gki/README.md @@ -0,0 +1,132 @@ +GKI Tools for Android Kernels +============================= + +Overview +-------- + +This directory contains helpful tools that may be used to aid in the +development of modularized drivers. + +add_EXPORT_SYMBOLS_GPL +---------------------- + + USAGE: add_EXPORT_SYMBOL_GPL [--no-skip-arch] < kernel_build_error_log + add_EXPORT_SYMBOL_GPL [--no-skip-arch] kernel_build_error_log + grep /<module>[.]ko build_error_log | add_EXPORT_SYMBOL_GPL [--no-skip-arch] + vi `add_EXPORT_SYMBOL_GPL [--no-skip-arch] < kernel_build_error_log` + +To acquire the kernel_build_error_log eg: + + $ ./build_sm8250.sh -j50 2>&1 | tee kernel_build_error_log + +To only create commit related to symbols needed for cam_spec.ko module: + + $ grep /cam_spec[.]ko kernel_build_error_log | add_EXPORT_SYMBOL_GPL + +The script will only affect the current directory level and downward, +this allows one to segregate the adjusted content. Any symbols that +are needed outside the range of that directory will result in errors +and the git commit phase will not be performed. + +Add EXPORT_SYMBOL_GPL for any noted missing symbols, output the list of files +modified to stdout (so it can be passed to an editor command line should you +need to check or adjust the results). Automatically commit the list of files +into git. + +Deals as simply as it can to handle \_\_trace\_\<symbols>, sorting the result. + +Keep in mind exports can change, be added or subtracted, and that preliminary +work may expose or remove required symbols to resolve during later work. As +such this script only adds, so you may need to revert the results and try +again to get the most up to date set. By making this part automated it can +deal with the tens or thousands of exports that need to be discovered or +added. If you need to adjust a subsystem, run this script in the subsystem +directory, and it will only adjust from that point downwards leaving other +higher up trees alone. + +add_MODULE_LICENSE +------------------ + + USAGE: add_MODULE_LICENSE < kernel_build_error_log + add_MODULE_LICENSE kernel_build_error_log + +Add MODULE_LICENSE to all the files. + +Must be performed in the root directory. + +find_circular +------------- + + USAGE: find_circular [dir] + +Call this when depmod breaks down, or when one needs a list of the symbols +implicated in the circular dependency. + +Search current or dir directory for all kernel modules. Itemize what they +export, and what they import. Discover links and report who fulfills them. +Report any first order circular relationships and the symbols that got us +into the situation. + +Standard output is of the form: + +module1.ko(symbols) -> module2.ko(symbols) -> module1.ko + +Leaves an annotated modules.dep file in the specified directory. + +device_snapshot +--------------- + + USAGE: device_snapshot [-s <serialno>] [-D] [-f [<input>]] [-F [-o <output> [-d <input>]]] + +Collect filtered /dev and /sys details, along with dmesg and probe list. + +-o \<output> will drop the collection into a set of files, but will not +overrite existing content. -F will overwrite. + +-D will wait for the display + +if \<output> is empty ('' or last option), will not collect dmesg or probe +list. If no -o option is specified, then \<output> will be default of - +(stdout) and all pieces will go to the standard output separated by a cut +and snip header. If specified, \<output> will contain the filtered /dev/ +and /sys/ dumps, \<output>.probed the subset filter of just the probed drivers, +\<output>.dmesg the kernel logs and \<output>.config the uncompressed +/proc/config.gz. + +-d \<input> will take the dropped collection specified to -o \<output> and +produce a diff -U1 output compared against the \<input>. + +-f \<input> allows one to utilize the filter to an existing find /dev /sys +output from a device. No dmesg will be collected. + +-s \<serialno> will allow one to specify a device to connect to when multiples +are available, otherwise will default to one available or ANDROID_SERIAL +environment variable. + +In your local build/flash/boot script for tight development cycles, add + + SEQ=`for i in out/${DEFAULT_BUILD}.snapshot.[0-9]*; do + echo ${i#out/${DEFAULT_BUILD}.snapshot.} + done | + sed 's/^0*//' | + grep -v 0-9 | + tr -d .[:alpha:] | + sort -nu | + tail -1` && + NEWSEQ=$((${SEQ:-0}+1)) && + NEWSEQ=`printf "%03u" ${NEWSEQ}` + if [ -z "${SEQ}" ]; then + private/msm-google/scripts/gki/device_snapshot \ + -o out/${DEFAULT_BUILD}.snapshot.${NEWSEQ} + else + SEQ=`printf "%03u" ${SEQ}` + private/msm-google/scripts/gki/device_snapshot \ + -o out/${DEFAULT_BUILD}.snapshot.${NEWSEQ} \ + -d out/${DEFAULT_BUILD}.snapshot.${SEQ} + fi + +instrument_module_init +---------------------- + USAGE: instrument_module_init [dir|file] + +Add debug instrumentation to module_init and probe functions. diff --git a/gki/add_EXPORT_SYMBOL_GPL b/gki/add_EXPORT_SYMBOL_GPL new file mode 100755 index 00000000..f1d2b84e --- /dev/null +++ b/gki/add_EXPORT_SYMBOL_GPL @@ -0,0 +1,389 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# (c) 2019, Google +progname="${0##*/}" + +# Change to EXPORT_SYMBOL, EXPORT_SYMBOL_GPL or other namespace variant default +EXPORT_SYMBOL=${EXPORT_SYMBOL_GPL:-${progname#add_}} + +USAGE="USAGE: ${progname} [--no-skip-arch] < kernel_build_error_log + ${progname} [--no-skip-arch] kernel_build_error_log + grep /<module>[.]ko build_error_log | ${progname} [--no-skip-arch] + ${EDITOR:-vi} \`${progname} [--no-skip-arch] < kernel_build_error_log\` + +To acquire the kernel_build_error_log eg: +\$ ./build_sm8250.sh -j50 2>&1 | tee kernel_build_error_log + +To only create commit related to symbols needed for cam_spec.ko module: +\$ grep /cam_spec[.]ko kernel_build_error_log | ${progname} + +The script will only affect the current directory level and downward, +this allows one to segregate the adjusted content. Any symbols that +are needed outside the range of that directory will result in errors +and the git commit phase will not be performed. + +Add ${EXPORT_SYMBOL} for any noted missing symbols, output the list of files +modified to stdout (so it can be passed to an editor command line should you +need to check or adjust the results). Automatically commit the list of files +into git. + +Deals as simply as it can to handle __trace_<symbols>, sorting the result. + +Keep in mind exports can change, be added or subtracted, and that preliminary +work may expose or remove required symbols to resolve during later work. As +such this script only adds, so you may need to revert the results and try +again to get the most up to date set. By making this part automated it can +deal with the tens or thousands of exports that need to be discovered or +added. If you need to adjust a subsystem, run this script in the subsystem +directory, and it will only adjust from that point downwards leaving other +higher up trees alone." + +if [ X"--help" = X"${1}" -o X"{-h}" = X"${1}" ]; then + echo "${USAGE}" >&2 + exit +fi +skip_arch=true +if [ X"--no-skip-arch" = X"${1}" ]; then + skip_arch=false + shift +fi +INPUT= +if [ 1 = ${#} ]; then + INPUT=${1} + shift +fi +if [ 0 != ${#} ]; then + echo "Unexpected Argument: ${*}" >&2 + echo >&2 + echo "${USAGE}" >&2 + exit 1 +fi + +# A _real_ embedded tab character +TAB="`echo | tr '\n' '\t'`" +# A _real_ embedded escape character +ESCAPE="`echo | tr '\n' '\033'`" +# Colours +RED="${ESCAPE}[38;5;196m" +BLUE="${ESCAPE}[35m" +NORMAL="${ESCAPE}[0m" + +# Common grep/sed regex expressions +STRUCT_TYPE="struct[ ${TAB}][^ ${TAB}]\{1,\}" +VAR_TYPE="\(${STRUCT_TYPE}\|bool\|char\|int\|long\|long long\|u\{0,1\}int[0-9]*t\)[ ${TAB}]\{1,\}[*]\{0,1\}" +STRUCT_TYPE="${STRUCT_TYPE}[ ${TAB}]\{1,\}[*]\{0,1\}" + +# Files that exports should never exist, or need to be carefully considered +skip_arch_files() { + if ${skip_arch}; then + grep -v '\(^\|^[^:]*/\)arch/[^a][^r][^m][^:]*:' + else + cat - + fi +} + +# Following sucks in all the data from stdin or ${INPUT} +# Check for depmod output (undefined!) or built-in kernel complaints (undefined +# references). The later is not advised because it means the kernel may be +# dependent on a module, we collect it to provide a signal for future work. +echo "Reading symbols" >&2 +MODULES="` + sed -n \ + -e 's/^ERROR: "\([^"]*\)" [[]\(.*[.]ko\)[]] undefined!$/\1 \2/p' \ + -e 's/^[^:]*:[0-9]*: undefined reference to \`\([^'\'']*\)'\''$/\1/p' \ + ${INPUT} | + sort -u`" +SYMBOLS="`echo \"${MODULES}\" | sed 's/ .*$//' | sort -u`" +if [ -z "${SYMBOLS}" ]; then + echo "${BLUE}WARNING${NORMAL}: no symbols found" >&2 + exit +fi + +SYMBOLS_PLUS_TRACE="${SYMBOLS}" +TRACE_MODULES="`echo \"${MODULES}\" | + sed -n 's/__tracepoint_.* //p' | + sort -u`" +TRACE_MODULES_2= +c=`echo "${TRACE_MODULES}" | wc -l` +if [ 1 -eq ${c} ];then + TRACE_MODULES="${TRACE_MODULES%/*.ko}" + if [ -z "${INPUT}" ]; then + TRACE_MODULES_2=* + else + TRACE_MODULES_2=${INPUT} + fi +elif [ -z "${INPUT}" ]; then + TRACE_MODULES=* +else + TRACE_MODULES=${INPUT} +fi +TRACE_FILE="`grep -rl '^#define CREATE_TRACE_POINTS' ${TRACE_MODULES}`" +if [ -z "${TRACE_FILE}" -a -n "${TRACE_MODULES_2}" ]; then + TRACE_FILE="`grep -rl '^#define CREATE_TRACE_POINTS' ${TRACE_MODULES_2}`" +fi +c=0 +if [ -n "${TRACE_FILE}" ]; then + c=`echo "${TRACE_FILE}" | wc -l` +fi +if [ 1 -eq ${c} ]; then + SYMBOLS_PLUS_TRACE="${SYMBOLS} +`echo \"${SYMBOLS}\" | + sed -n 's/^__tracepoint_\(.*\)/EXPORT_TRACEPOINT_SYMBOL_GPL(\1);/p'`" +else + TRACE_FILE= +fi + +echo "Finding symbols in the current tree" >&2 +DATA="`grep -Fr \"${SYMBOLS_PLUS_TRACE}\" * | + grep \"^[^:]*[.][cS]:[^ ${TAB}]\" | + skip_arch_files`" +echo "Editing the files that contain the symbols" >&2 +TMP=`mktemp -d` + +[ 'USAGE: report_strip_of_tag tag + +Report if a problematic function segment tag is stripped from the file + +expects ${F}, ${TMP}, ${TAB}, ${RED} and ${NORMAL} to be defined' ] +report_strip_of_tag() { + if diff --side-by-side --suppress-common-lines ${F} ${TMP}/${F##*/} | + sed -n "s/\(.*[ ${TAB}]\)\(__${1}\)\([^a-zA-Z0-9_].*[^ ${TAB}]\)[ ${TAB}]\{1,\}|[ ${TAB}]\{1,\}/\1${BLUE}\2${NORMAL}\3 -> /p" | + grep __${1} >&2; then + echo "${RED}ERROR${NORMAL}: scrubbing __${1} from function in ${F}" >&2 + echo " Compile check, possibly edit ${F} and friends." >&2 + echo " Then re-run ${progname} to complete." >&2 + echo FAILED + fi +} + +for s in ${SYMBOLS}; do + # Already there? + if echo "${DATA}" | + grep "EXPORT_\(TRACEPOINT_SYMBOL_GPL*(${s#__tracepoint_}\|SYMBOL\(_GPL\)*(${s}\))" >/dev/null; then + echo INFO: ${s} found and already exported >&2 + continue + fi + m="`echo \"${DATA}\" | grep \"^[^:]*:\([^ ${TAB}].*[ *]\|\)${s}(\"`" + c=0 + if [ -n "${m}" ]; then + c=`echo "${m}" | wc -l` + fi + if [ 0 -eq ${c} ]; then + m="`echo \"${DATA}\" | grep \"^[^:]*:\(const[ ${TAB}]\{1,\}\|\)${STRUCT_TYPE}${s}\([[][]]\|\) = {\"`" + c=0 + if [ -n "${m}" ]; then + c=`echo "${m}" | wc -l` + fi + if [ 0 -eq ${c} ]; then + m="`echo \"${DATA}\" | grep \"^[^:]*:\(const[ ${TAB}]\{1,\}\|\)${VAR_TYPE}${s}[\[ ${TAB};=]\"`" + c=0 + if [ -n "${m}" ]; then + c=`echo "${m}" | wc -l` + fi + if [ 0 -eq ${c} ]; then + if [ -z "${TRACE_FILE}" -o "${s}" = "${s#__tracepoint_}" ]; then + echo "${RED}ERROR${NORMAL}: ${s} not found" >&2 + if [ "${s}" != "${s#__tracepoint_}" -o "${s}" = "${s#__}" ]; then + echo FAILED + else + echo "${RED}WARNING${NORMAL}: ${s} might be a linker variable? skipping marking as failure" >&2 + fi + else + echo ${TRACE_FILE} ${s} + fi + elif [ 1 -eq ${c} ]; then + echo "${DATA}" | + sed -n "s/^\([^:]*\):\(const[ $TAB]\{1,\}\|\)${VAR_TYPE}${s}.*/\1 ${s}/p" + else + echo "${RED}ERROR${NORMAL}: ${c} matches: `echo \"${m}\" | + tr '\n' '&' | + sed -e 's/&$//' \ + -e 's/&/ & /g'`" >&2 + echo FAILED + fi + elif [ 1 -eq ${c} ]; then + echo "${DATA}" | + sed -n "s/^\([^:]*\):\(const[ $TAB]\{1,\}\|\)${STRUCT_TYPE}\(${s}\)\([[][]]\|\) = {.*/\1 \3/p" + else + echo "${RED}ERROR${NORMAL}: ${c} matches: `echo \"${m}\" | + tr '\n' '&' | + sed -e 's/&$//' \ + -e 's/&/ & /g'`" >&2 + echo FAILED + fi + elif [ 1 -eq ${c} ]; then + echo "${DATA}" | + sed -n "s/^\([^:]*\):\([^ ${TAB}].*[ *]\|\)\(${s}\)(.*/\1 \3/p" + else + echo "${RED}ERROR${NORMAL}: ${c} matches: `echo \"${m}\" | + tr '\n' '&' | + sed -e 's/&$//' \ + -e 's/&/ & /g'`" >&2 + echo FAILED + fi +done | + sort -u | + while read F s; do + if [ "FAILED" = "${F}" ]; then + echo FAILED + continue + fi + if [ "${s}" != "${s#__tracepoint_}" ]; then + sed "\$ { + /^[^E]/ { + a \ + + } + a \ + newEXPORT_TRACEPOINT_SYMBOL_GPL(${s#__tracepoint_}); + }" ${F} + else + cat ${F} + fi | + sed "/^\([^ ${TAB}].*[ *]\|\)${s}(/ { + : loop1 + N + /\(\n}\);*$/ { + s//\1/ + s/^\([^\n]*[^\n ${TAB}]\|\)[ ${TAB}]\{1,\}__\(init\|exit\)[ ${TAB}]\{1,\}\([^\n]*(\)/\1 \3/ + s/^\([^\n]*[^\n ${TAB}]\|\)[ ${TAB}]\{1,\}__\(init\|exit\)\([^ ${TAB}a-zA-Z0-9_][^\n]*(\)/\1 \3/ + s/^__\(init\|exit\)[ ${TAB}]\{1,\}\([^\n]*(\)/\2/ + s/^__\(init\|exit\)\([^ ${TAB}a-zA-Z0-9_][^\n]*(\)/\2/ + a \ + new${EXPORT_SYMBOL}(${s}); + b next1 + } + b loop1 + : next1 + } + /^\(const[ ${TAB}]\{1,\}\|\)${STRUCT_TYPE}${s}\([[][]]\|\)[ ${TAB}]*=[ ${TAB}]*{/ { + : loop2 + N + /\n};$/ { + a \ + new${EXPORT_SYMBOL}(${s}); + b next2 + } + b loop2 + : next2 + } + /^\(const[ ${TAB}]\{1,\}\|\)${VAR_TYPE}${s}\(\([[][]]\|\)[ ${TAB}]*=.*\|\);/ { + a \ + new${EXPORT_SYMBOL}(${s}); + } + /^\(const[ ${TAB}]\{1,\}\|\)${VAR_TYPE}${s}\([[][]]\|\)[ ${TAB}]*=[ ${TAB}]*{\{0,1\}\$/ { + : loop3 + N + /;$/ { + a \ + new${EXPORT_SYMBOL}(${s}); + b next3 + } + b loop3 + : next3 + }" | + sed '/^newEXPORT/ { + s//EXPORT/ + N + : loop + N + s/\(\n\)\n$/\1/ + t loop + }' >${TMP}/${F##*/} && + ! cmp -s ${F} ${TMP}/${F##*/} && + [ -s ${TMP}/${F##*/} ] && + report_strip_of_tag init && + report_strip_of_tag exit && + cp ${TMP}/${F##*/} ${F} && + echo INFO: export ${s} in ${F} >&2 || + ( + echo "${RED}ERROR${NORMAL}: export ${s} in ${F}" >&2 + echo FAILED + ) + echo ${F} + rm ${TMP}/${F##*/} + done >${TMP}/${progname}.out +FILES="`grep -v '^FAILED$' ${TMP}/${progname}.out | sort -u`" +echo "${FILES}" +grep '^FAILED$' ${TMP}/${progname}.out >/dev/null +ret=${?} +rm -rf ${TMP} +if [ 0 -ne ${ret} ]; then + echo "DONE" >&2 + GIT_FILES="`git diff | + sed -n 's@^diff --git a/\(.*\) b/\1$@\1@p' | + sort -u`" + if [ -z "${GIT_FILES}" ]; then + echo "${BLUE}WARNING${NORMAL}: no changes to commit" >&2 + if [ -n "${FILES}" ]; then + echo " but we altered files!" >&2 + fi + exit + fi + SEMI="" + if [ X"${GIT_FILES}" != X"`echo ${FILES} | tr ' ' '\n' | sort -u`" ]; then + SEMI="Semi-" + FILES=`echo ${FILES} ${GIT_FILES} | + tr ' ' '\n' | + sort -u` + echo "${BLUE}WARNING${NORMAL}: list of files in git different from files we expected to edit, continuation?" >&2 + fi + + AUTHOR_NAME="`git config user.name`" + AUTHOR_EMAIL="`git config user.email`" + if [ -z "${AUTHOR_EMAIL}" ]; then + AUTHOR_EMAIL="`${USER}@google.com`" + fi + if [ -z "${AUTHOR_NAME}" ]; then + convert_ldap_to_name() { + echo "${1##*/}" | + sed "s@[-_]@ @g + s@ *@ @g" | + sed 's@\(^\| \)a@\1A@g + s@\(^\| \)b@\1B@g + s@\(^\| \)c@\1C@g + s@\(^\| \)d@\1D@g + s@\(^\| \)e@\1E@g + s@\(^\| \)f@\1F@g + s@\(^\| \)g@\1G@g + s@\(^\| \)h@\1H@g + s@\(^\| \)i@\1I@g + s@\(^\| \)j@\1J@g + s@\(^\| \)k@\1K@g + s@\(^\| \)l@\1L@g + s@\(^\| \)m@\1M@g + s@\(^\| \)n@\1N@g + s@\(^\| \)o@\1O@g + s@\(^\| \)p@\1P@g + s@\(^\| \)q@\1Q@g + s@\(^\| \)r@\1R@g + s@\(^\| \)s@\1S@g + s@\(^\| \)t@\1T@g + s@\(^\| \)u@\1U@g + s@\(^\| \|Mc\)v@\1V@g + s@\(^\| \)w@\1W@g + s@\(^\| \)x@\1X@g + s@\(^\| \)y@\1Y@g + s@\(^\| \)z@\1Z@g' + } + AUTHOR_NAME="`convert_ldap_to_name ${USER}`" + fi + + git commit ${FILES} -m "GKI: add missing exports for <config>=m for <compatible> + +<edit> + +${SEMI}Automatically generated by ${0##*/} + +The following symbols are exported: +`echo \"${SYMBOLS}\" | + sed 's/^/ - /'` + +Signed-off-by: ${AUTHOR_NAME} <${AUTHOR_EMAIL}> +Test: compile" || + echo "${RED}ERROR${NORMAL}: failed to commit changes to git" >&2 + exit +fi +echo "${RED}FAILED${NORMAL}: could not complete task" >&2 +exit 1 diff --git a/gki/add_MODULE_LICENSE b/gki/add_MODULE_LICENSE new file mode 100755 index 00000000..d76fa85a --- /dev/null +++ b/gki/add_MODULE_LICENSE @@ -0,0 +1,112 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# (c) 2019, Google +progname="${0##*/}" + +USAGE="USAGE: ${progname} < kernel_build_error_log + ${progname} kernel_build_error_log + +Add MODULE_LICENSE to all the files. + +Must be performed in the root directory." + +if [ X"--help" = X"${1}" -o X"{-h}" = X"${1}" ]; then + echo "${USAGE}" >&2 + exit +fi +INPUT= +if [ 1 = ${#} ]; then + INPUT=${1} + shift +fi +if [ 0 != ${#} ]; then + echo "Unexpected Argument: ${*}" >&2 + echo >&2 + echo "${USAGE}" >&2 + exit 1 +fi + +convert_to_module_name() { + echo "${1##*/}" | + sed 's@[-_]@ @g + s@ *@ @g + s@^btfm @BTFM @ + s@^msm @MSM @ + s@ dma\( \|$\)@ DMA\1@g + s@ irq\( \|$\)@ IRQ\1@g + s@ io\( \|$\)@ I/O\1@g + s@ mem\( \|$\)@ memory\1@g + s@ hw\( \|$\)@ hardware\1@g + s@ jpeg\( \|$\)@ JPEG\1@g + s@ mgr$@ manager@ + s@ util$@ utilities@ + s@\(^\| \)a@\1A@g + s@\(^\| \)b@\1B@g + s@\(^\| \)c@\1C@g + s@\(^\| \)d@\1D@g + s@\(^\| \)e@\1E@g + s@\(^\| \)f@\1F@g + s@\(^\| \)g@\1G@g + s@\(^\| \)h@\1H@g + s@\(^\| \)i@\1I@g + s@\(^\| \)j@\1J@g + s@\(^\| \)k@\1K@g + s@\(^\| \)l@\1L@g + s@\(^\| \)m@\1M@g + s@\(^\| \)n@\1N@g + s@\(^\| \)o@\1O@g + s@\(^\| \)p@\1P@g + s@\(^\| \)q@\1Q@g + s@\(^\| \)r@\1R@g + s@\(^\| \)s@\1S@g + s@\(^\| \)t@\1T@g + s@\(^\| \)u@\1U@g + s@\(^\| \)v@\1V@g + s@\(^\| \)w@\1W@g + s@\(^\| \)x@\1X@g + s@\(^\| \)y@\1Y@g + s@\(^\| \)z@\1Z@g' +} + +sed -n \ + 's/WARNING: modpost: missing MODULE_LICENSE() in \(.*\)[.]o$/\1/p' \ + ${INPUT} | + sort -u | + while read f; do + F=${f}.c + if [ ! -f "${F}" ]; then + F=${f%${f##*/}}`echo ${f##*/} | tr -- - _`.c + if [ ! -f "${F}" ]; then + F=${F%.c}_core.c + if [ ! -f "${F}" ]; then + F=${f%${f##*/}}`echo ${f##*/} | tr _ -`.c + if [ ! -f "${F}" ]; then + F=${F%.c}_core.c + if [ ! -f "${F}" ]; then + echo WARNING: ${f}.c not found, needs a MODULE_LICENSE >&2 + continue + fi + fi + fi + fi + fi + if grep MODULE_LICENSE ${F} >/dev/null 2>/dev/null; then + echo INFO: MODULE_LICENSE already present in ${F} >&2 + continue + fi + module="`convert_to_module_name ${f}`" + if [ -z "${module}" ]; then + echo ERROR: ${f} can not be converted into a module name >&2 + continue + fi + sed -i "\${ + a \\ +\\ +MODULE_LICENSE(\"GPL v2\");\\ +MODULE_DESCRIPTION(\"${module}\"); + }" ${F} && + echo INFO: Added MODULE_LICENSE to ${F} >&2 || + echo ERROR: Failed to add MODULE_LICENSE to ${F} >&2 + done + diff --git a/gki/device_snapshot b/gki/device_snapshot new file mode 100755 index 00000000..089e5d9d --- /dev/null +++ b/gki/device_snapshot @@ -0,0 +1,675 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# (c) 2019, Google + +progname=${0##*/} +NUM=XX +USAGE="USAGE: ${progname} [-s <serialno>] [-D] [-f [<input>]] [-F [-o <output> [-d <input>]]] + +Collect filtered /dev and /sys details, along with dmesg and probe list. + +-o <output> will drop the collection into a set of files, but will not +overwrite existing content. -F will overwrite. + +-D will wait for the display. + +if <output> is empty ('' or last option), will not collect dmesg or probe +list. If no -o option is specified, then <output> will be default of - +(stdout) and all pieces will go to the standard output separated by a cut +and snip header. If specified, <output> will contain the filtered /dev/ +and /sys/ dumps, <output>.probed the subset filter of just the probed drivers, +<output>.dmesg the kernel logs and <output>.config the uncompressed +/proc/config.gz. + +-d <input> will take the dropped collection specified to -o <output> and +produce a diff -U1 output compared against the <input>. + +-f <input> allows one to utilize the filter to an existing find /dev /sys +output from a device. No dmesg will be collected. + +-s <serialno> will allow one to specify a device to connect to when multiples +are available, otherwise will default to one available or ANDROID_SERIAL +environment variable. + +NB: The /dev/ and /sys/ dumps can be compared with something like a diff -U1 + and report a generally noiseless result so it is easy to spot the side + effects from one build to the next. Basically nothing else should be + added or removed from the differences other than the expectations, save + for normal noise from the dynamic nature of the kernel, thus making it + beneficial to run this script collection at the same point in the boot + process to keep noise down. + +NB: for default standard output, the probed list will be post-filter so + some details may be altered to make it easier to discover differences. + So if bus enumerations change, some of the enumerators will be replaced + with an '${NUM}' so that they continue to match when moved. For a + specified output, the <output>.probed file will retain the details." + +EMPTY="" +SPACE=" " +# A _real_ embedded tab character +TAB="`echo | tr '\n' '\t'`" +# A _real_ embedded carriage return character +CR="`echo | tr '\n' '\r'`" +# A _real_ embedded escape character +ESCAPE="`echo | tr '\n' '\033'`" +# Colours +RED="${ESCAPE}[38;5;196m" +ORANGE="${ESCAPE}[38;5;255:165:0m" +BLUE="${ESCAPE}[35m" +NORMAL="${ESCAPE}[0m" + +## +## Helper Functions +## + +[ "USAGE: inFastboot + +Returns: true if device is in fastboot mode" ] +inFastboot() { + fastboot devices | + if [ -n "${ANDROID_SERIAL}" ]; then + grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null + else + wc -l | grep '^1$' >/dev/null + fi +} + +[ "USAGE: inAdb + +Returns: true if device is in adb mode" ] +inAdb() { + adb devices </dev/null | + grep -v -e 'List of devices attached' -e '^$' -e "[${SPACE}${TAB}]recovery\$" | + if [ -n "${ANDROID_SERIAL}" ]; then + grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null + else + wc -l | grep '^1$' >/dev/null + fi +} + +[ "USAGE: inRecovery + +Returns: true if device is in recovery mode" ] +inRecovery() { + local list="`adb devices | + grep -v -e 'List of devices attached' -e '^$'`" + if [ -n "${ANDROID_SERIAL}" ]; then + echo "${list}" | + grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null + return ${?} + fi + if echo "${list}" | wc -l | grep '^1$' >/dev/null; then + echo "${list}" | + grep "[${SPACE}${TAB}]recovery\$" >/dev/null + return ${?} + fi + false +} + +[ "USAGE: USB_DEVICE=\`usb_devnum [--next]\` + +USB_DEVICE contains cache. Update if system changes. + +Returns: the devnum for the USB_SERIAL device" ] +usb_devnum() { + if [ -n "${USB_SERIAL}" ]; then + local usb_device=`cat ${USB_SERIAL%/serial}/devnum 2>/dev/null | tr -d ' \t\r\n'` + if [ -n "${usb_device}" ]; then + USB_DEVICE=dev${usb_device} + elif [ -n "${USB_DEVICE}" -a "${1}" ]; then + USB_DEVICE=dev`expr ${USB_DEVICE#dev} + 1` + fi + echo "${USB_DEVICE}" + fi +} + +[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr + +Returns: true if the command succeeded" ] +adb_sh() { + local args= + for i in "${@}"; do + [ -z "${args}" ] || args="${args} " + if [ X"${i}" != X"${i#\'}" ]; then + args="${args}${i}" + elif [ X"${i}" != X"${i#*\\}" ]; then + args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`" + elif [ X"${i}" != X"${i#* }" ]; then + args="${args}'${i}'" + elif [ X"${i}" != X"${i#*${TAB}}" ]; then + args="${args}'${i}'" + else + args="${args}${i}" + fi + done + adb shell "${args}" +} + +[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr + +Returns: true if the command running as root succeeded" ] +adb_su() { + adb_sh su root "${@}" +} + +[ "USAGE: get_property <prop> + +Returns the property value" ] +get_property() { + adb_sh getprop ${1} 2>/dev/null </dev/null +} + +[ "USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d] + +human readable output whole seconds, whole minutes or mm:ss" ] +format_duration() { + if [ -z "${1}" ]; then + echo unknown + return + fi + local duration="${1}" + if [ X"${duration}" != X"${duration%s}" ]; then + duration=${duration%s} + elif [ X"${duration}" != X"${duration%m}" ]; then + duration=`expr ${duration%m} \* 60` + elif [ X"${duration}" != X"${duration%h}" ]; then + duration=`expr ${duration%h} \* 3600` + elif [ X"${duration}" != X"${duration%d}" ]; then + duration=`expr ${duration%d} \* 86400` + fi + local seconds=`expr ${duration} % 60` + local minutes=`expr \( ${duration} / 60 \) % 60` + local hours=`expr ${duration} / 3600` + if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then + if [ 1 -eq ${duration} ]; then + echo 1 second + return + fi + echo ${duration} seconds + return + elif [ 60 -eq ${duration} ]; then + echo 1 minute + return + elif [ 0 -eq ${seconds} -a 0 -eq ${hours} ]; then + echo ${minutes} minutes + return + fi + if [ 0 -eq ${hours} ]; then + echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10` + return + fi + echo ${hours}:`expr ${minutes} / 10``expr ${minutes} % 10`:`expr ${seconds} / 10``expr ${seconds} % 10` +} + +[ "USAGE: adb_wait [timeout] + +Returns: waits until the device has returned for adb or optional timeout" ] +adb_wait() { + local start=`date +%s` + local duration= + local ret + if [ -n "${1}" ]; then + USB_DEVICE=`usb_devnum --next` + duration=`format_duration ${1}` + echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}" + timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null + ret=${?} + echo -n " ${CR}" + else + adb wait-for-device + ret=${?} + fi + USB_DEVICE=`usb_devnum` + if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then + local active_slot=`get_active_slot` + if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then + echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2 + fi + fi + local end=`date +%s` + local diff_time=`expr ${end} - ${start}` + local _print_time=${print_time} + if [ ${diff_time} -lt 15 ]; then + _print_time=false + fi + diff_time=`format_duration ${diff_time}` + if [ "${diff_time}" = "${duration}" ]; then + _print_time=false + fi + + local reason= + if inAdb; then + reason=`get_property ro.boot.bootreason` + fi + case ${reason} in + reboot*) + reason= + ;; + ${EMPTY}) + ;; + *) + reason=" for boot reason ${reason}" + ;; + esac + if ${_print_time} || [ -n "${reason}" ]; then + echo "${BLUE}[ INFO ]${NORMAL} adb wait duration ${diff_time}${reason}" + fi >&2 + + return ${ret} +} + +[ "USAGE: fastboot_wait [timeout] + +Returns: waits until the device has returned for fastboot or optional timeout" ] +fastboot_wait() { + local ret + # fastboot has no wait-for-device, but it does an automatic + # wait and requires (even a nonsensical) command to do so. + if [ -n "${1}" ]; then + USB_DEVICE=`usb_devnum --next` + echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}" + timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null + ret=${?} + echo -n " ${CR}" + ( exit ${ret} ) + else + fastboot wait-for-device >/dev/null 2>/dev/null + fi || + inFastboot + ret=${?} + USB_DEVICE=`usb_devnum` + if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then + local active_slot=`get_active_slot` + if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then + echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" + fi >&2 + fi + return ${ret} +} + +[ "USAGE: recovery_wait [timeout] + +Returns: waits until the device has returned for recovery or optional timeout" ] +recovery_wait() { + local ret + if [ -n "${1}" ]; then + USB_DEVICE=`usb_devnum --next` + echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}" + timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null + ret=${?} + echo -n " ${CR}" + else + adb wait-for-recovery + ret=${?} + fi + USB_DEVICE=`usb_devnum` + if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then + local active_slot=`get_active_slot` + if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then + echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" + fi >&2 + fi + return ${ret} +} + +[ "any_wait [timeout] + +Returns: waits until a device has returned or optional timeout" ] +any_wait() { + ( + adb_wait ${1} & + adb_pid=${!} + fastboot_wait ${1} & + fastboot_pid=${!} + recovery_wait ${1} & + recovery_pid=${!} + wait -n + kill "${adb_pid}" "${fastboot_pid}" "${recovery_pid}" + ) >/dev/null 2>/dev/null + inFastboot || inAdb || inRecovery +} + +## +## RUNTIME +## + +filter= +output=- +input= +display=false +force=false +while [ ${#} -gt 0 ]; do + case ${1} in + -f=* | --filter=*) + filter="${1#-f=}" + filter="${filter#--filter=}" + ;; + -f | --filter) + if [ ${#} = 1 -o -z "${2}" ]; then + filter=- + else + shift + filter="${1}" + fi + ;; + -F | --force) + force=true + ;; + -h | -\? | --help) + echo "${USAGE}" + exit 0 + ;; + -o=* | --output=*) + output="${1#-o=}" + output="${output#--output=}" + ;; + -o | --output) + if [ ${#} = 1 -o -z "${2}" ]; then + # empty output results in no dmesg or probed collection + output= + else + shift + output="${1}" + fi + ;; + -d=* | --diff=*) + input="${1#-d=}" + input="${input#--diff=}" + ;; + -d | --diff) + shift + input="${1}" + ;; + -D | --wait-for-display) + display=true + ;; + -s=* | --serial=*) + ANDROID_SERIAL="${1#-s=}" + export ANDROID_SERIAL="${ANDROID_SERIAL#--serial=}" + ;; + -s | --serial) + shift + export ANDROID_SERIAL="${1}" + ;; + *) + echo "${RED}[ ERROR ]${NORMAL} unknown parm ${1}" >&2 + echo "${USAGE}" >&2 + exit 1 + ;; + esac + shift +done + +if [ -n "${input}" -a -z "${output#-}" ]; then + echo "${RED}[ ERROR ]${NORMAL} must specify an -o <output> along with the -d ${input}" >&2 + echo "${USAGE}" >&2 + exit 1 +fi + +USB_SERIAL= +[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial | + grep usb | + xargs grep -l ${ANDROID_SERIAL}` +USB_ADDRESS= +if [ -n "${USB_SERIAL}" ]; then + USB_ADDRESS=${USB_SERIAL%/serial} + USB_ADDRESS=usb${USB_ADDRESS##*/} +fi + +[ "USAGE: input= BLUE= output= NORMAL= perform_diff < ${output} + +Reports the differences between stdin and \${input}" ] +perform_diff() { + diff -U1 "${input}" - | + grep '[-+ ]' || + echo "${BLUE}[ INFO ]${NORMAL} no differences in /dev/ and /sys/ since last device snapshot" >&2 + if [ -s "${output}.probed" -a -s "${input}.probed" ]; then + diff -U1 "${input}.probed" "${output}.probed" | + grep '[-+ ]' || + echo "${BLUE}[ INFO ]${NORMAL} no differences in probed devices and drivers" >&2 + fi + if [ -s "${output}.dmesg" -a -s "${input}.dmesg" ]; then + TMP=`mktemp -d` + mknod ${TMP}/input p + sed -n 's@^.*init: Loaded kernel module /lib/modules/@@p' ${input}.dmesg > ${TMP}/input & + sed -n 's@^.*init: Loaded kernel module /lib/modules/@@p' ${output}.dmesg | + diff -U1 ${TMP}/input - | + grep '[-+ ]' || + echo "${BLUE}[ INFO ]${NORMAL} no differences in module load order" >&2 + wait + rm -rf ${TMP} + fi + if [ -s "${output}.config" -a -s "${input}.config" ]; then + diff -U1 "${input}.config" "${output}.config" | + grep '[-+ ]' || + echo "${BLUE}[ INFO ]${NORMAL} no differences in config" >&2 + fi + if [ -s "${output}.getprop" -a -s "${input}.getprop" ]; then + TMP=`mktemp -d` + sed -n 's/[[]ro[.]boottime[.]\([^]]*\)[]]: [[]\([0-9]*\)[]]$/\1 \2/p' "${input}.getprop" >${TMP}/old.getprop + sed -n 's/[[]ro[.]boottime[.]\([^]]*\)[]]: [[]\([0-9]*\)[]]$/\2 \1/p' "${output}.getprop" | + sort -un | + while read monotonic action; do + [ -n "${action}" ] || continue + [ -n "${monotonic}" ] || continue + old=`sed -n "s/^${action} //p" ${TMP}/old.getprop` + [ -n "${old}" ] || continue + old="$((${monotonic} - ${old}))" + [ -n "${old}" -a 0 != "${old}" ] || continue + sign=+ + if [ -z "${old##-*}" ]; then + sign=- + old=${old#-} + fi + printf "${action}\t${sign}%u.%09us\n" $((${old} / 1000000000)) $((${old} % 1000000000)) + done | + grep '[0-9][.][0-9]' || + echo "${BLUE}[ INFO ]${NORMAL} no differences in boottime" >&2 + rm -rf ${TMP} + fi +} + +if [ -n "${output#-}" -a -s "${output}" ]; then + if ! ${force}; then + if [ -n "${input}" ]; then + perform_diff <${output} + exit 0 + fi + echo "${RED}[ ERROR ]${NORMAL} will not overwrite existing content" >&2 + exit 1 + fi +fi + +if [ -z "${filter}" ]; then + if inFastboot; then + echo "${RED}[ ERROR ]${NORMAL} in fastboot mode" >&2 + exit 1 + fi + if inRecovery; then + echo "${RED}[ ERROR ]${NORMAL} in recovery mode" >&2 + exit 1 + fi + if ! inAdb; then + any_wait 2m + if ! inAdb; then + if [ -n "${ANDROID_SERIAL}" ]; then + echo "${RED}[ ERROR ]${NORMAL} not in adb mode" >&2 + exit 1 + fi + COUNT=`adb devices </dev/null | + grep -v -e 'List of devices attached' -e '^$' | + wc -l` + echo "${RED}[ ERROR ]${NORMAL} ${COUNT} devices attached" >&2 + exit 1 + fi + fi + counter=0 + while ${display}; do + if inFastboot; then + fastboot reboot + elif inAdb; then + if [ "1" = "`get_property sys.boot_completed`" ]; then + break + fi + fi + counter=$((${counter} + 1)) + if [ ${counter} -gt 120 ]; then + break + fi + sleep 1 + done +fi + +if [ -n "${filter}" ]; then + cat ${filter} +else + adb_su find /dev /sys </dev/null 2>/dev/null | + if [ -n "${output}" -a X"${output}" != X"-" ]; then + # Generate ${output}.probed: + # + # Report all the instantiated devices and which driver is responsible. + # If drivers do not have any instantiated devices, report them at end. + # Drivers are of a form driver:<subsystem>:<name>, if <subsystem> is + # empty, it is short hand for platform. + # + DRIVERS= + FOUND= + ( + tee /proc/self/fd/99 | + sed -n \ + -e 's@/sys/devices/platform/.*/driver$@&@p' \ + -e 's@/sys/bus/\([^/]*\)/drivers/\([^/]*\)$@driver:\1:\2@p' | + sort -u + echo DONE + ) | + while read driver; do + [ -n "${driver}" ] || continue + case ${driver} in + DONE) + [ -n "${DRIVERS}" ] || break + if [ -z "${FOUND}" ]; then + echo "${DRIVERS}" + else + echo "${DRIVERS}" | + grep -ve "${FOUND}\)\$" || + true + fi | + sed 's/driver:platform:/driver::/' + break + ;; + driver:*) + [ -z "${DRIVERS}" ] || DRIVERS="${DRIVERS} +" + DRIVERS="${DRIVERS}${driver}" + ;; + *) + INSTANCE="${driver#/sys/devices/platform/}" + INSTANCE="${INSTANCE%/driver}" + DRIVER="`adb_su ls -l ${driver} </dev/null 2>/dev/null | + sed -n 's@.* -> .*/bus/\([^/]*\)/drivers/\(.*\)@driver:\1:\2@p'`" + if [ -z "${DRIVER}" ]; then + echo "${INSTANCE}" + else + if [ -z "${FOUND}" ]; then + FOUND="^\(${DRIVER}" + else + FOUND="${FOUND}\|${DRIVER}" + fi + DRIVER="${DRIVER#driver:}" + echo "${INSTANCE} -> driver:${DRIVER#platform}" + fi + ;; + esac + done >"${output}.probed" + [ -s "${output}.probed" ] || rm -f "${output}.probed" + # Generate ${output}.dmesg + adb_su dmesg </dev/null 2>/dev/null >"${output}.dmesg" + [ -s "${output}.dmesg" ] || rm -f "${output}.dmesg" + # Generate ${output}.config if kernel provides it. + adb_su cat /proc/config.gz </dev/null 2>/dev/null | + gunzip >"${output}.config" + [ -s "${output}.config" ] || rm -f "${output}.config" + # Generate ${output}.getprop + adb $QD shell su root getprop </dev/null 2>/dev/null | + sort -u >"${output}.getprop" + [ -s "${output}.getprop" ] || rm -f "${output}.getprop" + else + cat - >&99 + fi 99>&1 +fi | + grep -v '^\(/dev/ashmem\|/dev/block/mapper/by-uuid/\|/dev/fscklogs/log\)' | + grep -v '^\(/dev/socket/location/\(socket_hal\|cld80211\)\|/sys/class/hwmon/\)' | + grep -v '^/dev/socket/netmgr/netmgr_connect_socket$' | + grep -v '^/dev/socket/qdma/qdma-\(file\|campmgr\)-s$' | + grep -v '^/dev/socket/qmux_radio/' | + grep -v '^/dev/socket/vendor_wpa_wlan' | + grep -v '^/dev/socket/wifihal/wifihal_ctrlsock' | + grep -v '^/sys/dev/char/[0-9]*:[0-9]*$' | + grep -v '^/sys/devices/virtual/thermal/tz-by-name/wsatz[.][0-9]*$' | + grep -v '^/sys/kernel/debug/tracing/instances/wifi' | + grep -v '^/sys/kernel/debug/\(wlan\|WMI\|p2p\|msm-bus-dbg/client-data/npu_bwmon_cdsp\)' | + grep -v '^/sys/class/net/\(wlan\|p2p\)' | + grep -v '^/sys/class/ieee80211/phy' | + grep -v '^/sys/devices/platform/soc/[0-9a-f]*.qcom,icnss/\(net\|firmware\|ieee80211\)' | + grep -v '^/sys/devices/platform/soc/[0-9a-f]*.*@[0-9][0-9]*-cam_\(vana\|vdig\|vio\|clk\)$' | + grep -v '^/sys/kernel/wlan/' | + grep -v '^/sys/kernel/slab/\(f2fs_fsync_inode_entry\|zs\|:[0-9a-f]\{,7\}\)' | + grep -v '^/sys/devices/system/cpu/cpufreq/policy[0-9]*/schedutil' | + grep -v '/__trace_printk_fmt$' | + sed "s@\(/regulator[.]\)[0-9][0-9]*\$@\1${NUM}@ + s@\(/regulator[.]\)[0-9][0-9]*/@\1${NUM}/@g + s@\(/vppservice_\)[0-9][0-9]*\$@\1${NUM}@ + s@\(/msi_irqs/\)[0-9][0-9]*\$@\1${NUM}@ + s@\(/sys/class/rfkill/rfkill\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/class\|/sys/devices/platform/soc/[0-9a-f]*[.]qcom,gpi-dma\)\(/dma/dma\)[0-9]*\(chan[0-9]*\(\|/.*\)\)\$@\1\2${NUM}\3@ + s@\(/i2c-\)\([0-9]\)\(/[^/]*/i2c-\)\2\(\|/.*\)\$@\1${NUM}\3${NUM}\4@ + s@\(/i2c-\)[0-9]\(\|/[^/]*\)\$@\1${NUM}\2@ + s@-\([0-9]\)/\1\(-[0-9][0-9][0-9][0-9]/\)@-${NUM}/${NUM}\2@g + s@/[0-9]\(-[0-9][0-9][0-9][0-9]\(\|-vdd\|-vdd/.*\|-avdd\|-avdd/.*\|-vio\|-vio/.*\|/.*\)\)\$@${NUM}\1@ + s@\(/sys/devices/platform/soc/[0-9a-z]*[.]qcom,[^/]*/subsys\)[0-9][0-9]*@\1${NUM}@ + s@\(rpmsg_chrdev[.]0[.]0/rpmsg/rpmsg_ctrl\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/devices/virtual/kgsl/kgsl/pagetables/\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/devices/virtual/kgsl/kgsl/proc/\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/devices/virtual/thermal/thermal_zone\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/devices/virtual/net/\)\(r_\|\)\(rmnet_data\)[0-9][0-9]*\(/queues/tx-\)[0-9][0-9]*@\1\3${NUM}\4${NUM}@ + s@\(/sys/devices/platform/soc/soc:qcom,msm-audio-apr/soc:qcom,msm-audio-apr:qcom,q6core-audio/soc:qcom,msm-audio-apr:qcom,q6core-audio:lpi_pinctrl[@][0-9a-f]*/gpiochip\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/kernel/debug/adsprpc/\(adsp\|cdsp\|ssc\)rpcd_\)[0-9][0-9]*\$@\1${NUM}@ + s@\(/sys/kernel/debug/binder/proc/\)[0-9][0-9]*\$@\1${NUM}@ + s@\(/sys/kernel/debug/kgsl/proc/\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/kernel/debug/kgsl/kgsl-[0-9a-f][0-9a-f]*/ctx/\)[0-9][0-9]*@\1${NUM}@ + s@\(/sys/kernel/debug/msm_cvp/core0/inst_\)[0-9a-f][0-9a-f]*@\1${NUM}@ + s@\(/sys/kernel/iommu_groups/\)[0-9][0-9]*@\1${NUM}@ + s@\(/devices/soc:qcom,msm_fastrpc:qcom,msm_fastrpc_compute_cb\)[0-9][0-9]*@\1${NUM}@ + s@/mmc[0-9][0-9]*\($\|/\|:\)@/mmc${NUM}\1@ + s@\(/sys/kernel/irq/\)[0-9][0-9]*@\1${NUM}@" | + if [ -n "${output}" -a X"${output}" != X"-" ]; then + sort -u | + if [ -n "${input}" ]; then + tee "${output}" | perform_diff + else + cat - >"${output}" + fi + else + TMP=`mktemp -d` + if [ X"${output}" = X"-" ]; then + echo "------------------------> cut dump < ------------------------" + sort -u | + tee /proc/self/fd/99 | + sed -n 's@/sys/devices/platform/\(.*\)/driver$@\1@p' >${TMP}/probed + else + sort -u >&99 + fi 99>&1 + if [ -s "${TMP}/probed" ]; then + echo "-----------------------> cut probed <------------------------" + cat ${TMP}/probed + fi + rm -rf ${TMP} + fi +if [ -z "${filter}" -a X"${output}" = X"-" ]; then + echo "------------------------> cut dmesg <------------------------" + adb_su dmesg </dev/null 2>/dev/null + echo "-----------------------> cut .config <-----------------------" + adb_su cat /proc/config.gz </dev/null 2>/dev/null | gunzip +fi diff --git a/gki/find_circular b/gki/find_circular new file mode 100755 index 00000000..e0b76bc2 --- /dev/null +++ b/gki/find_circular @@ -0,0 +1,170 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# (c) 2019, Google +progname="${0##*/}" + +USAGE="USAGE: ${progname} [dir] + +Call this when depmod breaks down, or when one needs a list of the symbols +implicated in the circular dependency. + +Search current or dir directory for all kernel modules. Itemize what they +export, and what they import. Discover links and report who fulfills them. +Report any first order circular relationships and the symbols that got us +into the situation. + +Standard output is of the form: + +module1.ko(symbols) -> module2.ko(symbols) -> module1.ko + +Leaves an annotated modules.dep file in the specified directory. + +TBA: higher order circular dependencies" + +if [ X"--help" = X"${1}" -o X"{-h}" = X"${1}" ]; then + echo "${USAGE}" >&2 + exit +fi +DIR=. +if [ 1 = ${#} ]; then + DIR=${1} + shift +fi +if [ 0 != ${#} ]; then + echo "Unexpected Argument: ${*}" >&2 + echo >&2 + echo "${USAGE}" >&2 + exit 1 +fi + +# A _real_ embedded tab character +TAB="`echo | tr '\n' '\t'`" + +# +# Start Work +# + +# Construct our version (symbols annoted as comments) of the modules.dep file. + +TMP=`mktemp -d` + +# Acquire a list of files that are modules +find ${DIR%/} -name '*.ko' >${TMP}/modules + +# Fill symbols with file type symbol +# type is > for requires symbol +# type is < for supplies symbol +# type is pre for requires module (in symbol field) +# type is post for supplies module (in file field) + +# Mine for symbols +xargs nm -o <${TMP}/modules | + sed "s@^${DIR%/}/@@" | + sed -n \ + -e 's/^\([^:]*\):.* U \(.*\)/\1 > \2/p' \ + -e 's/^\([^:]*\):.* [TD] \(.*\)/\1 < \2/p' >${TMP}/symbols +if [ ! -s ${TMP}/symbols ]; then + rm -rf ${TMP} + echo ERROR: failed to find any .ko module files in the specified directory ${DIR%/} + exit 1 +fi + +# Mine for softdep pre + while read file; do + strings ${file} | + sed -n 's/^softdep=pre: \(.*\)/\1/p' | + tr ' ' '\n' | + sed "s@.*@${file#${DIR%/}/} pre &@" + done < ${TMP}/modules | + sort -u >>${TMP}/symbols + +# Mine for softdep post + while read file; do + strings ${file} | + sed -n 's/^softdep=post: \(.*\)/\1/p' | + tr ' ' '\n' | + sed "s@.*@$& post {file#${DIR%/}/}@" + done < ${TMP}/modules | + sort -u >>${TMP}/symbols + +# Translate symbols to annotated modules.dep +( + sed -n 's/ > / /p' ${TMP}/symbols | + while read file symbol ; do + sed -n "s@^\(.*\) < ${symbol}\$@${file}: \1 # ${symbol}@p" ${TMP}/symbols + done + sed -n 's/ pre / /p' ${TMP}/symbols | + while read file softdep ; do + grep "/${softdep}[.]ko\$" ${TMP}/modules | + sed "s@^${DIR%/}/@@" | + while read source; do + echo "${file}: ${source} # pre:${softdep}" + done + done + sed -n 's/ post / /p' ${TMP}/symbols | + while read softdep source ; do + grep "/${softdep}[.]ko\$" ${TMP}/modules | + sed "s@^${DIR%/}/@@" | + while read file; do + echo "${file}: ${source} # post:${softdep}" + done + done +) | + sort -u >${DIR%/}/modules.dep + +# Cleanup (already!) +rm -rf ${TMP} + +# Evaluate first order dependencies from our annotated ${DIR}/modules.dep file. + +# squash adjacent lines with match for first symbols, merge second +merge_second_symbols() { + sed ': loop + N + s/^\(.*(.*) -> .*(\)\(.*\)\().*\)\n\1\(.*\)\3/\1\2,\4\3/ + t loop + h + s/\n.*//p + g + s/^[^\n]*\n// + t loop' +} + +# squash adjacent lines with match for second symbols, merge first +merge_first_symbols() { + sed ': loop + N + s/^\(.*(\)\(.*\)\() -> .*(.*).*\)\n\1\(.*\)\3/\1\2,\4\3/ + t loop + h + s/\n.*//p + g + s/^[^\n]*\n// + t loop' +} + +# squash adjacent lines with identical, but shifted one, circular dependencies +merge_duplicates() { + sed ': loop + N + s/^\(.*\)\((.*)\) -> \(.*\)\((.*)\) -> \1\n\3\4 -> \1\2 -> \3$/\1\2 -> \3\4 -> \1/ + t end + s/\n.*//p + g + s/^[^\n]*\n// + t loop + : end' +} + +# Report the first order circular dependencies +sed 's/://' ${DIR%/}/modules.dep | + while read file source comment symbol; do + sed -n "s@^${source}: ${file} # \(.*\)@${file}(${symbol}) -> ${source}(\1) -> ${file}@p" ${DIR%/}/modules.dep + done | + merge_second_symbols | + merge_first_symbols | + merge_duplicates | + sort -u + +# TBA: second order dependencies diff --git a/gki/instrument_module_init b/gki/instrument_module_init new file mode 100755 index 00000000..4f3c886d --- /dev/null +++ b/gki/instrument_module_init @@ -0,0 +1,307 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# (c) 2019, Google +progname="${0##*/}" + +# Add instrumentation to module_init and probe functions. + +USAGE="USAGE: ${progname} [dir|file] + +Add debug instrumentation to module_init and probe functions." + +if [ X"--help" = X"${1}" -o X"{-h}" = X"${1}" ]; then + echo "${USAGE}" >&2 + exit +fi +DIR=. +if [ 1 = ${#} ]; then + DIR=${1} + shift +fi +if [ 0 != ${#} ]; then + echo "Unexpected Argument: ${*}" >&2 + echo >&2 + echo "${USAGE}" >&2 + exit 1 +fi + +# A _real_ embedded tab character +TAB="`echo | tr '\n' '\t'`" +# A _real_ embedded escape character +ESCAPE="`echo | tr '\n' '\033'`" +# Colours +RED="${ESCAPE}[38;5;196m" +ORANGE="${ESCAPE}[38;5;255:165:0m" +BLUE="${ESCAPE}[35m" +NORMAL="${ESCAPE}[0m" + +TMP=`mktemp -d` + +# ${file} ${call} and ${symbol} input +SYMBOLS=" " +add_debug_printk() +{ + message="printk(KERN_WARNING \"${file##*/}:" + if [ -z "${call#select}" ]; then + # We need uniqueness for discovery, so instead of truth of __func__ to + # cover any of our mistakes(!), we select the expectation. + message+="${symbol}: ENTER\\\\n\");" + else + message+="%s:${call}: ENTER\\\\n\", __func__);" + fi + if grep "${TAB}${message%%\\*}" ${file} >/dev/null; then + if [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then + echo INFO: already ${file} instrumented ${call#select} ${symbol} >&2 + SYMBOLS="${SYMBOLS}${symbol}:${call} " + fi + return + fi + if [ "device_initcall" = "${call}" -o "module_init" = "${call}" -o "module_exit" = "${call}" ] && + grep "${TAB}printk(KERN_WARNING \"${file##*/}:%s:\(builtin\|module\)_[a-z]*_driver: ENTER" ${file} >/dev/null; then + if [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then + echo INFO: maybe already ${file} instrumented ${call#select} ${symbol} >&2 + fi + return + fi + sed "/^\(static \)\{0,1\}.* ${symbol}(/ { + N + s/^\(static \)\{0,1\}\(.* ${symbol}(\)\(void\)\{0,1\}) {[ ${TAB}]*}\(\n\)/\1\2\3)\4{\4${TAB}${message}\4}\4/ + t exit + N + /\/[*][^*]*\$/ { + : comment_loop + N + /\/[*].*[*]\// b comment_done + b comment_loop + : comment_done + /\/[*].*[*]\/\$/ N + } + /printk(KERN_WARNING \"${file##*/}:/ b exit + 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}&/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + 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}&/ + t exit + s/\(static void .*\)\(\n${TAB}\)\([a-z].*\)\2}\$/\1\2${message}\2\3\2}/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + t exit + N + /printk(KERN_WARNING \"${file##*/}:/ b exit + s/\(.*\)\(\n${TAB}\)\(.*\n\)\2/&${message}\2/ + : exit + } + 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);/ + 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);/ + " ${file} > ${TMP}/${file##*/} && + ! cmp -s ${file} ${TMP}/${file##*/} && + cp ${TMP}/${file##*/} ${file} && + ( + echo INFO: modified ${file} to instrument ${call#select} ${symbol} >&2 + echo ${symbol} + ) || + ( + files=`grep -lr "^\(static \)\{0,1\}[^ ${TAB}][ %{TAB}_a-zA-Z0-9]* ${symbol}(" ${DIR} | + sed -e '/[.]h$/d' -e 's@^[.]/@@'` + if [ X"${file}" != X"${files}" ]; then + echo "${ORANGE}WARNING${NORMAL}: ${symbol} is in ${files}" >&2 + if [ 1 -eq `echo ${files} | wc -w` ]; then + file=${files} + add_debug_printk + exit + fi + fi + if [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then + echo "${RED}ERROR${NORMAL}: failed to modify ${file} to instrument" ${call#select} ${symbol} >&2 + fi + false + ) + ret=${?} + rm -f ${TMP}/${file##*/} + if [ 0 -eq ${ret} ]; then + SYMBOLS="${SYMBOLS}${symbol}:${call} " + fi + return ${ret} +} + +# module_init, module_exit, late_initcall ... +( + grep -Hr '^\(builtin_[a-z]*_driver\|module_[a-z]*_driver\|module_\(init\|exit\)\|[a-z]*_initcall\)(' ${DIR} | + sed -n 's@^[.]/@@ + s/^\([^:]*\):\(builtin_[a-z]*_driver\|module_[a-z]*_driver\|module_init\|module_exit\|[a-z]*_initcall\)(\([^)]*\));$/\1 \2 \3/p' | + sort -u + find ${DIR} -name '*.c' | + while read file; do + sed -n "/.* [a-z_0-9]*(void)\$/ { + : loop + N + /^[^\n]* [a-z_0-9]*(void)\n.*\n}\$/ { + /.*platform_\(driver\|device\)_\(un\)\{0,1\}register([&][^)]*).*/ { + s@^[^\n]* \([a-z_0-9]*\)(void)\n.*@${file} select \1@p + b exit + } + /.*[ ${TAB}]_[a-z]_\(un\)\{0,1\}register_driver([&][^, ${TAB})]*).*/ { + s@^[^\n]* \([a-z_0-9]*\)(void)\n.*@${file} select \1@p + b exit + } + /.*i2c_\(add\|del\)_driver([&][^)]*).*/ { + s@^[^\n]* \([a-z_0-9]*\)(void)\n.*@${file} select \1@p + } + b exit + } + b loop + } + : exit" ${file} + done | + sort -u +) | + while read file call symbol; do + if ! add_debug_printk && [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call#select}}" ]; then + echo FAILED + fi + + if [ "${call}" != "${call#builtin_*_driver}" -o "${call}" != "${call#module_*_driver}" ]; then + symbol=${symbol}_init + fi + call=probe + sed -n "/\(static \)\{0,1\}.* ${symbol}(/,/^}/ { + s/.*platform_\(driver\|device\)_register([&]\([^)]*\)).*/struct platform_\1 \2/p + s/.*[ ${TAB}]\(_[a-z]\)_register_driver([&]\([^, ${TAB})]*\)).*/struct \1_driver \2/p + s/.*i2c_add_driver([&]\([^)]*\)).*/struct i2c_driver \1/p + } + s/\(builtin\|module\)_\([a-z]*_driver\)(\([^)]*\));/struct \2 \3/p" ${file} | + sort -u | + while read struct; do + sed -n "/\(static \)\{0,1\}${struct} = {/,/^};/ { + s/^[ ${TAB}]*[.]probe[ ${TAB}]*=[ ${TAB}]*\([^ ${TAB}]*\),\$/\1/p + }" ${file} | + sort -u | + while read symbol; do + if ! add_debug_printk && [ "${SYMBOLS}" == "${SYMBOLS#* ${symbol}:${call} }" ]; then + echo FAILED + fi + done + done + done | + sort -u | + tee ${TMP}/list | + grep '^FAILED' >/dev/null +ret=${?} +SYMBOLS=`grep -v '^FAILED$' ${TMP}/list` + +rm -rf ${TMP} + +if [ 0 -ne ${ret} ]; then + echo "DONE" >&2 + + AUTHOR_NAME="`git config user.name`" + AUTHOR_EMAIL="`git config user.email`" + if [ -z "${AUTHOR_EMAIL}" ]; then + AUTHOR_EMAIL="`${USER}@google.com`" + fi + if [ -z "${AUTHOR_NAME}" ]; then + convert_ldap_to_name() { + echo "${1##*/}" | + sed "s@[-_]@ @g + s@ *@ @g" | + sed 's@\(^\| \)a@\1A@g + s@\(^\| \)b@\1B@g + s@\(^\| \)c@\1C@g + s@\(^\| \)d@\1D@g + s@\(^\| \)e@\1E@g + s@\(^\| \)f@\1F@g + s@\(^\| \)g@\1G@g + s@\(^\| \)h@\1H@g + s@\(^\| \)i@\1I@g + s@\(^\| \)j@\1J@g + s@\(^\| \)k@\1K@g + s@\(^\| \)l@\1L@g + s@\(^\| \)m@\1M@g + s@\(^\| \)n@\1N@g + s@\(^\| \)o@\1O@g + s@\(^\| \)p@\1P@g + s@\(^\| \)q@\1Q@g + s@\(^\| \)r@\1R@g + s@\(^\| \)s@\1S@g + s@\(^\| \)t@\1T@g + s@\(^\| \)u@\1U@g + s@\(^\| \|Mc\)v@\1V@g + s@\(^\| \)w@\1W@g + s@\(^\| \)x@\1X@g + s@\(^\| \)y@\1Y@g + s@\(^\| \)z@\1Z@g' + } + AUTHOR_NAME="`convert_ldap_to_name ${USER}`" + fi + + git commit -a -m "GKI: DEBUG: add instrumentation to init and probe functions + +Automatically generated by ${0} + +The following symbols are instrumented: +`echo \"${SYMBOLS}\" | + sed 's/^/ - /'` + +Signed-off-by: ${AUTHOR_NAME} <${AUTHOR_EMAIL}> +Test: compile" || + echo "${RED}ERROR${NORMAL}: failed to commit changes to git" >&2 + exit +fi +echo "${RED}FAILED${NORMAL}: could not complete task" >&2 +exit 1 |