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