#!/bin/sh -u
parseoptions() {
trace_reads=false
trace_writes=false
trace_writepages=false
pid_view=false
while [ $# -ge 1 ]
do
case $1 in
-r)
trace_reads=true
;;
-w)
trace_writes=true
;;
-p)
trace_writepages=true
;;
-v)
pid_view=true
;;
*)
usage
;;
esac
shift
done
}
usage() {
echo "Usage: $0 [-r|-w|-p|-v]"
exit 1
}
getmodel() {
model=`adb shell getprop ro.product.name`
# Releases are inconsistent with various trailing characters, remove them all
model=`echo $model | sed 's/[ \t\r\n]*$//' `
echo Found $model Device
case $model in
aosp_gobo | gobo | gobo_512)
get_go_devnames
;;
marlin | sailfish)
get_marlin_sailfish_devnames
;;
angler)
get_angler_devnames
;;
bullhead)
get_bullhead_devnames
;;
volantis | volantisg)
get_volantis_devnames
;;
*)
echo Unknown Device $model
exit 1
;;
esac
}
get_go_devnames () {
# Hardcoding all of the mmcblk0 device for now
block_device=mmcblk0
bdev_set=true
}
get_volantis_devnames() {
bdev_set=true
block_device=mmcblk0
}
get_bullhead_devnames() {
bdev_set=true
block_device=mmcblk0
}
get_marlin_sailfish_devnames() {
bdev_set=true
block_device=sda
}
get_angler_devnames () {
# Get the underlying bdev from the "by-name" mapping
block_device=`adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep system | awk '{ print $10 }' `
# extract the last component of the absolute device pathname we got above
block_device=`echo $block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' | sed 's/p.*//g' `
bdev_set=true
}
disk_stats_before() {
if [ $bdev_set == true ]; then
DISKSTATS=`adb shell 'cat /proc/diskstats' | fgrep -w $block_device `
# Get BEFORE read stats for bdev
BEFORE_RD_IOS=`echo $DISKSTATS | awk '{ print $4 }' `
BEFORE_RD_SECTORS=`echo $DISKSTATS | awk '{ print $6 }' `
# Get BEFORE write stats for bdev
BEFORE_WR_IOS=`echo $DISKSTATS | awk '{ print $8 }' `
BEFORE_WR_SECTORS=`echo $DISKSTATS | awk '{ print $10 }' `
fi
if [ $f2fs_fs == 1 ] ; then
adb shell 'mount -o remount,background_gc=off /data'
F2FS_GC_SEGMENTS_BEFORE=`adb shell 'cat /sys/kernel/debug/f2fs/status' | grep segments | egrep 'data|node' | awk '{ segments += $5 } END { print segments }' `
fi
}
disk_stats_after() {
if [ $bdev_set == true ]; then
DISKSTATS=`adb shell 'cat /proc/diskstats' | fgrep -w $block_device `
# Get AFTER read stats for bdev
AFTER_RD_IOS=`echo $DISKSTATS | awk '{ print $4 }' `
AFTER_RD_SECTORS=`echo $DISKSTATS | awk '{ print $6 }' `
# Get BEFORE write stats for bdev
AFTER_WR_IOS=`echo $DISKSTATS | awk '{ print $8 }' `
AFTER_WR_SECTORS=`echo $DISKSTATS | awk '{ print $10 }' `
fi
if [ $f2fs_fs == 1 ] ; then
F2FS_GC_SEGMENTS_AFTER=`adb shell 'cat /sys/kernel/debug/f2fs/status' | grep segments | egrep 'data|node' | awk '{ segments += $5 } END { print segments }' `
adb shell 'mount -o remount,background_gc=on /data'
fi
}
disk_stats_delta_rd() {
file_data_KB=$1
if [ $bdev_set == true ]; then
# Sectors to KB
READ_KB=`expr $AFTER_RD_SECTORS - $BEFORE_RD_SECTORS`
READ_KB=`expr $READ_KB / 2`
echo "Total (ALL) Read KB $block_device = "$READ_KB
BLOCK_MINUS_FILE=`expr $READ_KB - $file_data_KB`
echo "READ DELTA: Total Blockdev Reads KB - Total File Data Reads KB = "$BLOCK_MINUS_FILE KB
echo "Total (ALL) Read IOs $block_device = "`expr $AFTER_RD_IOS - $BEFORE_RD_IOS`
fi
}
disk_stats_delta_wr() {
file_data_KB=$1
if [ $bdev_set == true ]; then
# Sectors to KB
WRITE_KB=`expr $AFTER_WR_SECTORS - $BEFORE_WR_SECTORS`
WRITE_KB=`expr $WRITE_KB / 2`
BLOCK_MINUS_FILE=`expr $WRITE_KB - $file_data_KB`
echo "WRITE DELTA: Total Blockdev Writes KB - Total File Data Writes KB = "$BLOCK_MINUS_FILE KB
echo "Total (ALL) Write IOs $block_device = "`expr $AFTER_WR_IOS - $BEFORE_WR_IOS`
fi
if [ $f2fs_fs == 1 ] ; then
F2FS_GC_SEGMENTS_DELTA=`expr $F2FS_GC_SEGMENTS_AFTER - $F2FS_GC_SEGMENTS_BEFORE`
F2FS_GC_KB_DELTA=`expr $F2FS_GC_SEGMENTS_DELTA \\* 2048`
fi
}
# For good measure clean up traces and reenable traces
clean_up_tracepoints() {
# This is a good point to check if the Android FS tracepoints are enabled in the
# kernel or not
tracepoint_exists=`adb shell 'if [ -d /sys/kernel/debug/tracing/events/android_fs ]; then echo 0; else echo 1; fi' `
if [ $tracepoint_exists == 1 ]; then
echo "Android FS tracepoints not enabled in kernel. Exiting..."
exit 1
fi
adb shell 'echo 0 > /sys/kernel/debug/tracing/tracing_on'
adb shell 'echo 0 > /sys/kernel/debug/tracing/trace'
if [ $trace_reads == true ]; then
adb shell 'echo 1 > /sys/kernel/debug/tracing/events/android_fs/android_fs_dataread_start/enable'
fi
if [ $trace_writes == true ]; then
adb shell 'echo 1 > /sys/kernel/debug/tracing/events/android_fs/android_fs_datawrite_start/enable'
fi
if [ $f2fs_fs == 1 ] ; then
if [ $trace_writepages == true ]; then
adb shell 'echo 1 > /sys/kernel/debug/tracing/events/android_fs/android_fs_writepages/enable'
fi
fi
adb shell 'echo 1 > /sys/kernel/debug/tracing/tracing_on'
}
# stream trace out of trace_pipe
# Start this in the background ('&')
streamtrace_start() {
adb shell cat /sys/kernel/debug/tracing/trace_pipe > trace_saved
}
# When signal is received, the trace_pipe reader will get killed
# Call this (just to make sure anyway)
streamtrace_end() {
ps_line=`ps -ef | grep trace_pipe | grep adb `
if [ $? == 0 ]; then
echo Killing `echo $ps_line | awk '{s = ""; for (i=8; i <= NF ; i++) s = s $i " "; print s}' `
kill `echo $ps_line | awk '{print $2}' `
fi
}
copyout_trace() {
streamtrace_end
if [ $trace_reads == true ]; then
adb shell 'echo 0 > /sys/kernel/debug/tracing/events/android_fs/android_fs_dataread_start/enable'
fi
if [ $trace_writes == true ]; then
adb shell 'echo 0 > /sys/kernel/debug/tracing/events/android_fs/android_fs_datawrite_start/enable'
fi
if [ $f2fs_fs == 1 ] ; then
if [ $trace_writepages == true ]; then
adb shell 'echo 0 > /sys/kernel/debug/tracing/events/android_fs/android_fs_writepages/enable'
fi
fi
adb shell 'echo 0 > /sys/kernel/debug/tracing/tracing_on'
}
prep_tracefile_common() {
cp trace_saved $infile
# Strip away all the extraneous stuff first
fgrep $1 $infile | sed 's/^.* \[.*\] //' | sed s/://g | sed s/,//g > foo
mv foo $infile
}
prep_tracefile_rd() {
prep_tracefile_common android_fs_dataread
# Strip away unnecessary stuff so we can compute latencies easily
fgrep android_fs_dataread_start $infile > foo0
# Throw away everything upto and including android_fs_dataread:
cat foo0 | sed -n -e 's/^.*android_fs_dataread_start //p' > foo1
mv foo1 $infile
# At this stage, $infile should the following format :
# entry_name <filename> offset <offset> bytes <bytes> cmdline <cmdline> pid <pid> i_size <i_size> ino <ino>
rm foo0
}
prep_tracefile_writepages() {
prep_tracefile_common android_fs_writepages
# Throw away everything up to and including android_fs_writepages_start:
cat $infile | sed -n -e 's/^.*android_fs_writepages //p' > foo1
mv foo1 $infile
# At this stage, $infile should the following format :
# entry_name <filename> bytes <bytes> ino <ino>
}
# Latencies not supported for Writes. 'Write End' is just when the data has been
# written back to page cache.
prep_tracefile_wr() {
prep_tracefile_common android_fs_datawrite
fgrep android_fs_datawrite_start $infile > foo0
# Throw away everything upto and including android_fs_datawrite:
cat foo0 | sed -n -e 's/^.*android_fs_datawrite_start //p' > foo1
mv foo1 $infile
# At this stage, $infile should the following format :
# entry_name <filename> offset <offset> bytes <bytes> cmdline <cmdline> pid <pid> i_size <i_size> ino <ino>
rm foo0
}
get_unique_files_rw() {
# Sort first by filename, then by pid
cat $infile | sed s/,//g | sort -d -k2,2 -k8,8 > foo1
mv foo1 $infile
# $infile now contains lines sorted by <filename, pid>
# How many unique files are there ?
cat $infile | awk '{ print $2 }' > foo1
cat foo1 | uniq > uniq_files
rm foo1
}
get_unique_files_writepages() {
cat $infile | sed s/,//g | sort -d -k2,2 > foo1
# $infile now contains lines sorted by <filename>
mv foo1 $infile
# How many unique files are there ?
cat $infile | awk '{ print $2 }' > foo1
cat foo1 | uniq > uniq_files
rm foo1
}
get_unique_pids_byfile() {
# How many unique pids are there reading this file ?
cat $1 | awk '{ print $8 }' > foo1
cat foo1 | uniq > uniq_pids_byfile
rm foo1
}
get_unique_pids() {
# Sort first by pid, then by filename
cat $infile | sed s/,//g | sort -d -k8,8 -k2,2 > foo1
mv foo1 $infile
# $infile now contains lines sorted by <pid, filename>
# How many unique pids are there ?
cat $infile | awk '{ print $8 }' > foo1
cat foo1 | uniq > uniq_pids
rm foo1
}
get_unique_files_bypid() {
# How many unique files are there read by this pid ?
cat $1 | awk '{ print $2 }' > foo1
cat foo1 | uniq > uniq_files_bypid
rm foo1
}
catch_sigint()
{
echo "signal INT received, killing streaming trace capture"
streamtrace_end
}
prep_to_do_something() {
# adb shell "am force-stop com.android.chrome"
# adb shell "am force-stop com.google.android.gm"
adb shell 'echo 3 > /proc/sys/vm/drop_caches'
sleep 1
}
do_something() {
# Arrange things so that the first SIGINT will kill the
# child process (sleep), but will return to the parent.
trap 'catch_sigint' INT
echo "OK to kill sleep when test is done"
sleep 30d
# adb shell "am start -W -n com.android.chrome/com.google.android.apps.chrome.Main"
# adb shell "am start -W -n com.google.android.gm/.ConversationListActivityGmail"
}
# Get the aggregate list of files read/written. For each file, break up the IOs by pid
process_files_rw() {
read_write=$1
get_unique_files_rw
# Loop over each file that was involved in IO
# Find all the pids doing IO on that file
# Aggregate the IO done by each pid on that file and dump it out
grand_total_KB=0
cp $infile tempfile
for i in `cat uniq_files`
do
# Get just the tracepoints for this file
fgrep -w "$i" tempfile > subtrace
if [ -s subtrace ]; then
echo "File: $i"
total_file_KB=0
# Remove the tracepoints we just picked up
fgrep -v -w "$i" tempfile > foo
mv foo tempfile
# Get all the pids doing IO on this file
get_unique_pids_byfile subtrace
for j in `cat uniq_pids_byfile`
do
echo -n " $j $read_write: "
pid_KB=`fgrep -w "$j" subtrace | awk '{ bytes += $6 } END { print bytes }' `
pid_KB=`expr $pid_KB / 1024`
echo "$pid_KB KB"
total_file_KB=`expr $total_file_KB + $pid_KB`
done
i_size=`tail -n1 subtrace | awk '{ if ($12 > 1024) printf "%d KB", ($12/1024); else printf "%d bytes", $12; }' `
echo " Total $read_write: $total_file_KB KB i_size: $i_size"
grand_total_KB=`expr $grand_total_KB + $total_file_KB`
fi
done
echo "Grand Total File DATA KB $read_write $grand_total_KB"
rm tempfile
}
process_files_writepages() {
get_unique_files_writepages
# Loop over each file that was involved in IO
# Aggregate the IO done on that file and dump it out
grand_total_KB=0
cp $infile tempfile
for i in `cat uniq_files`
do
# Get just the tracepoints for this file
fgrep -w "$i" tempfile > subtrace
if [ -s subtrace ]; then
fgrep -v -w "$i" tempfile > foo
mv foo tempfile
total_file_KB=`cat subtrace | awk '{ bytes += $4 } END { print bytes }' `
total_file_KB=`expr $total_file_KB / 1024`
if [ $total_file_KB -gt 0 ]; then
echo "File: $i Total $read_write: $total_file_KB KB"
grand_total_KB=`expr $grand_total_KB + $total_file_KB`
fi
fi
done
echo "Grand Total File DATA KB Writepages $grand_total_KB"
rm tempfile
}
# Get the aggregate list of pids. For each pid, break up the IOs by file
process_pids() {
read_write=$1
get_unique_pids
list_of_pids=`cat uniq_pids`
# $list_of_pids is a list of all the pids involved in IO
#
# Loop over each pid that was involved in IO
# Find all the files the pid was doing IO on
# Aggregate the IO done by the pid for each file and dump it out
#
grand_total_KB=0
for i in $list_of_pids
do
echo "PID: $i"
total_pid_KB=0
# Get just the tracepoints for this pid
fgrep -w "$i" $infile > subtrace
# Get all the pids doing IO on this file
get_unique_files_bypid subtrace
list_of_files=`cat uniq_files_bypid`
# $list_of_files is a list of all the files IO'ed by this pid
for j in $list_of_files
do
i_size=`fgrep -w "$j" subtrace | tail -n1 | awk '{ if ($12 > 1024) printf "%d KB", ($12/1024); else printf "%d bytes", $12; }' `
file_KB=`fgrep -w "$j" subtrace | awk '{ bytes += $6 } END { print bytes }' `
file_KB=`expr $file_KB / 1024`
echo " $j $read_write: $file_KB KB i_size: $i_size"
total_pid_KB=`expr $total_pid_KB + $file_KB`
done
echo " Total $read_write: $total_pid_KB KB"
grand_total_KB=`expr $grand_total_KB + $total_pid_KB`
done
echo "Grand Total File DATA KB $read_write $grand_total_KB"
}
# main() starts here :
if [ $# -lt 1 ]; then
usage
fi
bdev_set=false
infile=tracefile.$$
parseoptions $@
adb root && sleep 2
getmodel
found_f2fs=`adb shell 'mount | grep f2fs > /dev/null; echo $?' `
if [ $found_f2fs == 0 ]; then
f2fs_fs=1
else
f2fs_fs=0
fi
if [ $f2fs_fs == 0 ] && [ $trace_writepages == true ]; then
echo "Writepages is only supported with f2fs, please use -r, -w"
exit 1
fi
prep_to_do_something
clean_up_tracepoints
disk_stats_before
# Start streaming the trace into the tracefile
streamtrace_start &
do_something
streamtrace_end
disk_stats_after
copyout_trace
if [ $trace_reads == true ]; then
echo
echo "READS :"
echo "_______"
echo
prep_tracefile_rd
# Get file specific stats - for each file, how many pids read that file ?
echo "FILE VIEW:"
process_files_rw Reads
if [ $pid_view == true ]; then
# Get pid specific stats - for each pid, what files do they do IO on ?
echo "PID VIEW:"
process_pids Reads
fi
disk_stats_delta_rd $grand_total_KB
debug_FileKB_rd=`cat $infile | awk '{ bytes += $6 } END { printf "%d", bytes/1024 }' `
echo Debug Grand Total KB READ $debug_FileKB_rd
fi
if [ $trace_writes == true ]; then
echo
echo "Writes :"
echo "_______"
echo
prep_tracefile_wr
# Get file specific stats - for each file, how many pids read that file ?
echo "FILE VIEW:"
process_files_rw Writes
if [ $pid_view == true ]; then
# Get pid specific stats - for each pid, what files do they do IO on ?
echo "PID VIEW:"
process_pids Writes
fi
disk_stats_delta_wr $grand_total_KB
if [ $f2fs_fs == 1 ] ; then
echo f2fs GC_KB delta = $F2FS_GC_KB_DELTA
fi
fi
if [ $f2fs_fs == 1 ] && [ $trace_writepages == true ] ; then
echo
echo "Writepages :"
echo "__________"
echo
prep_tracefile_writepages
# Get file specific stats - for each file, how much did we writepage ?
echo "FILE VIEW:"
process_files_writepages
disk_stats_delta_wr $grand_total_KB
echo f2fs GC_KB delta = $F2FS_GC_KB_DELTA
fi
rm -rf tracefile* uniq_* subtrace trace_saved