1#! /bin/bash 2set -eu 3# Script to run compilation profile and build a flame graph for it. 4 5function usage { 6 echo "usage: ./profile.sh <gradle tasks> 7 ./profile.sh --name my_run \ 8 --target gradle \ 9 :room:integration-tests:room-testapp-kotlin:kspWithKspGenJavaDebugAndroidTestKotlin \ 10 --filter *K2JVMCompiler* 11 ./profile.sh --name my_run \ 12 --target test \ 13 :room:room-compiler-processing:test \ 14 --tests RawTypeCreationScenarioTest 15 16 17 Arguments: 18 -h: print usage 19 --name <report name>: Name of the report (used in output file if output file is not set) 20 --filter <stacktrace include filter>: Regex to filter stacktraces. e.g. *room* 21 --outputFile <file path>: Path to the output file 22 --preset [kapt|ksp]: Predefined tasks to run ksp/kapt profile on the KotlinTestApp 23 --target [gradle|test]: The target process type to profile. Gradle for compilation, 24 test for profile test tasks. Defaults to gradle 25 26 It will run the task once without profiling, stop gradle daemon and re-run it again while also 27 disabling up-to-date checks for the given tasks. 28 29 You also need to have ASYNC_PROFILER_PATH environment variable set to an installation of 30 Async Profiler (https://github.com/jvm-profiling-tools/async-profiler) 31 " 32 exit 1 33} 34 35WORKING_DIR=`pwd` 36SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 37OUTPUT_DIR="$SCRIPT_DIR/outputs" 38PROJECT_DIR="$SCRIPT_DIR/.." 39 40GRADLEW="$SCRIPT_DIR/../gradlew" # using playground file 41 42if [ -z ${ASYNC_PROFILER_PATH+x} ]; then # +x here to workaround unbound variable check 43 echo "invalid agent path, make sure you have ASYNC_PROFILER_PATH environment variable set to AsyncProfiler installation root" 44 usage 45fi 46 47AGENT_PATH="$ASYNC_PROFILER_PATH/build/libasyncProfiler.so" 48if test ! -f $AGENT_PATH; then 49 echo "invalid agent path, make sure you have ASYNC_PROFILER_PATH environment variable set to AsyncProfiler installation root" 50 usage 51fi 52 53REPORT_NAME="profile" 54GRADLE_ARGS="" 55ADDITIONAL_AGENT_PARAMS="" 56AGENT_TARGET="gradle" 57 58while [ $# -gt 0 ]; do 59 case $1 in 60 -h|"-?") 61 usage 62 ;; 63 --name) 64 REPORT_NAME=$2 65 shift 66 ;; 67 --filter) 68 ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=$2" 69 shift 70 ;; 71 --outputFile) 72 OUTPUT_FILE=$2 73 shift 74 ;; 75 --target) 76 if [ "$2" = "gradle" ]; then 77 AGENT_TARGET="gadle" 78 elif [ "$2" = "test" ]; then 79 AGENT_TARGET="test" 80 # add RealProfileScope to tracing automatically. Otherwise, it will be dominated 81 # by compilation. 82 ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=*RealProfileScope*" 83 else 84 echo "invalid target: $2" 85 exit 86 fi 87 shift 88 ;; 89 --preset) 90 if [ "$2" = "kapt" ]; then 91 GRADLE_ARGS=":room:integration-tests:room-testapp-kotlin:kaptGenerateStubsWithKaptDebugAndroidTestKotlin \ 92 :room:integration-tests:room-testapp-kotlin:kaptWithKaptDebugAndroidTestKotlin" 93 ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=*AbstractKapt3Extension*" 94 AGENT_TARGET="gradle" 95 elif [ "$2" = "ksp" ]; then 96 GRADLE_ARGS=":room:integration-tests:room-testapp-kotlin:kspWithKspGenJavaDebugAndroidTestKotlin" 97 ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=*AbstractKotlinSymbolProcessingExtension*" 98 AGENT_TARGET="gradle" 99 else 100 echo "invalid preset: $2" 101 exit 102 fi 103 shift 104 ;; 105 *) 106 GRADLE_ARGS="${GRADLE_ARGS} $1" 107 esac 108 shift 109done 110 111#sets an ouytput file if none defined 112function setOutputFile { 113 local number=0 114 OUTPUT_FILE="${OUTPUT_DIR}/$1_$number.html" 115 while [ -e "$OUTPUT_FILE" ]; do 116 OUTPUT_FILE="${OUTPUT_DIR}/${1}_$((++number)).html" 117 done 118} 119 120function createParentDirectoryForOutput { 121 PARENT_DIR="$(dirname "$OUTPUT_FILE")" 122 $(mkdir -p $PARENT_DIR) 123} 124 125if [ -z ${OUTPUT_FILE+x} ]; then # +x here to workaround unbound variable check 126 setOutputFile $REPORT_NAME 127fi 128createParentDirectoryForOutput 129echo "gradle args: $GRADLE_ARGS" 130echo "agent params: $ADDITIONAL_AGENT_PARAMS" 131echo "output file: $OUTPUT_FILE" 132 133 134set -x 135function profile { 136 local TASK=$1 137 138 local AGENT_PARAMS="file=${OUTPUT_FILE}${ADDITIONAL_AGENT_PARAMS}" 139 $GRADLEW -p $PROJECT_DIR $GRADLE_ARGS 140 local AGENT_PARAMETER_NAME="-Dorg.gradle.jvmargs" 141 if [ $AGENT_TARGET = "test" ]; then 142 AGENT_PARAMETER_NAME="-Dandroidx.room.testJvmArgs" 143 fi 144 $GRADLEW --no-daemon \ 145 --init-script $SCRIPT_DIR/rerun-requested-task-init-script.gradle \ 146 --init-script $SCRIPT_DIR/attach-async-profiler-to-tests-init-script.gradle \ 147 -p $PROJECT_DIR \ 148 --no-configuration-cache \ 149 $GRADLE_ARGS \ 150 -Dkotlin.compiler.execution.strategy="in-process" \ 151 $AGENT_PARAMETER_NAME="-agentpath:$AGENT_PATH=start,event=cpu,$AGENT_PARAMS,interval=500000" #sample every .5 ms 152} 153 154profile $GRADLE_ARGS, $REPORT_NAME 155 156function openFileInChrome { 157 for i in {1..5}; do 158 # wait until file is written 159 sleep 2 160 if test -f "$1"; then 161 case "`uname`" in 162 Darwin* ) 163 open $1 164 ;; 165 Linux* ) 166 google-chrome $1 167 ;; 168 esac 169 return 0 170 fi 171 done 172 echo "Couldn't find the output file. $OUTPUT_FILE" 173} 174 175# open it in chrome once done 176openFileInChrome $OUTPUT_FILE 177