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