1# functions and definitions for workload automation scripts
2#
3# See recentfling.sh, systemapps.sh, and other scripts that use
4# these definitions.
5#
6
7dflttracecategories="gfx input view am rs power sched freq idle load memreclaim"
8dfltAppList="gmail hangouts chrome youtube camera photos play maps calendar earth calculator sheets docs home"
9generateActivities=0
10
11# default activities. Can dynamically generate with -g.
12gmailActivity='com.google.android.gm/com.google.android.gm.ConversationListActivityGmail'
13hangoutsActivity='com.google.android.talk/com.google.android.talk.SigningInActivity'
14chromeActivity='com.android.chrome/_not_used'
15youtubeActivity='com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity'
16cameraActivity='com.google.android.GoogleCamera/com.android.camera.CameraActivity'
17playActivity='com.android.vending/com.google.android.finsky.activities.MainActivity'
18feedlyActivity='com.devhd.feedly/com.devhd.feedly.Main'
19photosActivity='com.google.android.apps.plus/com.google.android.apps.photos.phone.PhotosHomeActivity'
20mapsActivity='com.google.android.apps.maps/com.google.android.maps.MapsActivity'
21calendarActivity='com.google.android.calendar/com.android.calendar.AllInOneActivity'
22earthActivity='com.google.earth/com.google.earth.EarthActivity'
23calculatorActivity='com.android.calculator2/com.android.calculator2.Calculator'
24sheetsActivity='com.google.android.apps.docs.editors.sheets/com.google.android.apps.docs.app.NewMainProxyActivity'
25docsActivity='com.google.android.apps.docs.editors.docs/com.google.android.apps.docs.app.NewMainProxyActivity'
26operaActivity='com.opera.mini.native/com.opera.mini.android.Browser'
27firefoxActivity='org.mozilla.firefox/org.mozilla.firefox.App'
28homeActivity='com.google.android.googlequicksearchbox/com.google.android.launcher.GEL'
29
30function showUsage {
31	echo "$0: unrecognized option: $1"
32	echo; echo "Usage: $0 [options]"
33	echo "-e : stop on error"
34	echo "-i iterations"
35	echo "-n : keep trace files"
36	echo "-o output file"
37	echo "-s device : adb device"
38	echo "-t trace categories"
39	echo "-g : generate activity strings"
40}
41
42DEVICE=unknown
43
44# handle args
45while [ $# -gt 0 ]
46do
47	case "$1" in
48	(-d) DEVICE=$2; shift;;
49	(-e) stoponerror=1;;
50	(-n) savetmpfiles=1;;
51	(-t) tracecategories=$2; shift;;
52	(-i) iterations=$2; shift;;
53	(-o) output=$2; shift;;
54	(-v) verbose=1;;
55	(-nz) compress=0;;
56	(-s) deviceName=$2; shift;;
57	(-g) generateActivities=1;;
58	(--) ;;
59	(*)
60		chk1=$(functions 2>/dev/null)
61		chk2=$(typeset -F 2>/dev/null)
62
63		if echo $chk1 $chk2 | grep -q processLocalOption; then
64			if ! processLocalOption "$1" "$2"; then
65				shift
66			fi
67		else
68			showUsage $1
69			exit 1
70		fi;;
71	esac
72	shift
73done
74
75# check if running on a device
76if ls /etc/* 2>/dev/null | grep -q android.hardware; then
77	ADB=""
78	compress=0
79	isOnDevice=1
80else
81	# do a throw-away adb in case the server is out-of-date
82	adb devices -l 2>&1 >/dev/null
83
84	if [ -z "$deviceName" ]; then
85		devInfo=$(adb devices -l | grep -v ^List | head -1)
86	else
87		devInfo=$(adb devices -l | grep $deviceName)
88	fi
89	set -- $devInfo
90	if [ -z $1 ]; then
91		echo Error: could not find device $deviceName
92		exit 1
93	fi
94	deviceName=$1
95	ADB="adb -s $deviceName shell "
96	DEVICE=$(echo $4 | sed 's/product://')
97	isOnDevice=0
98fi
99
100# default values if not set by options or calling script
101appList=${appList:=$dfltAppList}
102savetmpfiles=${savetmpfiles:=0}
103stoponerror=${stoponerror:=0}
104verbose=${verbose:=0}
105compress=${compress:=1}
106iterations=${iterations:=5}
107tracecategories=${tracecategories:=$dflttracecategories}
108ADB=${ADB:=""}
109output=${output:="./out"}
110
111# clear the output file
112> $output
113
114# ADB commands
115AM_FORCE_START="${ADB}am start -W -S"
116AM_START="${ADB}am start -W"
117AM_START_NOWAIT="${ADB}am start"
118AM_STOP="${ADB}am force-stop"
119AM_LIST="${ADB}am stack list"
120WHO="${ADB}whoami"
121INPUT="${ADB}input"
122PS="${ADB}ps"
123
124function vout {
125	# debug output enabled by -v
126	if [ $verbose -gt 0 ]; then
127	    echo DEBUG: $* >&2
128	    echo DEBUG: $* >&2 >> $output
129	fi
130}
131
132function findtimestamp {
133	# extract timestamp from atrace log entry
134	while [ "$2" != "" -a "$2" != "tracing_mark_write" ]
135	do
136		shift
137	done
138	echo $1
139}
140
141function computeTimeDiff {
142	# Compute time diff given: startSeconds startNs endSeconds endNS
143
144	# strip leading zeros
145	startS=$(expr 0 + $1)
146	endS=$(expr 0 + $3)
147	if [ "$2" = N ]; then
148		startNs=0
149		endNs=0
150	else
151		startNs=$(expr 0 + $2)
152		endNs=$(expr 0 + $4)
153	fi
154
155	((startMs=startS*1000 + startNs/1000000))
156	((endMs=endS*1000 + endNs/1000000))
157	((diff=endMs-startMs))
158	echo $diff
159}
160
161function log2msec {
162	in=$1
163	in=${in:=0.0}
164	set -- $(echo $in | tr . " ")
165	# shell addition via (( )) doesn't like leading zeroes in msecs
166	# field so remove leading zeroes
167	msecfield=$(expr 0 + $2)
168
169	((msec=$1*1000000+msecfield))
170	((msec=msec/1000))
171	echo $msec
172}
173
174function getStartTime {
175	# extract event indicating beginning of start sequence
176	# a) look for a "launching" event indicating start from scratch
177	# b) look for another activity getting a pause event
178	_app=$1
179	traceout=$2
180	ret=0
181	s=$(grep "Binder.*tracing_mark_write.*launching" $traceout 2>/dev/null | head -1| tr [\(\)\[\]
182:] " ")
183	if [ -z "$s" ]; then
184		s=$(grep activityPause $traceout | head -1 2>/dev/null| tr [\(\)\[\]
185:] " ")
186	else
187		vout $_app was restarted!
188		ret=1
189	fi
190	vout STARTLOG: $s
191	log2msec $(findtimestamp $s)
192	return $ret
193}
194
195function getEndTime {
196	# extract event indicating end of start sequence. We use the
197	# first surfaceflinger event associated with the target activity
198	_app=$1
199	traceout=$2
200	f=$(grep "surfaceflinger.*tracing_mark_write.*$_app" $traceout 2>/dev/null |
201		grep -v Starting | head -1 | tr [\(\)\[\]
202:] " ")
203	if [ -z "$f" ]; then
204		# Hmm. sf symbols may not be there... get the pid
205		pid=$(${ADB}pidof /system/bin/surfaceflinger | tr "[
206]" "[ ]")
207		f=$(grep "           <...>-$pid.*tracing_mark_write.*$_app" $traceout 2>/dev/null |
208			grep -v Starting | head -1 | tr [\(\)\[\]
209:] " ")
210	fi
211	vout ENDLOG: $f
212	log2msec $(findtimestamp $f)
213}
214
215function resetJankyFrames {
216	_gfxapp=$1
217	_gfxapp=${app:="com.android.systemui"}
218	${ADB}dumpsys gfxinfo $_gfxapp reset 2>&1 >/dev/null
219}
220
221function getJankyFrames {
222	_gfxapp=$1
223	_gfxapp=${_gfxapp:="com.android.systemui"}
224
225	# Note: no awk or sed on devices so have to do this
226	# purely with bash
227	total=0
228	janky=0
229	latency=0
230	${ADB}dumpsys gfxinfo $_gfxapp | tr "\r" " " | egrep "9[059]th| frames" | while read line
231	do
232		if echo $line | grep -q "Total frames"; then
233			set -- $line
234			total=$4
235		elif echo $line | grep -q "Janky frames"; then
236			set -- $line
237			janky=$3
238		elif echo $line | grep -q "90th"; then
239			set -- $(echo $line | tr m " ")
240			l90=$3
241		elif echo $line | grep -q "95th"; then
242			set -- $(echo $line | tr m " ")
243			l95=$3
244		elif echo $line | grep -q "99th"; then
245			set -- $(echo $line | tr m " ")
246			l99=$3
247			echo $total $janky $l90 $l95 $l99
248			break
249		fi
250	done
251}
252
253function checkForDirectReclaim {
254	# look for any reclaim events in atrace output
255	_app=$1
256	traceout=$2
257	if grep -qi reclaim $traceout; then
258	   return 1
259	fi
260	return 0
261}
262
263function startInstramentation {
264	# Called at beginning of loop. Turn on instramentation like atrace
265	vout start instramentation $(date)
266	echo =============================== >> $output
267	echo Before iteration >> $output
268	echo =============================== >> $output
269	${ADB}cat /proc/meminfo 2>&1 >> $output
270	${ADB}dumpsys meminfo 2>&1 >> $output
271	if [ "$user" = root ]; then
272		vout ${ADB}atrace -b 32768 --async_start $tracecategories
273		${ADB}atrace -b 32768 --async_start $tracecategories >> $output
274		echo >> $output
275	fi
276}
277
278function stopInstramentation {
279	if [ "$user" = root ]; then
280		vout ${ADB}atrace --async_stop
281		${ADB}atrace --async_stop > /dev/null
282	fi
283}
284
285function stopAndDumpInstramentation {
286	# Called at beginning of loop. Turn on instramentation like atrace
287	vout stop instramentation $(date)
288	echo =============================== >> $output
289	echo After iteration >> $output
290	echo =============================== >> $output
291	${ADB}cat /proc/meminfo 2>&1 >> $output
292	${ADB}dumpsys meminfo 2>&1 >> $output
293	if [ "$user" = root ]; then
294		traceout=$1
295		traceout=${traceout:=$output}
296		echo =============================== >> $traceout
297		echo TRACE >> $traceout
298		echo =============================== >> $traceout
299		if [ $compress -gt 0 ]; then
300			tmpTrace=./tmptrace.$$
301			UNCOMPRESS=$CMDDIR/atrace-uncompress.py
302			> $tmpTrace
303			zarg="-z"
304			${ADB}atrace -z -b 32768 --async_dump >> $tmpTrace
305			python $UNCOMPRESS $tmpTrace >> $traceout
306			rm -f $tmpTrace
307		else
308			${ADB}atrace $zarg -b 32768 --async_dump >> $traceout
309		fi
310		vout ${ADB}atrace $zarg --async_dump
311		vout ${ADB}atrace --async_stop
312		${ADB}atrace --async_stop > /dev/null
313	fi
314}
315
316function getActivityName {
317	cmd="actName=\$${1}Activity"
318	eval $cmd
319	echo $actName
320}
321
322function getPackageName {
323	set -- $(getActivityName $1 | tr "[/]" "[ ]")
324	echo $1
325}
326
327function startActivityFromPackage {
328	if [ "$1" = home ]; then
329		doKeyevent HOME
330		echo 0
331		return 0
332	fi
333	vout $AM_START_NOWAIT -p "$(getPackageName $1)" -c android.intent.category.LAUNCHER -a android.intent.action.MAIN
334	$AM_START_NOWAIT -p "$(getPackageName $1)" -c android.intent.category.LAUNCHER -a android.intent.action.MAIN 2>&1
335	echo 0
336}
337
338function startActivity {
339	if [ "$1" = home ]; then
340		doKeyevent HOME
341		echo 0
342		return 0
343	elif [ "$1" = chrome ]; then
344		if [ "$DEVICE" = volantis ]; then
345			vout $AM_START_NOWAIT -p "$(getPackageName $1)" http://www.theverge.com
346			$AM_START_NOWAIT -p "$(getPackageName $1)" http://www.theverge.com > /dev/null
347			set -- 0 0
348		else
349			vout $AM_START -p "$(getPackageName $1)" http://www.theverge.com
350			set -- $($AM_START -p "$(getPackageName $1)" http://www.theverge.com | grep ThisTime)
351		fi
352	else
353		vout $AM_START "$(getActivityName $1)"
354		set -- $($AM_START "$(getActivityName $1)" | grep ThisTime)
355	fi
356	echo $2 | tr "[\r]" "[\n]"
357}
358
359function forceStartActivity {
360	if [ "$1" = chrome ]; then
361		vout $AM_START -p "$(getPackageName $1)" http://www.theverge.com
362		set -- $($AM_FORCE_START -p "$(getPackageName $1)" http://www.theverge.com | grep ThisTime)
363	else
364		vout $AM_FORCE_START "$(getActivityName $1)"
365		set -- $($AM_FORCE_START "$(getActivityName $1)" | grep ThisTime)
366	fi
367	echo $2 | tr "[\r]" "[\n]"
368}
369
370function checkActivity {
371	# requires root
372	actName="$(getActivityName $1)"
373	$AM_LIST | grep $actName
374}
375
376#function stopActivity {
377#    vout $AM_STOP $(getActivityName $1)
378#    $AM_STOP $(getActivityName $1)
379#}
380
381function doSwipe {
382	vout ${ADB}input swipe $*
383	${ADB}input swipe $*
384}
385
386function doTap {
387	vout ${ADB}input tap $*
388	${ADB}input tap $*
389}
390
391function doKeyevent {
392	vout $INPUT keyevent $*
393	$INPUT keyevent $*
394}
395
396function checkIsRunning {
397	p=$1
398	shift
399	if ! $PS | grep $p | grep -qv grep; then
400	   handleError $*: $p is not running
401	   exit 1
402	fi
403}
404
405function checkStartTime {
406	vout checkStartTime $1 v $2
407	if [ -z "$2" ]; then
408	    echo false
409	    return 2
410	fi
411	if [ "$1" -gt "$2" ]; then
412	    echo false
413	    return 1
414	fi
415	echo true
416	return 0
417}
418
419function handleError {
420	echo Error: $*
421	stopAndDumpInstramentation
422	if [ $stoponerror -gt 0 ]; then
423		exit 1
424	fi
425}
426
427user=root
428if ${ADB}ls /data 2>/dev/null | grep -q "Permission denied"; then
429	user=shell
430fi
431vout User is $user
432
433if [ $generateActivities -gt 0  ]; then
434	if [ $isOnDevice -gt 0 ]; then
435		echo Error: cannot generate activity list when run on device
436		exit 1
437	fi
438	echo Generating activities...
439	for app in $appList
440	do
441		startActivityFromPackage $app 2>&1 > /dev/null
442		act=$(${ADB}am stack list | grep $(getPackageName $app) | sed -e 's/
443//' | head -1 | awk '{ print $2; }')
444		eval "${app}Activity=$act"
445		echo "ACTIVITY: $app --> $(getActivityName $app)"
446	done
447fi
448
449