#!/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 | walleye | taimen | blueline | crosshatch) get_marlin_sailfish_devnames ;; angler) get_angler_devnames ;; bullhead) get_bullhead_devnames ;; volantis | volantisg) get_volantis_devnames ;; *) echo Unknown Device $model -- trying Pixel config get_marlin_sailfish_devnames ;; 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/f2fs ]; 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/f2fs/f2fs_dataread_start/enable' fi if [ $trace_writes == true ]; then adb shell 'echo 1 > /sys/kernel/debug/tracing/events/f2fs/f2fs_datawrite_start/enable' fi if [ $f2fs_fs == 1 ] ; then if [ $trace_writepages == true ]; then adb shell 'echo 1 > /sys/kernel/debug/tracing/events/f2fs/f2fs_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/f2fs/f2fs_dataread_start/enable' fi if [ $trace_writes == true ]; then adb shell 'echo 0 > /sys/kernel/debug/tracing/events/f2fs/f2fs_datawrite_start/enable' fi if [ $f2fs_fs == 1 ] ; then if [ $trace_writepages == true ]; then adb shell 'echo 0 > /sys/kernel/debug/tracing/events/f2fs/f2fs_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 f2fs_dataread # Strip away unnecessary stuff so we can compute latencies easily fgrep f2fs_dataread_start $infile > foo0 # Throw away everything upto and including f2fs_dataread: cat foo0 | sed -n -e 's/^.*f2fs_dataread_start //p' > foo1 mv foo1 $infile # At this stage, $infile should the following format : # entry_name offset bytes cmdline pid i_size ino rm foo0 } prep_tracefile_writepages() { prep_tracefile_common f2fs_writepages # Throw away everything up to and including f2fs_writepages_start: cat $infile | sed -n -e 's/^.*f2fs_writepages //p' > foo1 mv foo1 $infile # At this stage, $infile should the following format : # entry_name bytes 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 f2fs_datawrite fgrep f2fs_datawrite_start $infile > foo0 # Throw away everything upto and including f2fs_datawrite: cat foo0 | sed -n -e 's/^.*f2fs_datawrite_start //p' > foo1 mv foo1 $infile # At this stage, $infile should the following format : # entry_name offset bytes cmdline pid i_size 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 # 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 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 # 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