OSDN Git Service

Merge "Add a summary (-s) option for easier parseability."
authorMohan Srinivasan <srmohan@google.com>
Thu, 23 Feb 2017 19:29:03 +0000 (19:29 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Thu, 23 Feb 2017 19:29:03 +0000 (19:29 +0000)
49 files changed:
app-launcher/Android.mk [new file with mode: 0644]
app-launcher/README [new file with mode: 0644]
app-launcher/app-launcher [new file with mode: 0755]
app-launcher/computestats.c [new file with mode: 0644]
app-launcher/computestatsf.c [new file with mode: 0644]
boot_control_copy/Android.mk
bootctl/bootctl.cpp
cpustats/cpustats.c
ext4_utils/ext4_crypt.cpp
ext4_utils/ext4_crypt_init_extensions.cpp
ext4_utils/include/ext4_utils/ext4_crypt.h
ext4_utils/include/ext4_utils/make_ext4fs.h
ext4_utils/make_ext4fs.c
ext4_utils/mkuserimg_mke2fs.sh
perfprofd/tests/perfprofd_test.cc
simpleperf/Android.mk
simpleperf/IOEventLoop.cpp
simpleperf/InplaceSamplerClient.cpp [new file with mode: 0644]
simpleperf/InplaceSamplerClient.h [new file with mode: 0644]
simpleperf/cmd_list.cpp
simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/cmd_report_test.cpp
simpleperf/cmd_stat.cpp
simpleperf/cpu_hotplug_test.cpp
simpleperf/dso.cpp
simpleperf/event_fd.cpp
simpleperf/event_fd.h
simpleperf/event_selection_set.cpp
simpleperf/event_selection_set.h
simpleperf/event_type.h
simpleperf/event_type_table.h
simpleperf/generate_event_type_table.py
simpleperf/gtest_main.cpp
simpleperf/main.cpp
simpleperf/record.cpp
simpleperf/runtest/comm_change.cpp
simpleperf/runtest/function_fork.cpp
simpleperf/runtest/function_indirect_recursive.cpp
simpleperf/runtest/function_pthread.cpp
simpleperf/runtest/function_recursive.cpp
simpleperf/runtest/one_function.cpp
simpleperf/runtest/runtest.py
simpleperf/runtest/two_functions.cpp
simpleperf/workload.h
tests/bootloader/Android.mk [new file with mode: 0644]
tests/fstest/Android.mk
tests/memtest/bandwidth.cpp
verity/Android.mk

diff --git a/app-launcher/Android.mk b/app-launcher/Android.mk
new file mode 100644 (file)
index 0000000..755b1d3
--- /dev/null
@@ -0,0 +1,24 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+#LOCAL_32_BIT_ONLY = true
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := computestatsf.c
+LOCAL_MODULE := computestatsf
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+#LOCAL_32_BIT_ONLY = true
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_SRC_FILES := computestats.c
+LOCAL_MODULE := computestats
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_EXECUTABLES := app-launcher
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+include $(BUILD_HOST_PREBUILT)
diff --git a/app-launcher/README b/app-launcher/README
new file mode 100644 (file)
index 0000000..cca58aa
--- /dev/null
@@ -0,0 +1,76 @@
+               Instructions to Run (and modify) app-launcher script
+               ----------------------------------------------------
+
+Introduction: app-launcher is a script that launches apps many times,
+measures various system metrics, computes basic stats for the metrics
+and reports that stats.
+
+Launching app-launcher :
+app-launcher -a|-b|-u [-v] num-iterations
+-a:Run on all cores
+-b:Run only big cores
+-c:pagecached. Don't drop pagecache before each launch (not default)
+-h:Dump help menu'
+-u:user experience, no change to cpu/gpu frequencies or governors'
+-v : Optional, Verbose mode, prints stats on a lot of metrics.
+num-iterations : Must be >= 100 to get statistically valid data.
+
+Note, under -a|-b, we lock the CPU and GPU frequencies.
+
+Apps Supported :
+On phone, these 4 apps are launched
+Chrome
+Camera
+Maps
+Youtube
+
+On Fugu (Google TV), these 3 apps are launched
+YouTube
+Games
+Music
+
+To add new apps, launch app manually and grep for package name +
+activity name in logcat and add these to the launch_phone_apps()
+function.
+
+Adding support for new Devices to app-launcher :
+There are a few bits of code needed to do this.
+1) Add a new cpufreq_<device> routine to fix the CPU/GPU frequencies
+as desired.
+2) Add logic that checks the $model obtained and check against your device.
+   (a) Then add code to call your cpufreq_<device> routine there
+   (b) (Optional) Add code to get the /system block device pathname. This is
+   only needed if you wan to get storage block device (/system) data.
+
+Adding new Metrics to app-launcher :
+You can modify the way simpleperf is used in the script to collect
+different metrics, but that will require a change to getstats() to
+parse the output as necessary. Adding new storage stats or other stats
+collected from /proc (or /sys) is definitely possible, but code needs
+to be written for that - modeled after the disk_stats_before/after
+functions.
+
+Notes :
+
+Here are the commands to launch/stop the various Apps of interest. The
+way to find the package and activity for the app of interest is to
+launch the app and then grep for it in logcat to find the
+package+activity and use that in am start.
+
+Chrome :
+adb shell 'simpleperf stat -a am start -W -n com.android.chrome/com.google.android.apps.chrome.Main'
+adb shell 'am force-stop com.android.chrome'
+
+Camera :
+adb shell 'simpleperf stat -a am start -W -n com.google.android.GoogleCamera/com.android.camera.CameraActivity'
+adb shell 'am force-stop com.google.android.GoogleCamera'
+
+Maps :
+adb shell 'simpleperf stat -a am start -W -n com.google.android.apps.maps/com.google.android.maps.MapsActivity'
+adb shell 'am force-stop com.google.android.apps.maps'
+
+Youtube :
+adb shell 'am start -W -n com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity'
+adb shell 'am force-stop com.google.android.youtube'
+
+
diff --git a/app-launcher/app-launcher b/app-launcher/app-launcher
new file mode 100755 (executable)
index 0000000..f02b3dc
--- /dev/null
@@ -0,0 +1,455 @@
+#!/bin/sh
+
+parseoptions() {
+    verbose=false
+    user_experience=false
+    little_cores_off=false
+    iterations=0
+    pagecached=false
+
+    while [ $# -gt 1 ]
+    do
+       case $1 in
+           -a)
+               ;;
+           -b)
+               little_cores_off=true
+               ;;
+           -c)
+               pagecached=true
+               ;;
+           -h)
+               usage
+               ;;
+           -u)
+               user_experience=true
+               ;;
+           -v)
+               verbose=true
+               ;;
+           *)
+               usage
+               ;;
+           esac
+       shift
+    done
+
+    iterations=$1
+    if [ $iterations -lt 100 ]; then
+       usage
+    fi
+}
+
+getstats () {
+    infile=$1
+    app=$2
+    echo "Data for $app :"
+
+    # Activity Manager reports ThisTime and TotalTime. TotalTime seems to be
+    # a more measure of the launch from the users perspective. So using TotalTime
+    # as our metric for launch latency
+
+    # From Activity Manager
+    echo "Launch Time (TotalTime) :"
+    fgrep TotalTime $infile | awk '{print $2}' | computestats
+
+    # Data from simpleperf
+    echo "cpu-cycles :"
+    fgrep cpu-cycles $infile | awk '{print $1}' | sed s/,//g | computestats
+
+    # CPU util% Data from /proc/stat
+    echo "cpu-util% :"
+    fgrep 'Total CPU util' $infile | awk '{print $5}' | computestatsf
+    echo "user-cpu-util% :"
+    fgrep 'User CPU util' $infile | awk '{print $5}' | computestatsf
+    echo "sys-cpu-util% (incl hardirq/softirq) :"
+    fgrep 'Sys CPU util' $infile | awk '{print $5}' | computestatsf
+
+    if [ $verbose == true ]; then
+       echo "instructions : "
+       fgrep instructions $infile | awk '{print $1}' | sed s/,//g | computestats
+
+       echo "cycles per instruction : "
+       fgrep instructions $infile | awk '{print $4}' | sed s/,//g | computestatsf
+
+       echo "branch-misses : "
+       fgrep branch-misses $infile | awk '{print $1}' | sed s/,//g | computestats
+
+       echo "context-switches : "
+       fgrep context-switches $infile | awk '{print $1}' | sed s/,//g | computestats
+
+       echo "page-faults : "
+       fgrep page-faults $infile | awk '{print $1}' | sed s/,//g | computestats
+    fi
+
+    if [ $system_bdev_set == true ]; then
+       # (Storage) Data from /proc we've collected
+       echo "KB read for $system_block_device blkdev :"
+       fgrep KB $infile | grep system | awk '{print $5}' | computestats
+
+       echo "iowait% :"
+       fgrep IOwait $infile | awk '{print $3}' | computestatsf
+
+       echo "Device util% for $system_block_device blkdev :"
+       fgrep 'Device util' $infile | awk '{print $4}' | computestatsf
+    fi
+}
+
+cpufreq_volantis() {
+    echo "Setting Governor to performance"
+    if [ $little_cores_off == true ]; then
+        echo "Cannot turn off Little cores on $model"
+        exit 1
+    fi
+    i=0
+    num_cores=2
+    while [ $i -lt  $num_cores ]
+    do
+        adb shell "echo performance  > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_g\
+overnor"
+        adb shell "echo 2499000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_fr\
+eq"
+        i=`expr $i + 1`
+    done
+    # Lock the GPU frequencies
+    echo -n 852000000 > /d/clock/override.gbus/rate
+    echo -n 1 > /d/clock/override.gbus/state
+}
+
+cpufreq_fugu() {
+    echo "Setting Governor to performance"
+    if [ $little_cores_off == true ]; then
+       echo "Cannot turn off Little cores on $model"
+       exit 1
+    fi
+    i=0
+    num_cores=4
+    while [ $i -lt  $num_cores ]
+    do
+       adb shell "echo performance  > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor"
+       adb shell "echo 1833000 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq"
+       i=`expr $i + 1`
+    done
+}
+
+cpufreq_marlin_sailfish () {
+    echo "Setting Governor to performance"
+    # GPU Governor and Frequency
+    adb shell 'echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor'
+    adb shell 'echo 624000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq'
+    if [ $little_cores_off == true ]; then
+       # Disable Little Cores, force app to run on big cores
+       echo "Disabling Little Cores"
+       adb shell 'echo 0 > /sys/devices/system/cpu/cpu0/online'
+       adb shell 'echo 0 > /sys/devices/system/cpu/cpu1/online'
+    else
+       echo "Enabling All Cores"
+       adb shell 'echo 1 > /sys/devices/system/cpu/cpu0/online'
+       adb shell 'echo 1 > /sys/devices/system/cpu/cpu1/online'
+       adb shell 'echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'
+       adb shell 'echo 1996800 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq'
+       # cpu1 needed ?
+       adb shell 'echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor'
+       adb shell 'echo 1996800 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq'
+    fi
+    # Set Governor to performance, up scaling_max_frequency to highest
+    adb shell 'echo performance  > /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor'
+    # Only necessary to set max_freq on cpu2, cpu3 is in same cluster and will
+    # automatically get the same settings
+    adb shell 'echo 2150400 > /sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq'
+}
+
+cpufreq_angler () {
+    echo "Setting Governor and Frequency"
+    # GPU Governor and Frequency
+    adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
+    adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split"
+    adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
+    adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
+    if [ $little_cores_off == true ]; then
+       # Disable Little Cores, force app to run on big cores
+       echo "Disabling Little Cores"
+       i=0
+       num_cores=4
+       while [ $i -lt $num_cores ]
+       do
+           adb shell "echo 0 > /sys/devices/system/cpu/cpu$i/online"
+           i=`expr $i + 1`
+       done
+    else
+       echo "Enabling All Cores"
+       # Enable Little cores here, set governor to performance
+       # Lock frequency of little cores
+       i=0
+       num_cores=4
+       while [ $i -lt $num_cores ]
+       do
+           adb shell "echo 1 > /sys/devices/system/cpu/cpu$i/online"
+           adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor"
+           # Lock frequency of little cores
+           adb shell "echo 1555200 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq"
+           i=`expr $i + 1`
+       done
+    fi
+    i=4
+    num_cores=8
+    while [ $i -lt $num_cores ]
+    do
+       adb shell "echo performance > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor"
+       # Lock frequency of big cores
+       adb shell "echo 1958400 > /sys/devices/system/cpu/cpu$i/cpufreq/scaling_max_freq"
+       i=`expr $i + 1`
+    done
+}
+
+#
+# This strange bit of logic is needed to get the underlying block devices for /system
+# for Marlin/Sailfish
+#
+get_marlin_sailfish_devnames () {
+    # This bit of code required to get the block dev for /system and /vendor
+    # Suffix can be _a or _b, depending on what the active /system partition is
+#    suffix=`adb shell getprop ro.boot.slot_suffix`
+    # Get the blockdevice using the suffix we got above
+#    system_block_device=`adb shell ls -l /dev/block/platform/soc/*ufs*/by-name/system$suffix | awk '{ print $10 }' `
+    # Vendor is more straightforward, but we don't use it right now
+#    vendor_block_device=`adb shell df /vendor | grep -v Filesystem | awk '{print $1}' `
+    # finally extract the last component of the absolute device pathname we got above
+#    system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+#    vendor_block_device=`echo $vendor_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+    system_bdev_set=true
+#   For now, hardcode sda for Marlin/Sailfish block device
+#   XXX - We'll get stats for entire device
+    system_block_device=sda
+    echo Block Device $system_block_device
+}
+
+get_angler_devnames () {
+    # Get the underlying bdev from the "by-name" mapping
+    system_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
+    system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+    # vendor is unused right now, but get the bdev anyway in case we decide to use it
+    # Get the underlying bdev from the "by-name" mapping
+    vendor_block_device=`adb shell 'find /dev/block/platform -name by-name | xargs ls -l' | grep vendor | awk '{ print $10 }' `
+    # extract the last component of the absolute device pathname we got above
+   vendor_block_device=`echo $vendor_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+   system_bdev_set=true
+}
+
+get_fugu_devnames () {
+    system_block_device=`adb shell ls -l /dev/block/by-name/system | awk '{ print $10 }' `
+    system_block_device=`echo $system_block_device | awk 'BEGIN { FS ="/" } ; { print $4 }' `
+    system_bdev_set=true
+}
+
+get_volantis_devnames () {
+    # Hardcoding all of the mmcblk0 device for now
+    system_block_device=mmcblk0
+    system_bdev_set=true
+}
+
+system_stats_before() {
+    if [ $system_bdev_set == true ]; then
+       # Get BEFORE read stats for /system
+       adb shell 'cat /proc/diskstats' | grep -w $system_block_device > /tmp/$model-system
+       BEFORE_RD_IOS_SYSTEM=`awk '{ print $4 }' /tmp/$model-system`
+       BEFORE_RD_SECTORS_SYSTEM=`awk '{ print $6 }' /tmp/$model-system`
+       # iowait% computation
+       adb shell 'cat /proc/stat' | grep -w cpu > /tmp/procstat
+       user_ticks_before=`awk '{ print ($2 + $3) }' /tmp/procstat`
+       sys_ticks_before=`awk '{ print ($4 + $7 + $8) }' /tmp/procstat`
+       cpubusy_ticks_before=`awk '{ print ($2 + $3 + $4 + $7 + $8) }' /tmp/procstat`
+       iowait_ticks_before=`awk '{ print $6 }' /tmp/procstat`
+       total_ticks_before=`awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }' /tmp/procstat`
+       # Device util% computation
+       # Note hz=100, so multiplying uptime (in seconds) by 100, gives us
+       # the uptime in hz.
+       adb shell 'cat /proc/uptime' > /tmp/uptime
+       uptime_before_hz=`awk '{ print ($1 * 100) }' /tmp/uptime`
+       # Note that the device (busy) ticks is in ms. Since hz=100, dividing
+       # device (busy) ticks by 10, gives us this in the correct ticks units
+       device_util_before_hz=`awk '{ print ($13 / 10) }' /tmp/$model-system`
+    fi
+}
+
+system_stats_after() {
+    if [ $system_bdev_set == true ]; then
+       # Get AFTER read stats for /system
+       adb shell 'cat /proc/diskstats' | grep -w $system_block_device > /tmp/$model-system
+       AFTER_RD_IOS_SYSTEM=`awk '{ print $4 }' /tmp/$model-system`
+       AFTER_RD_SECTORS_SYSTEM=`awk '{ print $6 }' /tmp/$model-system`
+       # iowait% computation
+       adb shell 'cat /proc/stat' | grep -w cpu > /tmp/procstat
+       user_ticks_after=`awk '{ print ($2 + $3) }' /tmp/procstat`
+       sys_ticks_after=`awk '{ print ($4 + $7 + $8) }' /tmp/procstat`
+       cpubusy_ticks_after=`awk '{ print ($2 + $3 + $4 + $7 + $8) }' /tmp/procstat`
+       iowait_ticks_after=`awk '{ print $6 }' /tmp/procstat`
+       total_ticks_after=`awk '{ print ($2 + $3 + $4 + $5 + $7 + $8) }' /tmp/procstat`
+       # Device util% computation
+       # Note hz=100, so multiplying uptime (in seconds) by 100, gives us
+       # the uptime in hz.
+       adb shell 'cat /proc/uptime' > /tmp/uptime
+       uptime_after_hz=`awk '{ print ($1 * 100) }' /tmp/uptime`
+       # Note that the device (busy) ticks is in ms. Since hz=100, dividing
+       # device (busy) ticks by 10, gives us this in the correct ticks units
+       device_util_after_hz=`awk '{ print ($13 / 10) }' /tmp/$model-system`
+    fi
+}
+
+system_stats_delta() {
+    if [ $system_bdev_set == true ]; then
+       # Sectors to KB
+       READ_KB_SYSTEM=`expr $AFTER_RD_SECTORS_SYSTEM - $BEFORE_RD_SECTORS_SYSTEM`
+       READ_KB_SYSTEM=`expr $READ_KB_SYSTEM / 2`
+       echo Read IOs /system = `expr $AFTER_RD_IOS_SYSTEM - $BEFORE_RD_IOS_SYSTEM`
+       echo Read KB /system = $READ_KB_SYSTEM
+       echo $iowait_ticks_before $iowait_ticks_after $total_ticks_before $total_ticks_after | awk '{ printf "IOwait = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }'
+       echo $device_util_before_hz $device_util_after_hz $uptime_before_hz $uptime_after_hz | awk '{ printf "Device util% = %.2f\n", (($2 - $1) * 100.0) / ($4 - $3) }'
+       echo $user_ticks_after $user_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "User CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }'
+       echo $sys_ticks_after $sys_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "Sys CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }'
+       echo $cpubusy_ticks_after $cpubusy_ticks_before $total_ticks_after $total_ticks_before | awk '{ printf "Total CPU util% = %.2f\n", (($1 - $2) * 100.0) / ($3 - $4) }'
+    fi
+}
+
+launch_app() {
+    package=$1
+    activity=$2
+    adb shell "am force-stop $package"
+    sleep 1
+
+    i=0
+    while [ $i -lt $iterations ]
+    do
+       if [ $pagecached == false ]; then
+           adb shell 'echo 3 > /proc/sys/vm/drop_caches'
+       fi
+       # The -W argument to am start forces am start to wait till the launch completes.
+       # The -S argument forces it to kill any existing app that is running first
+       # eg. adb shell 'am start -W -S -n com.android.chrome/com.google.android.apps.chrome.Main'
+       system_stats_before
+       adb shell "simpleperf stat -a am start -W -n $package/$activity"
+       system_stats_after
+       system_stats_delta
+       sleep 1
+       adb shell "am force-stop $package"
+       sleep 1
+       i=`expr $i + 1`
+    done
+}
+
+launch_fugu_apps() {
+    launch_app com.google.android.youtube.tv com.google.android.apps.youtube.tv.activity.TvGuideActivity > youtube-$model
+    getstats youtube-$model YouTube
+    launch_app com.google.android.play.games com.google.android.gms.games.pano.activity.PanoGamesOnboardHostActivity > games-$model
+    getstats games-$model Games
+    launch_app com.google.android.music com.android.music.activitymanagement.TopLevelActivity > music-$model
+    getstats music-$model Music
+}
+
+launch_phone_apps() {
+    launch_app com.android.chrome com.google.android.apps.chrome.Main > chrome-$model
+    getstats chrome-$model Chrome
+    launch_app com.google.android.GoogleCamera com.android.camera.CameraActivity > camera-$model
+    getstats camera-$model Camera
+    launch_app com.google.android.apps.maps com.google.android.maps.MapsActivity > maps-$model
+    getstats maps-$model Maps
+    launch_app com.google.android.youtube com.google.android.apps.youtube.app.WatchWhileActivity > youtube-$model
+    getstats youtube-$model YouTube
+}
+
+usage() {
+    echo 'Usage: app-launcher [-c|-v] -a|-b|-u num-iterations'
+    echo 'where num-iterations >= 100'
+    echo '-v (optional) for verbose stats dump'
+    echo '-a|-b|-u required:'
+    echo '        -a:all cores'
+    echo '        -b:only big cores'
+    echo '        -c:pagecached. Do not drop pagecache before each launch (not default)'
+    echo '        -h:Dump this help menu'
+    echo '        -u:user experience, no change to cpu/gpu frequencies or governors'
+    echo '        -a/-b locks CPU/GPU freqs to max, performance governor, thermal/perfd off'
+    echo '        -u runs with default device configs, as users would see it'
+    exit 1
+}
+
+#
+# The main() part of the script follows :
+#
+
+if [ $# -lt 2 ]; then
+    usage
+fi
+
+which computestats > /dev/null
+if [ $? != 0 ]; then
+    echo "ERROR: Please add computestats utiliy to your PATH"
+    exit 1
+fi
+
+which computestatsf > /dev/null
+if [ $? != 0 ]; then
+    echo "Error: Please add computestatsf utility to your PATH"
+    exit 1
+fi
+
+parseoptions $@
+
+adb root && sleep 2
+
+if [ $user_experience == false ]; then
+    # Important to stop the thermal-engine to prevent throttling while test is running
+    # and stop perfd
+    adb shell 'stop thermal-engine'
+    adb shell 'stop perfd'
+else
+    echo "User Experience: Default Configs. No changes to cpufreq settings"
+fi
+
+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
+
+system_bdev_set=false
+case $model in
+    marlin | sailfish)
+        if [ $user_experience == false ]; then
+            cpufreq_marlin_sailfish
+        fi
+       get_marlin_sailfish_devnames
+        ;;
+    angler)
+        if [ $user_experience == false ]; then
+            cpufreq_angler
+        fi
+        get_angler_devnames
+        ;;
+    fugu)
+        if [ $user_experience == false ]; then
+            cpufreq_fugu
+        fi
+        get_fugu_devnames
+        ;;
+    volantis | volantisg)
+        if [ $user_experience == false ]; then
+            cpufreq_volantis
+        fi
+        get_volantis_devnames
+        ;;
+    *)
+        echo Unknown Device $model
+        exit 1
+        ;;
+esac
+
+
+#
+# launch each app in turn
+#
+if [ $model == "fugu" ]; then
+    launch_fugu_apps
+else # Phone Apps
+    launch_phone_apps
+fi
diff --git a/app-launcher/computestats.c b/app-launcher/computestats.c
new file mode 100644 (file)
index 0000000..e081e06
--- /dev/null
@@ -0,0 +1,118 @@
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+
+char *pname;
+char *in_file;
+
+#define DATA_COUNT     (1024*1024)
+u_int64_t data_items[DATA_COUNT];
+
+int num_data_items = 0;
+
+#define BUFSIZE                1024
+char in_buf[BUFSIZE];
+
+static int
+compare_long(const void *p1, const void *p2)
+{
+       u_int64_t val1 = *(u_int64_t *)p1;
+       u_int64_t val2 = *(u_int64_t *)p2;
+
+       if (val1 == val2)
+               return 0;
+       if (val1 < val2)
+               return -1;
+       return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+       FILE *in_fp;
+       u_int64_t sum_x = 0;
+       u_int64_t sum_sq_x = 0;
+       u_int64_t mean;
+       double std_dev;
+       int i;
+       int one_sd = 0;
+       int two_sd = 0;
+       int three_sd = 0;
+       double one_sd_low, one_sd_high;
+       double two_sd_low, two_sd_high;
+       double three_sd_low, three_sd_high;
+
+       pname = argv[0];
+       if (argc == 1)
+               in_fp = stdin;
+       else {
+               in_file = argv[1];
+               in_fp = fopen(in_file, "r");
+       }
+       while (fgets(in_buf, BUFSIZE, in_fp)) {
+               if (num_data_items == DATA_COUNT) {
+                       fprintf(stderr,
+                               "DATA overflow, increase size of data_items array\n");
+                       exit(1);
+               }
+               sscanf(in_buf, "%ju", &data_items[num_data_items]);
+#if 0
+               printf("%lu\n", data_items[num_data_items++]);
+#endif
+               num_data_items++;
+       }
+       if (num_data_items == 0) {
+               fprintf(stderr, "Empty input file ?\n");
+               exit(1);
+       }
+#if 0
+       printf("Total items %lu\n", num_data_items);
+#endif
+       for (i = 0 ; i < num_data_items ; i++) {
+               sum_x += data_items[i];
+               sum_sq_x += data_items[i] * data_items[i];
+       }
+       mean = sum_x / num_data_items;
+       printf("\tMean %lu\n", mean);
+       std_dev = sqrt((sum_sq_x / num_data_items) - (mean * mean));
+       printf("\tStd Dev %.2f (%.2f%% of mean)\n",
+              std_dev, (std_dev * 100.0) / mean);
+       one_sd_low = mean - std_dev;
+       one_sd_high = mean + std_dev;
+       two_sd_low = mean - (2 * std_dev);
+       two_sd_high = mean + (2 * std_dev);
+       three_sd_low = mean - (3 * std_dev);
+       three_sd_high = mean + (3 * std_dev);
+       for (i = 0 ; i < num_data_items ; i++) {
+               if (data_items[i] >= one_sd_low &&
+                   data_items[i] <= one_sd_high)
+                       one_sd++;
+               if (data_items[i] >= two_sd_low &&
+                   data_items[i] <= two_sd_high)
+                       two_sd++;
+               if (data_items[i] >= three_sd_low &&
+                        data_items[i] <= three_sd_high)
+                       three_sd++;
+       }
+       printf("\tWithin 1 SD %.2f%%\n",
+              ((double)one_sd * 100) / num_data_items);
+       printf("\tWithin 2 SD %.2f%%\n",
+              ((double)two_sd * 100) / num_data_items);
+       printf("\tWithin 3 SD %.2f%%\n",
+              ((double)three_sd* 100) / num_data_items);
+       printf("\tOutside 3 SD %.2f%%\n",
+              ((double)(num_data_items - three_sd) * 100) / num_data_items);
+       /* Sort the data to get percentiles */
+       qsort(data_items, num_data_items, sizeof(u_int64_t), compare_long);
+       printf("\t50th percentile %lu\n", data_items[num_data_items / 2]);
+       printf("\t75th percentile %lu\n", data_items[(3 * num_data_items) / 4]);
+       printf("\t90th percentile %lu\n", data_items[(9 * num_data_items) / 10]);
+       printf("\t99th percentile %lu\n", data_items[(99 * num_data_items) / 100]);
+}
+
diff --git a/app-launcher/computestatsf.c b/app-launcher/computestatsf.c
new file mode 100644 (file)
index 0000000..2ec0b9d
--- /dev/null
@@ -0,0 +1,118 @@
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+
+char *pname;
+char *in_file;
+
+#define DATA_COUNT     (1024*1024)
+double data_items[DATA_COUNT];
+
+int num_data_items = 0;
+
+#define BUFSIZE                1024
+char in_buf[BUFSIZE];
+
+static int
+compare_double(const void *p1, const void *p2)
+{
+       double val1 = *(u_int64_t *)p1;
+       double val2 = *(u_int64_t *)p2;
+
+       if (val1 == val2)
+               return 0;
+       if (val1 < val2)
+               return -1;
+       return 1;
+}
+
+int
+main(int argc, char **argv)
+{
+       FILE *in_fp;
+       double sum_x = 0;
+       double sum_sq_x = 0;
+       double mean;
+       double std_dev;
+       int i;
+       int one_sd = 0;
+       int two_sd = 0;
+       int three_sd = 0;
+       double one_sd_low, one_sd_high;
+       double two_sd_low, two_sd_high;
+       double three_sd_low, three_sd_high;
+
+       pname = argv[0];
+       if (argc == 1)
+               in_fp = stdin;
+       else {
+               in_file = argv[1];
+               in_fp = fopen(in_file, "r");
+       }
+       while (fgets(in_buf, BUFSIZE, in_fp)) {
+               if (num_data_items == DATA_COUNT) {
+                       fprintf(stderr,
+                               "DATA overflow, increase size of data_items array\n");
+                       exit(1);
+               }
+               sscanf(in_buf, "%lf", &data_items[num_data_items]);
+#if 0
+               printf("%lf\n", data_items[num_data_items]);
+#endif
+               num_data_items++;
+       }
+       if (num_data_items == 0) {
+               fprintf(stderr, "Empty input file ?\n");
+               exit(1);
+       }
+#if 0
+       printf("Total items %lu\n", num_data_items);
+#endif
+       for (i = 0 ; i < num_data_items ; i++) {
+               sum_x += data_items[i];
+               sum_sq_x += data_items[i] * data_items[i];
+       }
+       mean = sum_x / num_data_items;
+       printf("\tMean %.4f\n", mean);
+       std_dev = sqrt((sum_sq_x / num_data_items) - (mean * mean));
+       printf("\tStd Dev %.4f (%.4f%% of mean)\n",
+              std_dev, (std_dev * 100.0) / mean);
+       one_sd_low = mean - std_dev;
+       one_sd_high = mean + std_dev;
+       two_sd_low = mean - (2 * std_dev);
+       two_sd_high = mean + (2 * std_dev);
+       three_sd_low = mean - (3 * std_dev);
+       three_sd_high = mean + (3 * std_dev);
+       for (i = 0 ; i < num_data_items ; i++) {
+               if (data_items[i] >= one_sd_low &&
+                   data_items[i] <= one_sd_high)
+                       one_sd++;
+               if (data_items[i] >= two_sd_low &&
+                   data_items[i] <= two_sd_high)
+                       two_sd++;
+               if (data_items[i] >= three_sd_low &&
+                        data_items[i] <= three_sd_high)
+                       three_sd++;
+       }
+       printf("\tWithin 1 SD %.2f%%\n",
+              ((double)one_sd * 100) / num_data_items);
+       printf("\tWithin 2 SD %.2f%%\n",
+              ((double)two_sd * 100) / num_data_items);
+       printf("\tWithin 3 SD %.2f%%\n",
+              ((double)three_sd* 100) / num_data_items);
+       printf("\tOutside 3 SD %.2f%%\n",
+              ((double)(num_data_items - three_sd) * 100) / num_data_items);
+       /* Sort the data to get percentiles */
+       qsort(data_items, num_data_items, sizeof(u_int64_t), compare_double);
+       printf("\t50th percentile %lf\n", data_items[num_data_items / 2]);
+       printf("\t75th percentile %lf\n", data_items[(3 * num_data_items) / 4]);
+       printf("\t90th percentile %lf\n", data_items[(9 * num_data_items) / 10]);
+       printf("\t99th percentile %lf\n", data_items[(99 * num_data_items) / 100]);
+}
+
index 3125d6e..3a5f8a0 100644 (file)
@@ -6,7 +6,7 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES := boot_control_copy.c bootinfo.c
 LOCAL_CFLAGS := -Wall -Wno-missing-field-initializers -Wno-unused-parameter
 LOCAL_C_INCLUDES := system/core/mkbootimg bootable/recovery
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libbase libcutils
 LOCAL_STATIC_LIBRARIES := libfs_mgr
 
 LOCAL_MODULE_RELATIVE_PATH := hw
