--- /dev/null
+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)
--- /dev/null
+ 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'
+
+
--- /dev/null
+#!/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
--- /dev/null
+#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]);
+}
+
--- /dev/null
+#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]);
+}
+
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
};
}
-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;
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;
}
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");
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);
#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)
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;
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;
}
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;
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;
}
}
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);
}
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;
}
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;
}
#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>
}
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!",
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";
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
}
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,
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,
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.
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"
{
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)
environment.cpp \
event_fd.cpp \
event_selection_set.cpp \
+ InplaceSamplerClient.cpp \
IOEventLoop.cpp \
perf_clock.cpp \
record_file_writer.cpp \
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
IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false) {}
IOEventLoop::~IOEventLoop() {
+ events_.clear();
if (ebase_ != nullptr) {
event_base_free(ebase_);
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_
// 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());
}
}
{"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;
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";
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)) {
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;
}
}
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()) {
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));
+}
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());
success = true;
}
- TemporaryFile tmp_file;
std::string content;
std::vector<std::string> lines;
bool success;
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)) {
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;
}
}
};
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;
}
}
if (workload != nullptr && !workload->Start()) {
return false;
}
- if (!loop.RunLoop()) {
+ if (!loop->RunLoop()) {
return false;
}
// 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;
}
verbose_mode = true;
}
}
- InitLogging(argv, android::base::StderrLogger);
+ android::base::InitLogging(argv, android::base::StderrLogger);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
if (dirname.back() != '/') {
dirname.push_back('/');
}
- if (GetEntriesInDir(symfs_dir).empty()) {
+ if (!IsDir(symfs_dir)) {
LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
return false;
}
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;
}
DISALLOW_COPY_AND_ASSIGN(EventFd);
};
-bool IsEventAttrSupportedByKernel(perf_event_attr attr);
+bool IsEventAttrSupported(const perf_event_attr& attr);
#endif // SIMPLE_PERF_EVENT_FD_H_
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() {
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(
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();
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_) {
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);
}
}
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;
}
} 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));
}
}
}
}
}
}
+ for (auto& sampler : selection.inplace_samplers) {
+ if (!sampler->StartPolling(*loop_, callback,
+ [&] { return CheckMonitoredTargets(); })) {
+ return false;
+ }
+ }
}
}
}
}
+ 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 =
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;
+ }
}
}
}
}
bool EventSelectionSet::CheckMonitoredTargets() {
+ if (!HasSampler()) {
+ return loop_->ExitLoop();
+ }
for (const auto& tid : threads_) {
if (IsThreadAlive(tid)) {
return true;
}
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;
+}
#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"
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().
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);
return !processes_.empty() || !threads_.empty();
}
- void SetIOEventLoop(IOEventLoop& loop) {
- loop_ = &loop;
+ IOEventLoop* GetIOEventLoop() {
+ return loop_.get();
}
bool OpenEventFiles(const std::vector<int>& on_cpus);
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;
};
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);
bool HandleCpuOfflineEvent(int cpu);
bool CreateMappedBufferForCpu(int cpu);
bool CheckMonitoredTargets();
+ bool HasSampler();
const bool for_stat_cmd_;
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_;
#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
{"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},
+
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()
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",
- ¶noid_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",
- ¶noid_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) {
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";
}
}
- // 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;
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;
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;
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;
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);
}
}
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;
}
#include <stdlib.h>
+#include <sys/wait.h>
#include <unistd.h>
constexpr int LOOP_COUNT = 100000000;
}
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;
}
}
int main() {
- FunctionRecursiveOne(10);
+ while (true) {
+ FunctionRecursiveOne(10);
+ }
return 0;
}
}
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;
}
}
int main() {
- FunctionRecursive(10);
+ while (true) {
+ FunctionRecursive(10);
+ }
return 0;
}
}
int main() {
- Function1();
+ while (true) {
+ Function1();
+ }
return 0;
}
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):
"""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:
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
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')
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
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()
}
int main() {
- Function1();
- Function2();
+ while (true) {
+ Function1();
+ Function2();
+ }
return 0;
}
~Workload();
bool Start();
+ bool IsStarted() {
+ return work_state_ == Started;
+ }
pid_t GetPid() {
return work_pid_;
}
--- /dev/null
+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)
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
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;
}
}
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;
}
}
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