#! /bin/bash set -eu # Script to run compilation profile and build a flame graph for it. function usage { echo "usage: ./profile.sh ./profile.sh --name my_run \ --target gradle \ :room:integration-tests:room-testapp-kotlin:kspWithKspGenJavaDebugAndroidTestKotlin \ --filter *K2JVMCompiler* ./profile.sh --name my_run \ --target test \ :room:room-compiler-processing:test \ --tests RawTypeCreationScenarioTest Arguments: -h: print usage --name : Name of the report (used in output file if output file is not set) --filter : Regex to filter stacktraces. e.g. *room* --outputFile : Path to the output file --preset [kapt|ksp]: Predefined tasks to run ksp/kapt profile on the KotlinTestApp --target [gradle|test]: The target process type to profile. Gradle for compilation, test for profile test tasks. Defaults to gradle It will run the task once without profiling, stop gradle daemon and re-run it again while also disabling up-to-date checks for the given tasks. You also need to have ASYNC_PROFILER_PATH environment variable set to an installation of Async Profiler (https://github.com/jvm-profiling-tools/async-profiler) " exit 1 } WORKING_DIR=`pwd` SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" OUTPUT_DIR="$SCRIPT_DIR/outputs" PROJECT_DIR="$SCRIPT_DIR/.." GRADLEW="$SCRIPT_DIR/../gradlew" # using playground file if [ -z ${ASYNC_PROFILER_PATH+x} ]; then # +x here to workaround unbound variable check echo "invalid agent path, make sure you have ASYNC_PROFILER_PATH environment variable set to AsyncProfiler installation root" usage fi AGENT_PATH="$ASYNC_PROFILER_PATH/build/libasyncProfiler.so" if test ! -f $AGENT_PATH; then echo "invalid agent path, make sure you have ASYNC_PROFILER_PATH environment variable set to AsyncProfiler installation root" usage fi REPORT_NAME="profile" GRADLE_ARGS="" ADDITIONAL_AGENT_PARAMS="" AGENT_TARGET="gradle" while [ $# -gt 0 ]; do case $1 in -h|"-?") usage ;; --name) REPORT_NAME=$2 shift ;; --filter) ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=$2" shift ;; --outputFile) OUTPUT_FILE=$2 shift ;; --target) if [ "$2" = "gradle" ]; then AGENT_TARGET="gadle" elif [ "$2" = "test" ]; then AGENT_TARGET="test" # add RealProfileScope to tracing automatically. Otherwise, it will be dominated # by compilation. ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=*RealProfileScope*" else echo "invalid target: $2" exit fi shift ;; --preset) if [ "$2" = "kapt" ]; then GRADLE_ARGS=":room:integration-tests:room-testapp-kotlin:kaptGenerateStubsWithKaptDebugAndroidTestKotlin \ :room:integration-tests:room-testapp-kotlin:kaptWithKaptDebugAndroidTestKotlin" ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=*AbstractKapt3Extension*" AGENT_TARGET="gradle" elif [ "$2" = "ksp" ]; then GRADLE_ARGS=":room:integration-tests:room-testapp-kotlin:kspWithKspGenJavaDebugAndroidTestKotlin" ADDITIONAL_AGENT_PARAMS="$ADDITIONAL_AGENT_PARAMS,include=*AbstractKotlinSymbolProcessingExtension*" AGENT_TARGET="gradle" else echo "invalid preset: $2" exit fi shift ;; *) GRADLE_ARGS="${GRADLE_ARGS} $1" esac shift done #sets an ouytput file if none defined function setOutputFile { local number=0 OUTPUT_FILE="${OUTPUT_DIR}/$1_$number.html" while [ -e "$OUTPUT_FILE" ]; do OUTPUT_FILE="${OUTPUT_DIR}/${1}_$((++number)).html" done } function createParentDirectoryForOutput { PARENT_DIR="$(dirname "$OUTPUT_FILE")" $(mkdir -p $PARENT_DIR) } if [ -z ${OUTPUT_FILE+x} ]; then # +x here to workaround unbound variable check setOutputFile $REPORT_NAME fi createParentDirectoryForOutput echo "gradle args: $GRADLE_ARGS" echo "agent params: $ADDITIONAL_AGENT_PARAMS" echo "output file: $OUTPUT_FILE" set -x function profile { local TASK=$1 local AGENT_PARAMS="file=${OUTPUT_FILE}${ADDITIONAL_AGENT_PARAMS}" $GRADLEW -p $PROJECT_DIR $GRADLE_ARGS local AGENT_PARAMETER_NAME="-Dorg.gradle.jvmargs" if [ $AGENT_TARGET = "test" ]; then AGENT_PARAMETER_NAME="-Dandroidx.room.testJvmArgs" fi $GRADLEW --no-daemon \ --init-script $SCRIPT_DIR/rerun-requested-task-init-script.gradle \ --init-script $SCRIPT_DIR/attach-async-profiler-to-tests-init-script.gradle \ -p $PROJECT_DIR \ --no-configuration-cache \ $GRADLE_ARGS \ -Dkotlin.compiler.execution.strategy="in-process" \ $AGENT_PARAMETER_NAME="-agentpath:$AGENT_PATH=start,event=cpu,$AGENT_PARAMS,interval=500000" #sample every .5 ms } profile $GRADLE_ARGS, $REPORT_NAME function openFileInChrome { for i in {1..5}; do # wait until file is written sleep 2 if test -f "$1"; then case "`uname`" in Darwin* ) open $1 ;; Linux* ) google-chrome $1 ;; esac return 0 fi done echo "Couldn't find the output file. $OUTPUT_FILE" } # open it in chrome once done openFileInChrome $OUTPUT_FILE