blob: 66b48fafd0412dbf0ca22c11358053b6f10bd2be [file] [log] [blame]
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -07001#!/bin/sh
2#
3# Copyright (C) 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# Note: Requires $ANDROID_BUILD_TOP/build/envsetup.sh to have been run.
18#
19# This script takes in a logcat containing Sanitizer traces and outputs several
20# files, prints information regarding the traces, and plots information as well.
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070021ALL_PIDS=false
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -070022USE_TEMP=true
23DO_REDO=false
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070024PACKAGE_NAME=""
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -070025# EXACT_ARG and MIN_ARG are passed to prune_sanitizer_output.py
26EXACT_ARG=""
27MIN_ARG=""
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070028OFFSET_ARGS=""
29TIME_ARGS=""
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -070030usage() {
31 echo "Usage: $0 [options] [LOGCAT_FILE] [CATEGORIES...]"
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070032 echo " -a"
33 echo " Forces all pids associated with registered dex"
34 echo " files in the logcat to be processed."
35 echo " default: only the last pid is processed"
36 echo
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -070037 echo " -d OUT_DIRECTORY"
38 echo " Puts all output in specified directory."
39 echo " If not given, output will be put in a local"
40 echo " temp folder which will be deleted after"
41 echo " execution."
42 echo
43 echo " -e"
44 echo " All traces will have exactly the same number"
45 echo " of categories which is specified by either"
46 echo " the -m argument or by prune_sanitizer_output.py"
47 echo
48 echo " -f"
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070049 echo " Forces redo of all commands even if output"
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -070050 echo " files exist. Steps are skipped if their output"
51 echo " exist already and this is not enabled."
52 echo
53 echo " -m [MINIMUM_CALLS_PER_TRACE]"
54 echo " Filters out all traces that do not have"
55 echo " at least MINIMUM_CALLS_PER_TRACE lines."
56 echo " default: specified by prune_sanitizer_output.py"
57 echo
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070058 echo " -o [OFFSET],[OFFSET]"
59 echo " Filters out all Dex File offsets outside the"
60 echo " range between provided offsets. 'inf' can be"
61 echo " provided for infinity."
62 echo " default: 0,inf"
63 echo
64 echo " -p [PACKAGE_NAME]"
65 echo " Using the package name, uses baksmali to get"
66 echo " a dump of the Dex File format for the package."
67 echo
68 echo " -t [TIME_OFFSET],[TIME_OFFSET]"
69 echo " Filters out all time offsets outside the"
70 echo " range between provided offsets. 'inf' can be"
71 echo " provided for infinity."
72 echo " default: 0,inf"
73 echo
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -070074 echo " CATEGORIES are words that are expected to show in"
75 echo " a large subset of symbolized traces. Splits"
76 echo " output based on each word."
77 echo
78 echo " LOGCAT_FILE is the piped output from adb logcat."
79 echo
80}
81
82
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -070083while getopts ":ad:efm:o:p:t:" opt ; do
84case ${opt} in
85 a)
86 ALL_PIDS=true
87 ;;
88 d)
89 USE_TEMP=false
90 OUT_DIR=$OPTARG
91 ;;
92 e)
93 EXACT_ARG='-e'
94 ;;
95 f)
96 DO_REDO=true
97 ;;
98 m)
99 if ! [ "$OPTARG" -eq "$OPTARG" ]; then
100 usage
101 exit
102 fi
103 MIN_ARG='-m '"$OPTARG"
104 ;;
105 o)
106 set -f
107 OLD_IFS=$IFS
108 IFS=","
109 OFFSET_ARGS=( $OPTARG )
110 if [ "${#OFFSET_ARGS[@]}" -ne 2 ]; then
111 usage
112 exit
113 fi
114 OFFSET_ARGS=( "--offsets" "${OFFSET_ARGS[@]}" )
115 IFS=$OLD_IFS
116 ;;
117 t)
118 set -f
119 OLD_IFS=$IFS
120 IFS=","
121 TIME_ARGS=( $OPTARG )
122 if [ "${#TIME_ARGS[@]}" -ne 2 ]; then
123 usage
124 exit
125 fi
126 TIME_ARGS=( "--times" "${TIME_ARGS[@]}" )
127 IFS=$OLD_IFS
128 ;;
129 p)
130 PACKAGE_NAME=$OPTARG
131 ;;
132 \?)
133 usage
134 exit
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700135esac
136done
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700137shift $((OPTIND -1))
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700138
139if [ $# -lt 1 ]; then
140 usage
141 exit
142fi
143
144LOGCAT_FILE=$1
145NUM_CAT=$(($# - 1))
146
147# Use a temp directory that will be deleted
148if [ $USE_TEMP = true ]; then
149 OUT_DIR=$(mktemp -d --tmpdir=$PWD)
150 DO_REDO=true
151fi
152
153if [ ! -d "$OUT_DIR" ]; then
154 mkdir $OUT_DIR
155 DO_REDO=true
156fi
157
158# Note: Steps are skipped if their output exists until -f flag is enabled
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700159echo "Output folder: $OUT_DIR"
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700160unique_pids=( $(grep "RegisterDexFile" "$LOGCAT_FILE" | grep -v "zygote64" | tr -s ' ' | cut -f3 -d' ' | awk '!a[$0]++') )
161echo "List of pids: ${unique_pids[@]}"
162if [ $ALL_PIDS = false ]; then
163 unique_pids=( ${unique_pids[-1]} )
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700164fi
165
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700166for pid in "${unique_pids[@]}"
167do
168 echo
169 echo "Current pid: $pid"
170 echo
171 PID_DIR=$OUT_DIR/$pid
172 if [ ! -d "$PID_DIR" ]; then
173 mkdir $PID_DIR
174 DO_REDO[$pid]=true
175 fi
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700176
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700177 INTERMEDIATES_DIR=$PID_DIR/intermediates
178 RESULTS_DIR=$PID_DIR/results
179 LOGCAT_PID_FILE=$PID_DIR/logcat
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700180
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700181 if [ ! -f "$PID_DIR/logcat" ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
182 DO_REDO[$pid]=true
183 awk '{if($3 == '$pid') print $0}' $LOGCAT_FILE > $LOGCAT_PID_FILE
184 fi
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700185
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700186 if [ ! -d "$INTERMEDIATES_DIR" ]; then
187 mkdir $INTERMEDIATES_DIR
188 DO_REDO[$pid]=true
189 fi
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700190
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700191 # Step 1 - Only output lines related to Sanitizer
192 # Folder that holds all file output
193 ASAN_OUT=$INTERMEDIATES_DIR/asan_output
194 if [ ! -f $ASAN_OUT ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
195 DO_REDO[$pid]=true
196 echo "Extracting ASAN output"
197 grep "app_process64" $LOGCAT_PID_FILE > $ASAN_OUT
198 else
199 echo "Skipped: Extracting ASAN output"
200 fi
Bharadwaj Kalandhabhatta9e1c45d2017-06-13 08:56:51 -0700201
Bharadwaj Kalandhabhatta9edf8d12017-07-11 13:32:24 -0700202 # Step 2 - Only output lines containing Dex File Start Addresses
203 DEX_START=$INTERMEDIATES_DIR/dex_start
204 if [ ! -f $DEX_START ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
205 DO_REDO[$pid]=true
206 echo "Extracting Start of Dex File(s)"
207 grep "RegisterDexFile" $LOGCAT_PID_FILE > $DEX_START
208 else
209 echo "Skipped: Extracting Start of Dex File(s)"
210 fi
211
212 # Step 3 - Clean Sanitizer output from Step 2 since logcat cannot
213 # handle large amounts of output.
214 ASAN_OUT_FILTERED=$INTERMEDIATES_DIR/asan_output_filtered
215 if [ ! -f $ASAN_OUT_FILTERED ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
216 DO_REDO[$pid]=true
217 echo "Filtering/Cleaning ASAN output"
218 python $ANDROID_BUILD_TOP/art/tools/runtime_memusage/prune_sanitizer_output.py \
219 $EXACT_ARG $MIN_ARG -d $INTERMEDIATES_DIR $ASAN_OUT
220 else
221 echo "Skipped: Filtering/Cleaning ASAN output"
222 fi
223
224 # Step 4 - Retrieve symbolized stack traces from Step 3 output
225 SYM_FILTERED=$INTERMEDIATES_DIR/sym_filtered
226 if [ ! -f $SYM_FILTERED ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
227 DO_REDO[$pid]=true
228 echo "Retrieving symbolized traces"
229 $ANDROID_BUILD_TOP/development/scripts/stack $ASAN_OUT_FILTERED > $SYM_FILTERED
230 else
231 echo "Skipped: Retrieving symbolized traces"
232 fi
233
234 # Step 4.5 - Obtain Dex File Format of dex file related to package
235 BAKSMALI_DMP_OUT="$INTERMEDIATES_DIR""/baksmali_dex_file"
236 BAKSMALI_DMP_ARG="--dex-file="$BAKSMALI_DMP_OUT
237 if [ ! -f $BAKSMALI_DMP_OUT ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
238 if [ $PACKAGE_NAME != "" ]; then
239 # Extracting Dex File path on device from Dex File related to package
240 apk_directory=$(dirname $(grep $PACKAGE_NAME $DEX_START | tail -n1 | awk '{print $8}'))
241 apk_dex_files=$(adb shell find $apk_directory -name "*.?dex" -type f 2> /dev/null)
242 for apk_file in $apk_dex_files; do
243 base_name=$(basename $apk_file)
244 adb pull $apk_file $INTERMEDIATES_DIR/base."${base_name#*.}"
245 done
246 oatdump --oat-file=$INTERMEDIATES_DIR/base.odex --export-dex-to=$INTERMEDIATES_DIR --output=/dev/null
247 export_dex=( $INTERMEDIATES_DIR/*apk_export* )
248 baksmali -JXmx1024M dump $export_dex > $BAKSMALI_DMP_OUT 2> /dev/null
249 if ! [ -s $BAKSMALI_DMP_OUT ]; then
250 rm $BAKSMALI_DMP_OUT
251 BAKSMALI_DMP_ARG=""
252 echo "Failed to retrieve Dex File format"
253 fi
254 else
255 BAKSMALI_DMP_ARG=""
256 echo "Failed to retrieve Dex File format"
257 fi
258 else
259 echo "Skipped: Retrieving Dex File format from baksmali"
260 fi
261
262 if [ ! -d "$RESULTS_DIR" ]; then
263 mkdir $RESULTS_DIR
264 DO_REDO[$pid]=true
265 fi
266
267 # Step 5 - Using Steps 2, 3, 4 outputs in order to output graph data
268 # and trace data
269 # Only the category names are needed for the commands giving final output
270 shift
271 TIME_OUTPUT=($RESULTS_DIR/time_output_*.dat)
272 if [ ! -e ${TIME_OUTPUT[0]} ] || [ "${DO_REDO[$pid]}" = true ] || [ $DO_REDO = true ]; then
273 DO_REDO[$pid]=true
274 echo "Creating Categorized Time Table"
275 python $ANDROID_BUILD_TOP/art/tools/runtime_memusage/symbol_trace_info.py \
276 -d $RESULTS_DIR ${OFFSET_ARGS[@]} ${TIME_ARGS[@]} $BAKSMALI_DMP_ARG $ASAN_OUT_FILTERED $SYM_FILTERED $DEX_START $@
277 else
278 echo "Skipped: Creating Categorized Time Table"
279 fi
280
281 # Step 6 - Use graph data from Step 5 to plot graph
282 # Contains the category names used for legend of gnuplot
283 PLOT_CATS=`echo \"Uncategorized $@\"`
284 PACKAGE_STRING=""
285 if [ $PACKAGE_NAME != "" ]; then
286 PACKAGE_STRING="Package name: "$PACKAGE_NAME" "
287 fi
288 echo "Plotting Categorized Time Table"
289 # Plots the information from logcat
290 gnuplot --persist -e \
291 'filename(n) = sprintf("'"$RESULTS_DIR"'/time_output_%d.dat", n);
292 catnames = '"$PLOT_CATS"';
293 set title "'"$PACKAGE_STRING"'PID: '"$pid"'";
294 set xlabel "Time (milliseconds)";
295 set ylabel "Dex File Offset (bytes)";
296 plot for [i=0:'"$NUM_CAT"'] filename(i) using 1:2 title word(catnames, i + 1);'
297
298 if [ $USE_TEMP = true ]; then
299 echo "Removing temp directory and files"
300 rm -rf $OUT_DIR
301 fi
302done