index 2b3865e..6dda28f 100644 (file)
@@ -83,7 +83,7 @@ static std::function<void(CommandResult)> generate_callback(CommandResult *crp)
     };
 }
 
-static int handle_return(Return<void> ret, CommandResult cr, const char* errStr) {
+static int handle_return(const Return<void> &ret, CommandResult cr, const char* errStr) {
     if (!ret.isOk()) {
         fprintf(stderr, errStr, ret.description().c_str());
         return EX_SOFTWARE;
@@ -117,7 +117,7 @@ static int do_set_slot_as_unbootable(sp<IBootControl> module,
     return handle_return(ret, cr, "Error setting slot as unbootable: %s\n");
 }
 
-static int handle_return(Return<BoolResult> ret, const char* errStr) {
+static int handle_return(const Return<BoolResult> &ret, const char* errStr) {
     if (!ret.isOk()) {
         fprintf(stderr, errStr, ret.description().c_str());
         return EX_SOFTWARE;
index 0042caf..375143b 100644 (file)
@@ -105,6 +105,7 @@ int main(int argc, char *argv[]) {
     }
 
     cpu_count = get_cpu_count();
+    if (cpu_count < 1) die("Unexpected cpu count\n");
 
     old_cpus = malloc(sizeof(struct cpu_info) * cpu_count);
     if (!old_cpus) die("Could not allocate struct cpu_info\n");
@@ -112,7 +113,9 @@ int main(int argc, char *argv[]) {
     if (!new_cpus) die("Could not allocate struct cpu_info\n");
 
     for (i = 0; i < cpu_count; i++) {
-        old_cpus[i].freq_count = new_cpus[i].freq_count = get_freq_scales_count(i);
+        freq_count = get_freq_scales_count(i);
+        if (freq_count < 1) die("Unexpected frequency scale count\n");
+        old_cpus[i].freq_count = new_cpus[i].freq_count = freq_count;
         new_cpus[i].freqs = malloc(sizeof(struct freq_info) * new_cpus[i].freq_count);
         if (!new_cpus[i].freqs) die("Could not allocate struct freq_info\n");
         old_cpus[i].freqs = malloc(sizeof(struct freq_info) * old_cpus[i].freq_count);
index 890082e..de85f7e 100644 (file)
@@ -48,8 +48,16 @@ struct ext4_encryption_policy {
 
 #define EXT4_ENCRYPTION_MODE_AES_256_XTS    1
 #define EXT4_ENCRYPTION_MODE_AES_256_CTS    4
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH    126
 #define EXT4_ENCRYPTION_MODE_PRIVATE        127
 
+#define EXT4_POLICY_FLAGS_PAD_4         0x00
+#define EXT4_POLICY_FLAGS_PAD_8         0x01
+#define EXT4_POLICY_FLAGS_PAD_16        0x02
+#define EXT4_POLICY_FLAGS_PAD_32        0x03
+#define EXT4_POLICY_FLAGS_PAD_MASK      0x03
+#define EXT4_POLICY_FLAGS_VALID         0x03
+
 // ext4enc:TODO Get value from somewhere sensible
 #define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
 #define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
@@ -100,8 +108,25 @@ static bool is_dir_empty(const char *dirname, bool *is_empty)
     return true;
 }
 
+static uint8_t e4crypt_get_policy_flags(int filenames_encryption_mode) {
+
+    // With HEH, pad filenames with zeroes to the next 16-byte boundary.  This
+    // is not required, but it's more secure (helps hide the length of
+    // filenames), makes the inputs evenly divisible into blocks which is more
+    // efficient for encryption and decryption, and we had the opportunity to
+    // make a breaking change when introducing a new mode anyway.
+    if (filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_HEH) {
+        return EXT4_POLICY_FLAGS_PAD_16;
+    }
+
+    // Default flags (4-byte padding) for CTS
+    return EXT4_POLICY_FLAGS_PAD_4;
+}
+
 static bool e4crypt_policy_set(const char *directory, const char *policy,
-                               size_t policy_length, int contents_encryption_mode) {
+                               size_t policy_length,
+                               int contents_encryption_mode,
+                               int filenames_encryption_mode) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
         LOG(ERROR) << "Policy wrong length: " << policy_length;
         return false;
@@ -115,8 +140,8 @@ static bool e4crypt_policy_set(const char *directory, const char *policy,
     ext4_encryption_policy eep;
     eep.version = 0;
     eep.contents_encryption_mode = contents_encryption_mode;
-    eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
-    eep.flags = 0;
+    eep.filenames_encryption_mode = filenames_encryption_mode;
+    eep.flags = e4crypt_get_policy_flags(filenames_encryption_mode);
     memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
     if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
         PLOG(ERROR) << "Failed to set encryption policy for " << directory;
@@ -132,7 +157,9 @@ static bool e4crypt_policy_set(const char *directory, const char *policy,
 }
 
 static bool e4crypt_policy_get(const char *directory, char *policy,
-                               size_t policy_length, int contents_encryption_mode) {
+                               size_t policy_length,
+                               int contents_encryption_mode,
+                               int filenames_encryption_mode) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
         LOG(ERROR) << "Policy wrong length: " << policy_length;
         return false;
@@ -155,8 +182,9 @@ static bool e4crypt_policy_get(const char *directory, char *policy,
 
     if ((eep.version != 0)
             || (eep.contents_encryption_mode != contents_encryption_mode)
-            || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
-            || (eep.flags != 0)) {
+            || (eep.filenames_encryption_mode != filenames_encryption_mode)
+            || (eep.flags !=
+                e4crypt_get_policy_flags(filenames_encryption_mode))) {
         LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
         return false;
     }
@@ -166,14 +194,17 @@ static bool e4crypt_policy_get(const char *directory, char *policy,
 }
 
 static bool e4crypt_policy_check(const char *directory, const char *policy,
-                                 size_t policy_length, int contents_encryption_mode) {
+                                 size_t policy_length,
+                                 int contents_encryption_mode,
+                                 int filenames_encryption_mode) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
         LOG(ERROR) << "Policy wrong length: " << policy_length;
         return false;
     }
     char existing_policy[EXT4_KEY_DESCRIPTOR_SIZE];
     if (!e4crypt_policy_get(directory, existing_policy, EXT4_KEY_DESCRIPTOR_SIZE,
-                            contents_encryption_mode)) return false;
+                            contents_encryption_mode,
+                            filenames_encryption_mode)) return false;
     char existing_policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
 
     policy_to_hex(existing_policy, existing_policy_hex);
@@ -191,14 +222,30 @@ static bool e4crypt_policy_check(const char *directory, const char *policy,
 }
 
 int e4crypt_policy_ensure(const char *directory, const char *policy,
-                          size_t policy_length, const char* contents_encryption_mode) {
-    int mode = 0;
-    if (!strcmp(contents_encryption_mode, "software")) {
-        mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+                          size_t policy_length,
+                          const char *contents_encryption_mode,
+                          const char *filenames_encryption_mode) {
+    int contents_mode = 0;
+    int filenames_mode = 0;
+
+    if (!strcmp(contents_encryption_mode, "software") ||
+        !strcmp(contents_encryption_mode, "aes-256-xts")) {
+        contents_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
     } else if (!strcmp(contents_encryption_mode, "ice")) {
-        mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+        contents_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+    } else {
+        LOG(ERROR) << "Invalid file contents encryption mode: "
+                   << contents_encryption_mode;
+        return -1;
+    }
+
+    if (!strcmp(filenames_encryption_mode, "aes-256-cts")) {
+        filenames_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+    } else if (!strcmp(filenames_encryption_mode, "aes-256-heh")) {
+        filenames_mode = EXT4_ENCRYPTION_MODE_AES_256_HEH;
     } else {
-        LOG(ERROR) << "Invalid encryption mode";
+        LOG(ERROR) << "Invalid file names encryption mode: "
+                   << filenames_encryption_mode;
         return -1;
     }
 
@@ -206,10 +253,10 @@ int e4crypt_policy_ensure(const char *directory, const char *policy,
     if (!is_dir_empty(directory, &is_empty)) return -1;
     if (is_empty) {
         if (!e4crypt_policy_set(directory, policy, policy_length,
-                                mode)) return -1;
+                                contents_mode, filenames_mode)) return -1;
     } else {
         if (!e4crypt_policy_check(directory, policy, policy_length,
-                                  mode)) return -1;
+                                  contents_mode, filenames_mode)) return -1;
     }
     return 0;
 }
index e219d2f..55a1c1c 100644 (file)
@@ -28,6 +28,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <logwrap/logwrap.h>
@@ -117,14 +118,23 @@ int e4crypt_set_directory_policy(const char* dir)
     }
 
     auto type_filename = std::string("/data") + e4crypt_key_mode;
-    std::string contents_encryption_mode;
-    if (!android::base::ReadFileToString(type_filename, &contents_encryption_mode)) {
+    std::string modestring;
+    if (!android::base::ReadFileToString(type_filename, &modestring)) {
         LOG(ERROR) << "Cannot read mode";
     }
 
+    std::vector<std::string> modes = android::base::Split(modestring, ":");
+
+    if (modes.size() < 1 || modes.size() > 2) {
+        LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+        return -1;
+    }
+
     LOG(INFO) << "Setting policy on " << dir;
     int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.length(),
-                                       contents_encryption_mode.c_str());
+                                       modes[0].c_str(),
+                                       modes.size() >= 2 ?
+                                            modes[1].c_str() : "aes-256-cts");
     if (result) {
         LOG(ERROR) << android::base::StringPrintf(
             "Setting %02x%02x%02x%02x policy on %s failed!",
index 2be0bec..d410ccf 100644 (file)
@@ -25,9 +25,10 @@ __BEGIN_DECLS
 
 bool e4crypt_is_native();
 
-int e4crypt_policy_ensure(const char *directory,
-                          const char* policy, size_t policy_length,
-                          const char* contents_encryption_mode);
+int e4crypt_policy_ensure(const char *directory, const char *policy,
+                          size_t policy_length,
+                          const char *contents_encryption_mode,
+                          const char *filenames_encryption_mode);
 
 static const char* e4crypt_unencrypted_folder = "/unencrypted";
 static const char* e4crypt_key_ref = "/unencrypted/ref";
index c558b87..44e9481 100644 (file)
@@ -37,6 +37,12 @@ int make_ext4fs_sparse_fd(int fd, long long len,
 int make_ext4fs_sparse_fd_directory(int fd, long long len,
                 const char *mountpoint, struct selabel_handle *sehnd,
                 const char *directory);
+int make_ext4fs_sparse_fd_align(int fd, long long len,
+                const char *mountpoint, struct selabel_handle *sehnd,
+                unsigned eraseblk, unsigned logicalblk);
+int make_ext4fs_sparse_fd_directory_align(int fd, long long len,
+                const char *mountpoint, struct selabel_handle *sehnd,
+                const char *directory, unsigned eraseblk, unsigned logicalblk);
 
 #ifdef __cplusplus
 }
index ec093f8..b84db9b 100644 (file)
@@ -426,15 +426,32 @@ void reset_ext4fs_info() {
 int make_ext4fs_sparse_fd(int fd, long long len,
                                const char *mountpoint, struct selabel_handle *sehnd)
 {
-       return make_ext4fs_sparse_fd_directory(fd, len, mountpoint, sehnd, NULL);
+       return make_ext4fs_sparse_fd_align(fd, len, mountpoint, sehnd, 0, 0);
+}
+
+int make_ext4fs_sparse_fd_align(int fd, long long len,
+                               const char *mountpoint, struct selabel_handle *sehnd,
+                               unsigned eraseblk, unsigned logicalblk)
+{
+       return make_ext4fs_sparse_fd_directory_align(fd, len, mountpoint, sehnd, NULL,
+                                                               eraseblk, logicalblk);
 }
 
 int make_ext4fs_sparse_fd_directory(int fd, long long len,
                                const char *mountpoint, struct selabel_handle *sehnd,
                                const char *directory)
 {
+       return make_ext4fs_sparse_fd_directory_align(fd, len, mountpoint, sehnd, directory, 0, 0);
+}
+
+int make_ext4fs_sparse_fd_directory_align(int fd, long long len,
+                               const char *mountpoint, struct selabel_handle *sehnd,
+                               const char *directory, unsigned eraseblk, unsigned logicalblk)
+{
        reset_ext4fs_info();
        info.len = len;
+       info.flash_erase_block_size = eraseblk;
+       info.flash_logical_block_size = logicalblk;
 
        return make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
                                                                0, 1, 0, 0, 0,
@@ -447,6 +464,13 @@ int make_ext4fs(const char *filename, long long len,
        return make_ext4fs_directory(filename, len, mountpoint, sehnd, NULL);
 }
 
+int make_ext4fs_directory(const char *filename, long long len,
+                                                 const char *mountpoint, struct selabel_handle *sehnd,
+                                                 const char *directory)
+{
+       return make_ext4fs_directory_align(filename, len, mountpoint, sehnd, directory, 0, 0);
+}
+
 int make_ext4fs_directory_align(const char *filename, long long len,
                                                  const char *mountpoint, struct selabel_handle *sehnd,
                                                  const char *directory, unsigned eraseblk,
@@ -474,30 +498,6 @@ int make_ext4fs_directory_align(const char *filename, long long len,
        return status;
 }
 
-int make_ext4fs_directory(const char *filename, long long len,
-                                                 const char *mountpoint, struct selabel_handle *sehnd,
-                                                 const char *directory)
-{
-       int fd;
-       int status;
-
-       reset_ext4fs_info();
-       info.len = len;
-
-       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
-       if (fd < 0) {
-               error_errno("open");
-               return EXIT_FAILURE;
-       }
-
-       status = make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
-                                                                 0, 0, 0, 1, 0,
-                                                                 sehnd, 0, -1, NULL, NULL, NULL);
-       close(fd);
-
-       return status;
-}
-
 /* return a newly-malloc'd string that is a copy of str.  The new string
    is guaranteed to have a trailing slash.  If absolute is true, the new string
    is also guaranteed to have a leading slash.
index 8dbbcb5..e6fca45 100755 (executable)
@@ -42,6 +42,9 @@ MOUNT_POINT=$4
 SIZE=$5
 shift; shift; shift; shift; shift
 
+# selinux requires ext_attr.
+MKE2FS_OPTS+="-O ext_attr "
+
 if [ "$1" = "-j" ]; then
   if [ "$2" = "0" ]; then
     MKE2FS_OPTS+="-O ^has_journal"
index 679a509..2e9229b 100644 (file)
@@ -202,7 +202,7 @@ class PerfProfdRunner {
   {
     std::string semaphore(test_dir);
     semaphore += "/" SEMAPHORE_FILENAME;
-    close(open(semaphore.c_str(), O_WRONLY|O_CREAT));
+    close(open(semaphore.c_str(), O_WRONLY|O_CREAT, 0600));
   }
 
   void write_processed_file(int start_seq, int end_seq)
index 67eb0a0..2a39b59 100644 (file)
@@ -110,6 +110,7 @@ libsimpleperf_src_files_linux := \
   environment.cpp \
   event_fd.cpp \
   event_selection_set.cpp \
+  InplaceSamplerClient.cpp \
   IOEventLoop.cpp \
   perf_clock.cpp \
   record_file_writer.cpp \
@@ -351,7 +352,7 @@ libsimpleperf_cts_test_src_files := \
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_MODULE := libsimpleperf_cts_test
-LOCAL_CPPFLAGS := $(simpleperf_cppflags_target)
+LOCAL_CPPFLAGS := $(simpleperf_cppflags_target) -DRUN_IN_APP_CONTEXT
 LOCAL_SRC_FILES := $(libsimpleperf_cts_test_src_files)
 LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
 LOCAL_MULTILIB := both
index 44de289..ce25928 100644 (file)
@@ -40,6 +40,7 @@ struct IOEvent {
 IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false) {}
 
 IOEventLoop::~IOEventLoop() {
+  events_.clear();
   if (ebase_ != nullptr) {
     event_base_free(ebase_);
   }
diff --git a/simpleperf/InplaceSamplerClient.cpp b/simpleperf/InplaceSamplerClient.cpp
new file mode 100644 (file)
index 0000000..5a78861
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InplaceSamplerClient.h"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "environment.h"
+#include "utils.h"
+
+static constexpr uint64_t EVENT_ID_FOR_INPLACE_SAMPLER = ULONG_MAX;
+
+std::unique_ptr<InplaceSamplerClient> InplaceSamplerClient::Create(const perf_event_attr& attr,
+                                                                   pid_t pid,
+                                                                   const std::set<pid_t>& tids) {
+  if (pid == -1) {
+    LOG(ERROR) << "inplace-sampler can't monitor system wide events.";
+    return nullptr;
+  }
+  std::unique_ptr<InplaceSamplerClient> sampler(new InplaceSamplerClient(attr, pid, tids));
+  if (!sampler->ConnectServer()) {
+    return nullptr;
+  }
+  if (!sampler->StartProfiling()) {
+    return nullptr;
+  }
+  return sampler;
+}
+
+InplaceSamplerClient::InplaceSamplerClient(const perf_event_attr& attr, pid_t pid,
+                                           const std::set<pid_t>& tids)
+    : attr_(attr), pid_(pid), tids_(tids), closed_(false) {
+}
+
+uint64_t InplaceSamplerClient::Id() const {
+  return EVENT_ID_FOR_INPLACE_SAMPLER;
+}
+
+bool InplaceSamplerClient::ConnectServer() {
+  return true;
+}
+
+bool InplaceSamplerClient::StartProfiling() {
+  return true;
+}
+
+bool InplaceSamplerClient::StartPolling(IOEventLoop& loop,
+                                        const std::function<bool(Record*)>& record_callback,
+                                        const std::function<bool()>& close_callback) {
+  record_callback_ = record_callback;
+  close_callback_ = close_callback;
+  auto callback = [this]() {
+    // Fake records for testing.
+    uint64_t time = GetSystemClock();
+    CommRecord comm_r(attr_, pid_, pid_, "fake_comm", Id(), time);
+    if (!record_callback_(&comm_r)) {
+      return false;
+    }
+    MmapRecord mmap_r(attr_, false, pid_, pid_, 0x1000, 0x1000, 0x0, "fake_elf", Id(), time);
+    if (!record_callback_(&mmap_r)) {
+      return false;
+    }
+    std::vector<uint64_t> ips(1, 0x1000);
+    SampleRecord r(attr_, Id(), ips[0], pid_, pid_, time, 0, 1, ips);
+    if (!record_callback_(&r)) {
+      return false;
+    }
+    closed_ = true;
+    return close_callback_();
+  };
+  timeval duration;
+  duration.tv_sec = 0;
+  duration.tv_usec = 1000;
+  return loop.AddPeriodicEvent(duration, callback);
+}
diff --git a/simpleperf/InplaceSamplerClient.h b/simpleperf/InplaceSamplerClient.h
new file mode 100644 (file)
index 0000000..0c606bb
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_PERF_INPLACE_SAMPLER_CLIENT_H_
+#define SIMPLE_PERF_INPLACE_SAMPLER_CLIENT_H_
+
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "event_attr.h"
+#include "record.h"
+#include "UnixSocket.h"
+
+class InplaceSamplerClient {
+ public:
+  static std::unique_ptr<InplaceSamplerClient> Create(const perf_event_attr& attr, pid_t pid,
+                                                      const std::set<pid_t>& tids);
+  uint64_t Id() const;
+  bool IsClosed() {
+    return closed_;
+  }
+  bool StartPolling(IOEventLoop& loop, const std::function<bool(Record*)>& record_callback,
+                    const std::function<bool()>& close_callback);
+  bool StopProfiling();
+
+ private:
+  InplaceSamplerClient(const perf_event_attr& attr, pid_t pid, const std::set<pid_t>& tids);
+  bool ConnectServer();
+  bool StartProfiling();
+
+  const perf_event_attr attr_;
+  const pid_t pid_;
+  const std::set<pid_t> tids_;
+  std::function<bool(Record*)> record_callback_;
+  std::function<bool()> close_callback_;
+  bool closed_;
+};
+
+#endif  // SIMPLE_PERF_INPLACE_SAMPLER_CLIENT_H_
index 273a803..0248aa9 100644 (file)
@@ -36,7 +36,7 @@ static void PrintEventTypesOfType(uint32_t type, const std::string& type_name,
       // Exclude kernel to list supported events even when
       // /proc/sys/kernel/perf_event_paranoid is 2.
       attr.exclude_kernel = 1;
-      if (IsEventAttrSupportedByKernel(attr)) {
+      if (IsEventAttrSupported(attr)) {
         printf("  %s\n", event_type.name.c_str());
       }
     }
@@ -65,6 +65,7 @@ bool ListCommand::Run(const std::vector<std::string>& args) {
       {"sw", {PERF_TYPE_SOFTWARE, "software events"}},
       {"cache", {PERF_TYPE_HW_CACHE, "hw-cache events"}},
       {"tracepoint", {PERF_TYPE_TRACEPOINT, "tracepoint events"}},
+      {"user-space-sampler", {SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS, "user-space samplers"}},
   };
 
   std::vector<std::string> names;
index b9dfff0..1cbb86c 100644 (file)
@@ -259,6 +259,13 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     if (workload != nullptr) {
       event_selection_set_.AddMonitoredProcesses({workload->GetPid()});
       event_selection_set_.SetEnableOnExec(true);
+      if (event_selection_set_.HasInplaceSampler()) {
+        // Start worker early, because the worker process has to setup inplace-sampler server
+        // before we try to connect it.
+        if (!workload->Start()) {
+          return false;
+        }
+      }
     } else {
       LOG(ERROR)
           << "No threads to monitor. Try `simpleperf help record` for help";
@@ -282,9 +289,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     return false;
   }
 
-  // 5. Create IOEventLoop and add read/signal/periodic Events.
-  IOEventLoop loop;
-  event_selection_set_.SetIOEventLoop(loop);
+  // 5. Add read/signal/periodic Events.
   auto callback =
       std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1);
   if (!event_selection_set_.PrepareToReadMmapEventData(callback)) {
@@ -296,13 +301,14 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
   if (need_to_check_targets && !event_selection_set_.StopWhenNoMoreTargets()) {
     return false;
   }
-  if (!loop.AddSignalEvents({SIGCHLD, SIGINT, SIGTERM, SIGHUP},
-                            [&]() { return loop.ExitLoop(); })) {
+  IOEventLoop* loop = event_selection_set_.GetIOEventLoop();
+  if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM, SIGHUP},
+                             [&]() { return loop->ExitLoop(); })) {
     return false;
   }
   if (duration_in_sec_ != 0) {
-    if (!loop.AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
-                               [&]() { return loop.ExitLoop(); })) {
+    if (!loop->AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
+                                [&]() { return loop->ExitLoop(); })) {
       return false;
     }
   }
@@ -312,10 +318,10 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
   start_sampling_time_in_ns_ = GetPerfClock();
   LOG(VERBOSE) << "start_sampling_time is " << start_sampling_time_in_ns_
                << " ns";
-  if (workload != nullptr && !workload->Start()) {
+  if (workload != nullptr && !workload->IsStarted() && !workload->Start()) {
     return false;
   }
-  if (!loop.RunLoop()) {
+  if (!loop->RunLoop()) {
     return false;
   }
   if (!event_selection_set_.FinishReadMmapEventData()) {
index 35e871f..c3fb8f2 100644 (file)
@@ -385,3 +385,14 @@ TEST(record_cmd, stop_when_no_more_targets) {
   while (tid == 0);
   ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-t", std::to_string(tid)}));
 }
+
+TEST(record_cmd, donot_stop_when_having_targets) {
+  std::vector<std::unique_ptr<Workload>> workloads;
+  CreateProcesses(1, &workloads);
+  std::string pid = std::to_string(workloads[0]->GetPid());
+  uint64_t start_time_in_ns = GetSystemClock();
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-p", pid, "--duration", "3"}));
+  uint64_t end_time_in_ns = GetSystemClock();
+  ASSERT_GT(end_time_in_ns - start_time_in_ns, static_cast<uint64_t>(2e9));
+}
index f445bd5..9a5c23b 100644 (file)
@@ -45,6 +45,7 @@ class ReportCommandTest : public ::testing::Test {
       const std::string& perf_data,
       const std::vector<std::string>& add_args = std::vector<std::string>()) {
     success = false;
+    TemporaryFile tmp_file;
     std::vector<std::string> args = {
         "-i", perf_data, "--symfs", GetTestDataDir(), "-o", tmp_file.path};
     args.insert(args.end(), add_args.begin(), add_args.end());
@@ -63,7 +64,6 @@ class ReportCommandTest : public ::testing::Test {
     success = true;
   }
 
-  TemporaryFile tmp_file;
   std::string content;
   std::vector<std::string> lines;
   bool success;
index 423fbff..cdd5593 100644 (file)
@@ -383,9 +383,7 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
     fp = fp_holder.get();
   }
 
-  // 4. Create IOEventLoop and add signal/periodic Events.
-  IOEventLoop loop;
-  event_selection_set_.SetIOEventLoop(loop);
+  // 4. Add signal/periodic Events.
   std::chrono::time_point<std::chrono::steady_clock> start_time;
   std::vector<CountersInfo> counters;
   if (system_wide_collection_ || (!cpus_.empty() && cpus_[0] != -1)) {
@@ -396,13 +394,14 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
   if (need_to_check_targets && !event_selection_set_.StopWhenNoMoreTargets()) {
     return false;
   }
-  if (!loop.AddSignalEvents({SIGCHLD, SIGINT, SIGTERM, SIGHUP},
-                            [&]() { return loop.ExitLoop(); })) {
+  IOEventLoop* loop = event_selection_set_.GetIOEventLoop();
+  if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM, SIGHUP},
+                             [&]() { return loop->ExitLoop(); })) {
     return false;
   }
   if (duration_in_sec_ != 0) {
-    if (!loop.AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
-                               [&]() { return loop.ExitLoop(); })) {
+    if (!loop->AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
+                                [&]() { return loop->ExitLoop(); })) {
       return false;
     }
   }
@@ -422,8 +421,8 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
   };
 
   if (interval_in_ms_ != 0) {
-    if (!loop.AddPeriodicEvent(SecondToTimeval(interval_in_ms_ / 1000.0),
-                               print_counters)) {
+    if (!loop->AddPeriodicEvent(SecondToTimeval(interval_in_ms_ / 1000.0),
+                                print_counters)) {
       return false;
     }
   }
@@ -433,7 +432,7 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
   if (workload != nullptr && !workload->Start()) {
     return false;
   }
-  if (!loop.RunLoop()) {
+  if (!loop->RunLoop()) {
     return false;
   }
 
@@ -548,7 +547,7 @@ bool StatCommand::AddDefaultMeasuredEventTypes() {
     // supported by the kernel.
     const EventType* type = FindEventTypeByName(name);
     if (type != nullptr &&
-        IsEventAttrSupportedByKernel(CreateDefaultPerfEventAttr(*type))) {
+        IsEventAttrSupported(CreateDefaultPerfEventAttr(*type))) {
       if (!event_selection_set_.AddEventType(name)) {
         return false;
       }
index 23a6bec..66dbd0b 100644 (file)
@@ -492,7 +492,7 @@ int main(int argc, char** argv) {
       verbose_mode = true;
     }
   }
-  InitLogging(argv, android::base::StderrLogger);
+  android::base::InitLogging(argv, android::base::StderrLogger);
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
index 351d88a..40603fe 100644 (file)
@@ -97,7 +97,7 @@ bool Dso::SetSymFsDir(const std::string& symfs_dir) {
     if (dirname.back() != '/') {
       dirname.push_back('/');
     }
-    if (GetEntriesInDir(symfs_dir).empty()) {
+    if (!IsDir(symfs_dir)) {
       LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
       return false;
     }
index 08cf98f..b78da4c 100644 (file)
@@ -260,7 +260,12 @@ bool EventFd::StartPolling(IOEventLoop& loop,
 
 bool EventFd::StopPolling() { return IOEventLoop::DelEvent(ioevent_ref_); }
 
-bool IsEventAttrSupportedByKernel(perf_event_attr attr) {
-  auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, nullptr, false);
+bool IsEventAttrSupported(const perf_event_attr& attr) {
+  if (attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
+      attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
+    // User space samplers don't need kernel support.
+    return true;
+  }
+  std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, getpid(), -1, nullptr, false);
   return event_fd != nullptr;
 }
index aaba0ef..f1ddb55 100644 (file)
@@ -126,6 +126,6 @@ class EventFd {
   DISALLOW_COPY_AND_ASSIGN(EventFd);
 };
 
-bool IsEventAttrSupportedByKernel(perf_event_attr attr);
+bool IsEventAttrSupported(const perf_event_attr& attr);
 
 #endif  // SIMPLE_PERF_EVENT_FD_H_
index 038997f..bc1f5a8 100644 (file)
@@ -36,7 +36,7 @@ bool IsBranchSamplingSupported() {
   perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
   attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
   attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
-  return IsEventAttrSupportedByKernel(attr);
+  return IsEventAttrSupported(attr);
 }
 
 bool IsDwarfCallChainSamplingSupported() {
@@ -50,7 +50,7 @@ bool IsDwarfCallChainSamplingSupported() {
   attr.exclude_callchain_user = 1;
   attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
   attr.sample_stack_user = 8192;
-  return IsEventAttrSupportedByKernel(attr);
+  return IsEventAttrSupported(attr);
 }
 
 bool EventSelectionSet::BuildAndCheckEventSelection(
@@ -78,9 +78,9 @@ bool EventSelectionSet::BuildAndCheckEventSelection(
   selection->event_attr.exclude_host = event_type->exclude_host;
   selection->event_attr.exclude_guest = event_type->exclude_guest;
   selection->event_attr.precise_ip = event_type->precise_ip;
-  if (!IsEventAttrSupportedByKernel(selection->event_attr)) {
+  if (!IsEventAttrSupported(selection->event_attr)) {
     LOG(ERROR) << "Event type '" << event_type->name
-               << "' is not supported by the kernel";
+               << "' is not supported on the device";
     return false;
   }
   selection->event_fds.clear();
@@ -129,6 +129,18 @@ std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const {
   return result;
 }
 
+bool EventSelectionSet::HasInplaceSampler() const {
+  for (const auto& group : groups_) {
+    for (const auto& sel : group) {
+      if (sel.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
+          sel.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const {
   std::vector<EventAttrWithId> result;
   for (const auto& group : groups_) {
@@ -138,6 +150,9 @@ std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const {
       for (const auto& fd : selection.event_fds) {
         attr_id.ids.push_back(fd->Id());
       }
+      if (!selection.inplace_samplers.empty()) {
+        attr_id.ids.push_back(selection.inplace_samplers[0]->Id());
+      }
       result.push_back(attr_id);
     }
   }
@@ -347,12 +362,24 @@ bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group,
   return true;
 }
 
-static std::set<pid_t> PrepareThreads(const std::set<pid_t>& processes,
-                                      const std::set<pid_t>& threads) {
-  std::set<pid_t> result = threads;
-  for (const auto& pid : processes) {
+static std::map<pid_t, std::set<pid_t>> PrepareThreads(const std::set<pid_t>& processes,
+                                                       const std::set<pid_t>& threads) {
+  std::map<pid_t, std::set<pid_t>> result;
+  for (auto& pid : processes) {
     std::vector<pid_t> tids = GetThreadsInProcess(pid);
-    result.insert(tids.begin(), tids.end());
+    std::set<pid_t>& threads_in_process = result[pid];
+    threads_in_process.insert(tids.begin(), tids.end());
+  }
+  for (auto& tid : threads) {
+    // tid = -1 means monitoring all threads.
+    if (tid == -1) {
+      result[-1].insert(-1);
+    } else {
+      pid_t pid;
+      if (GetProcessForThread(tid, &pid)) {
+        result[pid].insert(tid);
+      }
+    }
   }
   return result;
 }
@@ -367,26 +394,56 @@ bool EventSelectionSet::OpenEventFiles(const std::vector<int>& on_cpus) {
   } else {
     cpus = GetOnlineCpus();
   }
-  std::set<pid_t> threads = PrepareThreads(processes_, threads_);
+  std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_);
   for (auto& group : groups_) {
-    for (const auto& tid : threads) {
-      size_t success_cpu_count = 0;
-      std::string failed_event_type;
-      for (const auto& cpu : cpus) {
-        if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
-          success_cpu_count++;
+    if (IsUserSpaceSamplerGroup(group)) {
+      if (!OpenUserSpaceSamplersOnGroup(group, process_map)) {
+        return false;
+      }
+    } else {
+      for (const auto& pair : process_map) {
+        for (const auto& tid : pair.second) {
+          size_t success_cpu_count = 0;
+          std::string failed_event_type;
+          for (const auto& cpu : cpus) {
+            if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
+              success_cpu_count++;
+            }
+          }
+          // As the online cpus can be enabled or disabled at runtime, we may not
+          // open event file for all cpus successfully. But we should open at
+          // least one cpu successfully.
+          if (success_cpu_count == 0) {
+            PLOG(ERROR) << "failed to open perf event file for event_type "
+                        << failed_event_type << " for "
+                        << (tid == -1 ? "all threads" : "thread " + std::to_string(tid))
+                        << " on all cpus";
+            return false;
+          }
         }
       }
-      // As the online cpus can be enabled or disabled at runtime, we may not
-      // open event file for all cpus successfully. But we should open at
-      // least one cpu successfully.
-      if (success_cpu_count == 0) {
-        PLOG(ERROR) << "failed to open perf event file for event_type "
-                    << failed_event_type << " for "
-                    << (tid == -1 ? "all threads"
-                                  : "thread " + std::to_string(tid))
-                    << " on all cpus";
-        return false;
+    }
+  }
+  return true;
+}
+
+bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) {
+  return group.size() == 1 && group[0].event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS;
+}
+
+bool EventSelectionSet::OpenUserSpaceSamplersOnGroup(EventSelectionGroup& group,
+    const std::map<pid_t, std::set<pid_t>>& process_map) {
+  CHECK_EQ(group.size(), 1u);
+  for (auto& selection : group) {
+    if (selection.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
+        selection.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
+      for (auto& pair : process_map) {
+        std::unique_ptr<InplaceSamplerClient> sampler = InplaceSamplerClient::Create(
+            selection.event_attr, pair.first, pair.second);
+        if (sampler == nullptr) {
+          return false;
+        }
+        selection.inplace_samplers.push_back(std::move(sampler));
       }
     }
   }
@@ -479,6 +536,12 @@ bool EventSelectionSet::PrepareToReadMmapEventData(const std::function<bool(Reco
           }
         }
       }
+      for (auto& sampler : selection.inplace_samplers) {
+        if (!sampler->StartPolling(*loop_, callback,
+                                   [&] { return CheckMonitoredTargets(); })) {
+          return false;
+        }
+      }
     }
   }
 
@@ -518,6 +581,9 @@ bool EventSelectionSet::ReadMmapEventData() {
     }
   }
 
+  if (head_size == 0) {
+    return true;
+  }
   if (head_size == 1) {
     // Only one buffer has data, process it directly.
     std::vector<std::unique_ptr<Record>> records =
@@ -645,17 +711,21 @@ bool EventSelectionSet::HandleCpuOfflineEvent(int cpu) {
 bool EventSelectionSet::HandleCpuOnlineEvent(int cpu) {
   // We need to start profiling when opening new event files.
   SetEnableOnExec(false);
-  std::set<pid_t> threads = PrepareThreads(processes_, threads_);
+  std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_);
   for (auto& group : groups_) {
-    for (const auto& tid : threads) {
-      std::string failed_event_type;
-      if (!OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
-        // If failed to open event files, maybe the cpu has been offlined.
-        PLOG(WARNING) << "failed to open perf event file for event_type "
-                      << failed_event_type << " for "
-                      << (tid == -1 ? "all threads"
-                                    : "thread " + std::to_string(tid))
-                      << " on cpu " << cpu;
+    if (IsUserSpaceSamplerGroup(group)) {
+      continue;
+    }
+    for (const auto& pair : process_map) {
+      for (const auto& tid : pair.second) {
+        std::string failed_event_type;
+        if (!OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
+          // If failed to open event files, maybe the cpu has been offlined.
+          PLOG(WARNING) << "failed to open perf event file for event_type "
+                        << failed_event_type << " for "
+                        << (tid == -1 ? "all threads" : "thread " + std::to_string(tid))
+                        << " on cpu " << cpu;
+        }
       }
     }
   }
@@ -723,6 +793,9 @@ bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) {
 }
 
 bool EventSelectionSet::CheckMonitoredTargets() {
+  if (!HasSampler()) {
+    return loop_->ExitLoop();
+  }
   for (const auto& tid : threads_) {
     if (IsThreadAlive(tid)) {
       return true;
@@ -735,3 +808,19 @@ bool EventSelectionSet::CheckMonitoredTargets() {
   }
   return loop_->ExitLoop();
 }
+
+bool EventSelectionSet::HasSampler() {
+  for (auto& group : groups_) {
+    for (auto& sel : group) {
+      if (!sel.event_fds.empty()) {
+        return true;
+      }
+      for (auto& sampler : sel.inplace_samplers) {
+        if (!sampler->IsClosed()) {
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
index 8aca784..97ad7e5 100644 (file)
@@ -28,6 +28,8 @@
 #include "event_attr.h"
 #include "event_fd.h"
 #include "event_type.h"
+#include "InplaceSamplerClient.h"
+#include "IOEventLoop.h"
 #include "perf_event.h"
 #include "record.h"
 
@@ -47,8 +49,6 @@ struct CountersInfo {
   std::vector<CounterInfo> counters;
 };
 
-class IOEventLoop;
-
 // EventSelectionSet helps to monitor events. It is used in following steps:
 // 1. Create an EventSelectionSet, and add event types to monitor by calling
 //    AddEventType() or AddEventGroup().
@@ -67,13 +67,14 @@ class IOEventLoop;
 class EventSelectionSet {
  public:
   EventSelectionSet(bool for_stat_cmd)
-      : for_stat_cmd_(for_stat_cmd), mmap_pages_(0), loop_(nullptr) {}
+      : for_stat_cmd_(for_stat_cmd), mmap_pages_(0), loop_(new IOEventLoop) {}
 
   bool empty() const { return groups_.empty(); }
 
   bool AddEventType(const std::string& event_name);
   bool AddEventGroup(const std::vector<std::string>& event_names);
   std::vector<const EventType*> GetTracepointEvents() const;
+  bool HasInplaceSampler() const;
   std::vector<EventAttrWithId> GetEventAttrWithId() const;
 
   void SetEnableOnExec(bool enable);
@@ -104,8 +105,8 @@ class EventSelectionSet {
     return !processes_.empty() || !threads_.empty();
   }
 
-  void SetIOEventLoop(IOEventLoop& loop) {
-    loop_ = &loop;
+  IOEventLoop* GetIOEventLoop() {
+    return loop_.get();
   }
 
   bool OpenEventFiles(const std::vector<int>& on_cpus);
@@ -128,6 +129,7 @@ class EventSelectionSet {
     EventTypeAndModifier event_type_modifier;
     perf_event_attr event_attr;
     std::vector<std::unique_ptr<EventFd>> event_fds;
+    std::vector<std::unique_ptr<InplaceSamplerClient>> inplace_samplers;
     // counters for event files closed for cpu hotplug events
     std::vector<CounterInfo> hotplugged_counters;
   };
@@ -136,6 +138,9 @@ class EventSelectionSet {
   bool BuildAndCheckEventSelection(const std::string& event_name,
                                    EventSelection* selection);
   void UnionSampleType();
+  bool IsUserSpaceSamplerGroup(EventSelectionGroup& group);
+  bool OpenUserSpaceSamplersOnGroup(EventSelectionGroup& group,
+                                    const std::map<pid_t, std::set<pid_t>>& process_map);
   bool OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t tid, int cpu,
                              std::string* failed_event_type);
 
@@ -147,6 +152,7 @@ class EventSelectionSet {
   bool HandleCpuOfflineEvent(int cpu);
   bool CreateMappedBufferForCpu(int cpu);
   bool CheckMonitoredTargets();
+  bool HasSampler();
 
   const bool for_stat_cmd_;
 
@@ -155,7 +161,7 @@ class EventSelectionSet {
   std::set<pid_t> threads_;
   size_t mmap_pages_;
 
-  IOEventLoop* loop_;
+  std::unique_ptr<IOEventLoop> loop_;
   std::function<bool(Record*)> record_callback_;
 
   std::set<int> monitored_cpus_;
index 12d83b3..a1e401f 100644 (file)
 #include <string>
 #include <vector>
 
+// A uint32_t value far from 0 is picked, so it is unlikely to conflict with further
+// PERF_TYPE_* events.
+static constexpr uint32_t SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS = 32768;
+
+enum {
+  SIMPLEPERF_CONFIG_INPLACE_SAMPLER,
+};
+
 // EventType represents one type of event, like cpu_cycle_event, cache_misses_event.
 // The user knows one event type by its name, and the kernel knows one event type by its
 // (type, config) pair. EventType connects the two representations, and tells the user if
index a77be0a..123216c 100644 (file)
@@ -63,3 +63,5 @@
 {"node-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
 {"node-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
 
+{"inplace-sampler", SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS, SIMPLEPERF_CONFIG_INPLACE_SAMPLER},
+
index ff60c23..eaffd60 100755 (executable)
@@ -106,11 +106,18 @@ def gen_hw_cache_events():
   return generated_str
 
 
+def gen_user_space_events():
+  generated_str = gen_event_type_entry_str("inplace-sampler",
+                                           "SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS",
+                                           "SIMPLEPERF_CONFIG_INPLACE_SAMPLER")
+  return generated_str
+
 def gen_events():
   generated_str = "// This file is auto-generated by generate-event_table.py.\n\n"
   generated_str += gen_hardware_events() + '\n'
   generated_str += gen_software_events() + '\n'
   generated_str += gen_hw_cache_events() + '\n'
+  generated_str += gen_user_space_events() + '\n'
   return generated_str
 
 generated_str = gen_events()
index 922b490..3cc929c 100644 (file)
@@ -99,45 +99,72 @@ static bool ExtractTestDataFromElfSection() {
   return true;
 }
 
-class SavedPerfHardenProperty {
+class ScopedEnablingPerf {
  public:
-  SavedPerfHardenProperty() {
+  ScopedEnablingPerf() {
+    memset(prop_value_, '\0', sizeof(prop_value_));
     __system_property_get("security.perf_harden", prop_value_);
-    if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_paranoid",
-                                    &paranoid_value_)) {
-      PLOG(ERROR) << "failed to read /proc/sys/kernel/perf_event_paranoid";
-    }
+    SetProp("0");
   }
 
-  ~SavedPerfHardenProperty() {
+  ~ScopedEnablingPerf() {
     if (strlen(prop_value_) != 0) {
-      __system_property_set("security.perf_harden", prop_value_);
-      // Sleep one second to wait for security.perf_harden changing
-      // /proc/sys/kernel/perf_event_paranoid.
-      sleep(1);
-      std::string paranoid_value;
-      if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_paranoid",
-                                           &paranoid_value)) {
-        PLOG(ERROR) << "failed to read /proc/sys/kernel/perf_event_paranoid";
-        return;
-      }
-      if (paranoid_value_ != paranoid_value) {
-        LOG(ERROR) << "failed to restore /proc/sys/kernel/perf_event_paranoid";
-      }
+      SetProp(prop_value_);
     }
   }
 
  private:
+  void SetProp(const char* value) {
+    __system_property_set("security.perf_harden", value);
+    // Sleep one second to wait for security.perf_harden changing
+    // /proc/sys/kernel/perf_event_paranoid.
+    sleep(1);
+  }
+
   char prop_value_[PROP_VALUE_MAX];
-  std::string paranoid_value_;
 };
 
+static bool TestInAppContext(int argc, char** argv) {
+  // Use run-as to move the test executable to the data directory of debuggable app
+  // 'com.android.simpleperf', and run it.
+  std::string exe_path;
+  if (!android::base::Readlink("/proc/self/exe", &exe_path)) {
+    PLOG(ERROR) << "readlink failed";
+    return false;
+  }
+  std::string copy_cmd = android::base::StringPrintf("run-as com.android.simpleperf cp %s .",
+                                                     exe_path.c_str());
+  if (system(copy_cmd.c_str()) == -1) {
+    PLOG(ERROR) << "system(" << copy_cmd << ") failed";
+    return false;
+  }
+  std::string arg_str;
+  arg_str += basename(argv[0]);
+  for (int i = 1; i < argc; ++i) {
+    arg_str.push_back(' ');
+    arg_str += argv[i];
+  }
+  std::string test_cmd = android::base::StringPrintf("run-as com.android.simpleperf ./%s",
+                                                     arg_str.c_str());
+  test_cmd += " --in-app-context";
+  if (system(test_cmd.c_str()) == -1) {
+    PLOG(ERROR) << "system(" << test_cmd << ") failed";
+    return false;
+  }
+  return true;
+}
+
 #endif  // defined(__ANDROID__)
 
 int main(int argc, char** argv) {
-  InitLogging(argv, android::base::StderrLogger);
-  testing::InitGoogleTest(&argc, argv);
+  android::base::InitLogging(argv, android::base::StderrLogger);
   android::base::LogSeverity log_severity = android::base::WARNING;
+  bool need_app_context __attribute__((unused)) = false;
+  bool in_app_context __attribute__((unused)) = false;
+
+#if defined(RUN_IN_APP_CONTEXT)
+  need_app_context = true;
+#endif
 
   for (int i = 1; i < argc; ++i) {
     if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
@@ -154,11 +181,25 @@ int main(int argc, char** argv) {
         LOG(ERROR) << "Missing argument for --log option.\n";
         return 1;
       }
+    } else if (strcmp(argv[i], "--in-app-context") == 0) {
+      in_app_context = true;
     }
   }
   android::base::ScopedLogSeverity severity(log_severity);
 
 #if defined(__ANDROID__)
+  std::unique_ptr<ScopedEnablingPerf> scoped_enabling_perf;
+  if (!in_app_context) {
+    // A cts test PerfEventParanoidTest.java is testing if
+    // /proc/sys/kernel/perf_event_paranoid is 3, so restore perf_harden
+    // value after current test to not break that test.
+    scoped_enabling_perf.reset(new ScopedEnablingPerf);
+  }
+
+  if (need_app_context && !in_app_context) {
+    return TestInAppContext(argc, argv) ? 0 : 1;
+  }
+
   std::unique_ptr<TemporaryDir> tmp_dir;
   if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
     testdata_dir = std::string(dirname(argv[0])) + "/testdata";
@@ -172,12 +213,9 @@ int main(int argc, char** argv) {
     }
   }
 
-  // A cts test PerfEventParanoidTest.java is testing if
-  // /proc/sys/kernel/perf_event_paranoid is 3, so restore perf_harden
-  // value after current test to not break that test.
-  SavedPerfHardenProperty saved_perf_harden;
 #endif
 
+  testing::InitGoogleTest(&argc, argv);
   if (!::testing::GTEST_FLAG(list_tests) && testdata_dir.empty()) {
     printf("Usage: %s -t <testdata_dir>\n", argv[0]);
     return 1;
index a8a8935..07b0e4b 100644 (file)
@@ -27,7 +27,7 @@
 constexpr int SIMPLEPERF_VERSION = 1;
 
 int main(int argc, char** argv) {
-  InitLogging(argv, android::base::StderrLogger);
+  android::base::InitLogging(argv, android::base::StderrLogger);
   std::vector<std::string> args;
   android::base::LogSeverity log_severity = android::base::INFO;
 
index 86da065..ba04daf 100644 (file)
@@ -464,7 +464,8 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, uint64_t id,
   sample_type = attr.sample_type;
   CHECK_EQ(0u, sample_type & ~(PERF_SAMPLE_IP | PERF_SAMPLE_TID
       | PERF_SAMPLE_TIME | PERF_SAMPLE_ID | PERF_SAMPLE_CPU
-      | PERF_SAMPLE_PERIOD | PERF_SAMPLE_CALLCHAIN));
+      | PERF_SAMPLE_PERIOD | PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER
+      | PERF_SAMPLE_STACK_USER));
   ip_data.ip = ip;
   tid_data.pid = pid;
   tid_data.tid = tid;
@@ -502,6 +503,13 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, uint64_t id,
   if (sample_type & PERF_SAMPLE_CALLCHAIN) {
     size += sizeof(uint64_t) * (ips.size() + 1);
   }
+  if (sample_type & PERF_SAMPLE_REGS_USER) {
+    size += sizeof(uint64_t);
+  }
+  if (sample_type & PERF_SAMPLE_STACK_USER) {
+    size += sizeof(uint64_t);
+  }
+
   SetSize(size);
   char* new_binary = new char[size];
   char* p = new_binary;
@@ -529,6 +537,12 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, uint64_t id,
     callchain_data.ips = reinterpret_cast<uint64_t*>(p);
     MoveToBinaryFormat(ips.data(), ips.size(), p);
   }
+  if (sample_type & PERF_SAMPLE_REGS_USER) {
+    MoveToBinaryFormat(regs_user_data.abi, p);
+  }
+  if (sample_type & PERF_SAMPLE_STACK_USER) {
+    MoveToBinaryFormat(stack_user_data.size, p);
+  }
   CHECK_EQ(p, new_binary + size);
   UpdateBinary(new_binary);
 }
index 12d64fa..cdcb2bf 100644 (file)
@@ -8,9 +8,14 @@ void Function1() {
 }
 
 int main() {
-  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM1"), 0, 0, 0); // NOLINT
-  Function1();
-  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM2"), 0, 0, 0); // NOLINT
-  Function1();
+  // Run the test in an infinite loop, so if we profile the test manually, the process
+  // doesn't exit before we attach to it. This scheme also allows simpleperf to control
+  // how long to profile.
+  while (true) {
+    prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM1"), 0, 0, 0); // NOLINT
+    Function1();
+    prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("RUN_COMM2"), 0, 0, 0); // NOLINT
+    Function1();
+  }
   return 0;
 }
index b1477a6..8551927 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdlib.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 constexpr int LOOP_COUNT = 100000000;
@@ -19,12 +20,15 @@ void ChildFunction() {
 }
 
 int main() {
-  pid_t pid = fork();
-  if (pid == 0) {
-    ChildFunction();
-    return 0;
-  } else {
-    ParentFunction();
+  while (true) {
+    pid_t pid = fork();
+    if (pid == 0) {
+      ChildFunction();
+      return 0;
+    } else {
+      ParentFunction();
+      waitpid(pid, nullptr, 0);
+    }
   }
   return 0;
 }
index 5e70fd3..70645a1 100644 (file)
@@ -19,6 +19,8 @@ void FunctionRecursiveTwo(int loop) {
 }
 
 int main() {
-  FunctionRecursiveOne(10);
+  while (true) {
+    FunctionRecursiveOne(10);
+  }
   return 0;
 }
index 02fc0a5..c80fb3f 100644 (file)
@@ -17,17 +17,19 @@ void MainThreadFunction() {
 }
 
 int main() {
-  pthread_t thread;
-  int ret = pthread_create(&thread, nullptr, ChildThreadFunction, nullptr);
-  if (ret != 0) {
-    fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));
-    exit(1);
-  }
-  MainThreadFunction();
-  ret = pthread_join(thread, nullptr);
-  if (ret != 0) {
-    fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
-    exit(1);
+  while (true) {
+    pthread_t thread;
+    int ret = pthread_create(&thread, nullptr, ChildThreadFunction, nullptr);
+    if (ret != 0) {
+      fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));
+      exit(1);
+    }
+    MainThreadFunction();
+    ret = pthread_join(thread, nullptr);
+    if (ret != 0) {
+      fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
+      exit(1);
+    }
   }
   return 0;
 }
index d8d28bc..bf60668 100644 (file)
@@ -11,6 +11,8 @@ void FunctionRecursive(int loop) {
 }
 
 int main() {
-  FunctionRecursive(10);
+  while (true) {
+    FunctionRecursive(10);
+  }
   return 0;
 }
index 49090ac..561bb5a 100644 (file)
@@ -6,6 +6,8 @@ void Function1() {
 }
 
 int main() {
-  Function1();
+  while (true) {
+    Function1();
+  }
   return 0;
 }
index bbfdc48..77fc566 100644 (file)
@@ -277,22 +277,29 @@ def load_symbol_relation_requirement(symbol_item):
 
 class Runner(object):
 
-  def __init__(self, perf_path):
+  def __init__(self, target, perf_path):
+    self.target = target
     self.perf_path = perf_path
+    self.use_callgraph = False
+    self.sampler = 'cpu-cycles'
 
   def record(self, test_executable_name, record_file, additional_options=[]):
-    call_args = [self.perf_path,
-                 'record'] + additional_options + ['-e',
-                                                   'cpu-cycles:u',
-                                                   '-o',
-                                                   record_file,
-                                                   test_executable_name]
+    call_args = [self.perf_path, 'record']
+    call_args += ['--duration', '1']
+    call_args += ['-e', '%s:u' % self.sampler]
+    if self.use_callgraph:
+      call_args += ['-f', '1000', '-g']
+    call_args += ['-o', record_file]
+    call_args += additional_options
+    call_args += [test_executable_name]
     self._call(call_args)
 
   def report(self, record_file, report_file, additional_options=[]):
-    call_args = [self.perf_path,
-                 'report'] + additional_options + ['-i',
-                                                   record_file]
+    call_args = [self.perf_path, 'report']
+    call_args += ['-i', record_file]
+    if self.use_callgraph:
+      call_args += ['-g', 'callee']
+    call_args += additional_options
     self._call(call_args, report_file)
 
   def _call(self, args, output_file=None):
@@ -303,6 +310,9 @@ class HostRunner(Runner):
 
   """Run perf test on host."""
 
+  def __init__(self, perf_path):
+    super(HostRunner, self).__init__('host', perf_path)
+
   def _call(self, args, output_file=None):
     output_fh = None
     if output_file is not None:
@@ -318,8 +328,8 @@ class DeviceRunner(Runner):
 
   def __init__(self, perf_path):
     self.tmpdir = '/data/local/tmp/'
+    super(DeviceRunner, self).__init__('device', self.tmpdir + perf_path)
     self._download(os.environ['OUT'] + '/system/xbin/' + perf_path, self.tmpdir)
-    self.perf_path = self.tmpdir + perf_path
 
   def _call(self, args, output_file=None):
     output_fh = None
@@ -518,7 +528,7 @@ class ReportAnalyzer(object):
     return result
 
 
-def runtest(host, device, normal, callgraph, selected_tests):
+def runtest(host, device, normal, callgraph, use_inplace_sampler, selected_tests):
   tests = load_config_file(os.path.dirname(os.path.realpath(__file__)) + \
                            '/runtest.conf')
   host_runner = HostRunner('simpleperf')
@@ -581,26 +591,76 @@ def runtest(host, device, normal, callgraph, selected_tests):
       if not result:
         exit(1)
 
+
+def build_runner(target, use_callgraph, sampler):
+  if target == 'host':
+    runner = HostRunner('simpleperf')
+  else:
+    runner = DeviceRunner('simpleperf')
+  runner.use_callgraph = use_callgraph
+  runner.sampler = sampler
+  return runner
+
+
+def test_with_runner(runner, tests):
+  report_analyzer = ReportAnalyzer()
+  for test in tests:
+    runner.record(test.executable_name, 'perf.data')
+    if runner.sampler == 'inplace-sampler':
+      # TODO: fix this when inplace-sampler actually works.
+      runner.report('perf.data', 'perf.report')
+      symbols = report_analyzer._read_report_file('perf.report', runner.use_callgraph)
+      result = False
+      if len(symbols) == 1 and symbols[0].name == 'fake_elf[+0]':
+        result = True
+    else:
+      runner.report('perf.data', 'perf.report', additional_options = test.report_options)
+      result = report_analyzer.check_report_file(test, 'perf.report', runner.use_callgraph)
+    str = 'test %s on %s ' % (test.test_name, runner.target)
+    if runner.use_callgraph:
+      str += 'with call graph '
+    str += 'using %s ' % runner.sampler
+    str += ' Succeeded' if result else 'Failed'
+    print str
+    if not result:
+      exit(1)
+
+
+def runtest(target_options, use_callgraph_options, sampler_options, selected_tests):
+  tests = load_config_file(os.path.dirname(os.path.realpath(__file__)) + \
+                           '/runtest.conf')
+  if selected_tests is not None:
+    new_tests = []
+    for test in tests:
+      if test.test_name in selected_tests:
+        new_tests.append(test)
+    tests = new_tests
+  for target in target_options:
+    for use_callgraph in use_callgraph_options:
+      for sampler in sampler_options:
+        runner = build_runner(target, use_callgraph, sampler)
+        test_with_runner(runner, tests)
+
+
 def main():
-  host = True
-  device = True
-  normal = True
-  callgraph = True
+  target_options = ['host', 'target']
+  use_callgraph_options = [False, True]
+  sampler_options = ['cpu-cycles', 'inplace-sampler']
   selected_tests = None
   i = 1
   while i < len(sys.argv):
     if sys.argv[i] == '--host':
-      host = True
-      device = False
+      use_callgraph_options = ['host']
     elif sys.argv[i] == '--device':
-      host = False
-      device = True
+      use_callgraph_options = ['device']
     elif sys.argv[i] == '--normal':
-      normal = True
-      callgraph = False
+      use_callgraph_options = [False]
     elif sys.argv[i] == '--callgraph':
-      normal = False
-      callgraph = True
+      use_callgraph_options = [True]
+    elif sys.argv[i] == '--no-inplace-sampler':
+      sampler_options = ['cpu-cycles']
+    elif sys.argv[i] == '--inplace-sampler':
+      sampler_options = ['inplace-sampler']
     elif sys.argv[i] == '--test':
       if i < len(sys.argv):
         i += 1
@@ -609,7 +669,7 @@ def main():
             selected_tests = {}
           selected_tests[test] = True
     i += 1
-  runtest(host, device, normal, callgraph, selected_tests)
+  runtest(target_options, use_callgraph_options, sampler_options, selected_tests)
 
 if __name__ == '__main__':
   main()
index 1d3e389..b74c153 100644 (file)
@@ -18,7 +18,9 @@ void Function2() {
 }
 
 int main() {
-  Function1();
-  Function2();
+  while (true) {
+    Function1();
+    Function2();
+  }
   return 0;
 }
index 2141830..9d9d595 100644 (file)
@@ -40,6 +40,9 @@ class Workload {
   ~Workload();
 
   bool Start();
+  bool IsStarted() {
+    return work_state_ == Started;
+  }
   pid_t GetPid() {
     return work_pid_;
   }
diff --git a/tests/bootloader/Android.mk b/tests/bootloader/Android.mk
new file mode 100644 (file)
index 0000000..21905e9
--- /dev/null
@@ -0,0 +1,26 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build a module that has all of the python files as its LOCAL_PICKUP_FILES.
+# Since no action needs to be taken to compile the python source, just
+# use BUILD_PHONY_PACKAGE to give us a target to execute.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootloader_unit_test
+LOCAL_MODULE_TAGS := optional
+
+bootloader_py_files := $(call find-subdir-files, *.py)
+
+bootloader_zip_prefix := $(TARGET_OUT_DATA)/py_bootloader
+bootloader_zip_path := $(bootloader_zip_prefix)/nativetest/py_bootloader
+
+GEN := $(addprefix $(bootloader_zip_path)/, $(bootloader_py_files))
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN) : $(bootloader_zip_path)/% : $(LOCAL_PATH)/%
+       $(transform-generated-source)
+
+LOCAL_PICKUP_FILES := $(bootloader_zip_prefix)/nativetest
+
+bootloader_unit_test: $(GEN)
+
+include $(BUILD_PHONY_PACKAGE)
index 7b769b0..8ea4881 100644 (file)
@@ -18,7 +18,8 @@ include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := recovery_test
 LOCAL_SRC_FILES := recovery_test.cpp
-LOCAL_SHARED_LIBRARIES += libcutils libutils liblog liblogwrap libext4_utils
+LOCAL_SHARED_LIBRARIES += libcutils libutils libbase liblog \
+                          liblogwrap libext4_utils
 LOCAL_STATIC_LIBRARIES += libtestUtil libfs_mgr
 LOCAL_C_INCLUDES += system/extras/tests/include \
                     system/core/logwrapper/include
index c3c0595..aa02e66 100644 (file)
@@ -313,6 +313,8 @@ int per_core_bandwidth(int argc, char** argv) {
         args[i].core = *it;
         args[i].bench = createBandwidthBenchmarkObject(values);
         if (!args[i].bench) {
+            for (int j = 0; j < i; j++)
+                delete args[j].bench;
             return -1;
         }
     }
@@ -348,6 +350,8 @@ int multithread_bandwidth(int argc, char** argv) {
         args[i].core = -1;
         args[i].bench = createBandwidthBenchmarkObject(values);
         if (!args[i].bench) {
+            for (int j = 0; j < i; j++)
+                delete args[j].bench;
             return -1;
         }
     }
index c2ebd0f..e379a47 100644 (file)
@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := verify_boot_signature.c
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := libcrypto
-LOCAL_C_INCLUDES += external/openssl/include system/core/mkbootimg
+LOCAL_C_INCLUDES += system/core/mkbootimg
 include $(BUILD_HOST_EXECUTABLE)
 
 endif # HOST_OS == linux