1#!/bin/bash
2# Copyright (c) 2023 Huawei Device Co., Ltd.
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15set -e
16
17declare -i ret_error=1
18declare SCRIPT_NAME
19SCRIPT_NAME="$(basename "$0")"
20declare JS_PERF_RESULT_PATH=""
21declare OPENHARMONY_ROOT_PATH=""
22declare D8_BINARY_PATH="/usr/bin/v8/d8"
23declare D8_DEVICE_DIR=""
24declare CONFIG_PATH
25CONFIG_PATH="$(dirname "${BASH_SOURCE[0]}")"/config.json
26declare VER_PLATFORM="full_x86_64"
27declare -a BENCH_FILTER=()
28declare -a EXCLUDE_BENCHMARKS=()
29declare NO_NEED_DOWNLOAD_BENCHS=false
30declare BENCH_MULTIPLIER=""
31declare HDC_PATH=""
32declare TASKSET_MASK=""
33declare -i ITERATIONS=1
34
35function echo_fatal()
36{
37    echo "Error: $1"
38    exit 1
39}
40
41function init()
42{
43    while [[ -n "$1" ]]
44    do
45        case $1 in
46            --help|-h)
47                usage
48                exit 0
49                ;;
50            --d8-host-path)
51                shift
52                D8_BINARY_PATH="$1"
53                ;;
54            --d8-device-dir)
55                shift
56                D8_DEVICE_DIR="$1"
57                ;;
58            --platform)
59                shift
60                VER_PLATFORM="$1"
61                ;;
62            --config)
63                shift
64                CONFIG_PATH="$1"
65                ;;
66            --bench-filter=*)
67                local BENCH_FILTER_STR="${1#*=}"
68                BENCH_FILTER=(${BENCH_FILTER_STR//:/ })
69                ;;
70            --exclude=*)
71                local EXCLUDE_BENCHS_STR="${1#*=}"
72                EXCLUDE_BENCHMARKS=(${EXCLUDE_BENCHS_STR//:/ })
73                ;;
74            --no-download-benchs)
75                NO_NEED_DOWNLOAD_BENCHS=true
76                ;;
77            --multiplier)
78                shift
79                BENCH_MULTIPLIER=$1
80                ;;
81            --iterations)
82                shift
83                ITERATIONS=$1
84                ;;
85            --hdc-path)
86                shift
87                HDC_PATH="$1"
88                ;;
89            --taskset)
90                shift
91                TASKSET_MASK="$1"
92                ;;
93            *)
94                JS_PERF_RESULT_PATH="$1"
95                OPENHARMONY_ROOT_PATH="$2"
96                break
97                ;;
98        esac
99        shift
100    done
101    if [[ "${VER_PLATFORM}" == *arm64* ]]
102    then
103        if [[ -z "${D8_DEVICE_DIR}" ]]
104        then
105            echo_fatal "--d8-device-dir is required for device"
106        fi
107        if [[ ! -d "${D8_DEVICE_DIR}" ]]
108        then
109            echo_fatal "${D8_DEVICE_DIR}: directory with v8 libs for device does not exist"
110        fi
111        if [[ ! -f "${D8_DEVICE_DIR}/d8" ]]
112        then
113            echo_fatal "${D8_DEVICE_DIR}/d8 is required, but does not exist"
114        fi
115        if [[ ! -f "${D8_DEVICE_DIR}/snapshot_blob.bin" ]]
116        then
117            echo_fatal "${D8_DEVICE_DIR}/snapshot_blob.bin is required, but does not exist"
118        fi
119        if [[ ! -f "${D8_DEVICE_DIR}/icudtl.dat" ]]
120        then
121            echo_fatal "${D8_DEVICE_DIR}/icudtl.dat is required, but does not exist"
122        fi
123        if [[ -z "${HDC_PATH}" ]]
124        then
125            if ! command -v hdc
126            then
127                echo_fatal "hdc path was not specified"
128            fi
129            HDC_PATH=$(command -v hdc)
130        elif [[ ! -e "${HDC_PATH}" ]]
131        then
132            echo_fatal "Executable file does not exist: ${HDC_PATH}"
133        else
134            HDC_PATH=$(realpath "${HDC_PATH}")
135        fi
136    elif [[ ! -x "${D8_BINARY_PATH}" ]]
137    then
138        echo_fatal "Executable file does not exist: ${D8_BINARY_PATH}"
139    fi
140    if [[ ! -f "${CONFIG_PATH}" ]]
141    then
142        echo_fatal "Config file does not exist: ${CONFIG_PATH}"
143    fi
144    CONFIG_PATH=$(realpath "${CONFIG_PATH}")
145    if [[ -z "${JS_PERF_RESULT_PATH}" ]]
146    then
147        usage
148        echo_fatal "JS_PERF_RESULT_PATH argument is not set"
149    fi
150    if [[ -z "${OPENHARMONY_ROOT_PATH}" ]]
151    then
152        usage
153        echo_fatal "OPENHARMONY_ROOT argument is not set"
154    fi
155    if [[ ! -d "${OPENHARMONY_ROOT_PATH}" ]]
156    then
157        echo_fatal "Path to openharmony root dir does not exist: ${OPENHARMONY_ROOT_PATH}"
158    else
159        OPENHARMONY_ROOT_PATH="$(realpath "${OPENHARMONY_ROOT_PATH}")"
160    fi
161    if [[ ! -d "${JS_PERF_RESULT_PATH}" ]]
162    then
163        mkdir -p "${JS_PERF_RESULT_PATH}"
164    fi
165    JS_PERF_RESULT_PATH="$(realpath "${JS_PERF_RESULT_PATH}")"
166    WORKDIR_PATH="${JS_PERF_RESULT_PATH}/workdir"
167    if [[ -d "${WORKDIR_PATH}" ]]
168    then
169        rm -rf "${WORKDIR_PATH}"
170    fi
171    mkdir -p "${WORKDIR_PATH}"
172    echo "
173OpenHarmony root path: ${OPENHARMONY_ROOT_PATH}
174JS perf results: ${JS_PERF_RESULT_PATH}
175Config: ${CONFIG_PATH}
176Platform: ${VER_PLATFORM}
177"
178}
179
180function check_command_exist()
181{
182    command=$1
183    type "$command" >/dev/null 2>&1
184    echo  $?
185}
186
187function check_pip_component()
188{
189    pip3 list|grep "$1"
190    return $?
191}
192
193function prepare_js_test_files()
194{
195    local -r bench_repo_path="${JS_PERF_RESULT_PATH}/code/arkjs-perf-test"
196    if ! ${NO_NEED_DOWNLOAD_BENCHS}
197    then
198        rm -rf "${bench_repo_path}"
199    fi
200    if [[ ! -d "${bench_repo_path}" ]]
201    then
202        mkdir -p "${bench_repo_path}"
203        git clone -b builtins_test1110 https://gitee.com/dov1s/arkjs-perf-test.git "${bench_repo_path}"
204    fi
205    local -r test_dir="js-perf-test"
206    JS_TEST_PATH="${JS_PERF_RESULT_PATH}/${test_dir}"
207    if [[ -d "${JS_TEST_PATH}" ]]
208    then
209        rm -rf "${JS_TEST_PATH}"
210    fi
211    mkdir -p "${JS_TEST_PATH}"
212    if [[ ${#BENCH_FILTER[@]} -eq 0 ]]
213    then
214        cp -r "${bench_repo_path}/${test_dir}"/* "${JS_TEST_PATH}"
215    else
216        for bench in "${BENCH_FILTER[@]}"
217        do
218            if [[ -d "${bench_repo_path}/${test_dir}/${bench}" ]]
219            then
220                mkdir -p "${JS_TEST_PATH}/${bench}"
221                cp -r "${bench_repo_path}/${test_dir}/${bench}"/* "${JS_TEST_PATH}/${bench}"
222            elif [[ -f "${bench_repo_path}/${test_dir}/${bench}" ]]
223            then
224                mkdir -p "$(dirname "${JS_TEST_PATH}/${bench}")"
225                cp "${bench_repo_path}/${test_dir}/${bench}" "${JS_TEST_PATH}/${bench}"
226            else
227                echo "No benchmarks for filter '${bench}'"
228            fi
229        done
230    fi
231    if [[ ${#EXCLUDE_BENCHMARKS[@]} -ne 0 ]]
232    then
233        for bench in "${EXCLUDE_BENCHMARKS[@]}"
234        do
235            if [[ -e "${JS_TEST_PATH}/${bench}" ]]
236            then
237                rm -rf "${JS_TEST_PATH}/${bench}"
238            else
239                echo "No excluding benchmark '${bench}'"
240            fi
241        done
242    fi
243}
244
245
246function get_cores {
247    local -i cores_count
248    local -i mask=$((16#$1))
249    cores_count=$(${HDC_PATH} shell nproc --all)
250    for i in $(seq 0 $((cores_count - 1)) )
251    do
252        if [[ $((mask & 1)) -ne 0 ]]
253        then
254            cores+=("${i}")
255        fi
256        mask=$((mask >> 1))
257    done
258}
259
260function fix_cores_freq()
261{
262    if [[ -z "${TASKSET_MASK}" ]]
263    then
264        echo "Taskset mask was not set. Frequency for cores will not be fixed"
265        return
266    fi
267    local -a cores=()
268    get_cores "${TASKSET_MASK}"
269    echo "Using cores: ${cores[*]}"
270    declare -i avg_freq
271    declare freqs_str=""
272    declare -a freqs_arr=()
273    declare -i freq_index=0
274    for core in "${cores[@]}"
275    do
276        freqs_str=$(hdc shell "cat /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_available_frequencies")
277        # split string to array
278        freqs_arr=(${freqs_str})
279        freq_index=$(( ${#freqs_arr[@]} / 2 ))
280        avg_freq=${freqs_arr[${freq_index}]}
281        ${HDC_PATH} shell "echo performance > /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_governor"
282        ${HDC_PATH} shell "echo ${avg_freq} > /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_min_freq"
283        ${HDC_PATH} shell "echo ${avg_freq} > /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_max_freq"
284    done
285    echo "Cores info after frequencies fix:"
286    for core in "${cores[@]}"
287    do
288        echo "Core ${core} info:"
289        echo "Governor: $(${HDC_PATH} shell "cat /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_governor")"
290        echo "Min freq: $(${HDC_PATH} shell "cat /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_min_freq")"
291        echo "Max freq: $(${HDC_PATH} shell "cat /sys/devices/system/cpu/cpu${core}/cpufreq/scaling_max_freq")"
292    done
293}
294
295function prepare_device()
296{
297    # Check device
298    local -a devices
299    devices=$(${HDC_PATH} list targets)
300    if [[ ${#devices[@]} -eq 1 ]]
301    then
302        if [[ "${devices[0]}" == "[Empty]"* ]]
303        then
304            echo_fatal "No devices"
305        fi
306        echo "Device id: ${devices[0]}"
307    else
308        echo_fatal "Required immidieatly one device"
309    fi
310    # Create workdir
311    local -r WORKDIR_ON_DEVICE="/data/local/tmp/jsperftest"
312    D8_BINARY_PATH="${WORKDIR_ON_DEVICE}/v8/d8"
313    set -x
314    ${HDC_PATH} shell "rm -rf ${WORKDIR_ON_DEVICE}"
315    ${HDC_PATH} shell "mkdir -p ${WORKDIR_ON_DEVICE}/v8 ${WORKDIR_ON_DEVICE}/lib ${WORKDIR_ON_DEVICE}/thirdparty"
316    ${HDC_PATH} file send "${D8_DEVICE_DIR}"/d8 ${D8_BINARY_PATH}
317    ${HDC_PATH} shell chmod u+x "${D8_BINARY_PATH}"
318    ${HDC_PATH} file send "${D8_DEVICE_DIR}"/snapshot_blob.bin ${WORKDIR_ON_DEVICE}/v8
319    ${HDC_PATH} file send "${D8_DEVICE_DIR}"/icudtl.dat ${WORKDIR_ON_DEVICE}/v8
320    set +x
321    fix_cores_freq
322}
323
324function usage()
325{
326    echo "${SCRIPT_NAME} [options] <JS_PERF_RESULT_PATH> <OPENHARMONY_ROOT>
327Options:
328    --d8-host-path <path>  - path to d8 binary file.
329                             Default: /usr/bin/v8/d8
330    --d8-device-dir <path> - path to dir with d8 for ohos device.
331                             See https://gitee.com/qishui7/ohcompiler-daily/repository/archive/master.zip
332    --hdc-path <hdc_path>  - path to hdc executable file. Use from PATH env variable by default
333    --platform <platform>  - used platform. Possible values in config.json.
334                             Default: full_x86_64
335    --config <config_path> - path to specific json config
336    --taskset mask         - use CPU affinity with mask for benchmark runnings.
337                             Frequency of these cores will be fixed
338    --multiplier N         - iteration multiplier for js benchmarks
339    --iterations N         - number of benchmark launches and get average
340    --bench-filter=BenchDir1:BenchDir2:BenchDir3/bench.js:...
341                           - filter for benchmarks: directory or file
342    --exclude=BenchDir1:BenchDir2:BenchDir3/bench.js:...
343                           - exclude benchmarks from running: directory or file
344    --no-download-benchs   - no download benchmarks from repository if repo already exists
345    --help, -h             - print help info about script
346
347Positional arguments:
348    JS_PERF_RESULT_PATH - directory path to benchmark results
349    OPENHARMONY_ROOT    - path to root directory for ark_js_vm and es2panda
350"
351}
352
353main() 
354{
355    init "$@"
356    cur_path=$(dirname "$(readlink -f "$0")")
357
358    check_command_exist git || { echo "git is not available"; return $ret_error; }
359    check_command_exist unzip || { echo "unzip is not available"; return $ret_error; }
360    check_command_exist jq || { echo "jq is not available"; return $ret_error; }
361    check_command_exist python3 || { echo "python3 is not available"; return $ret_error; }
362    check_pip_component "openpyxl"  || { pip3 install openpyxl; }
363    
364    [ -f "$cur_path/run_js_test.py" ] || { echo "no run_js_test.py, please check it";return $ret_error;}
365
366    if [[ "${VER_PLATFORM}" == *arm64* ]]
367    then
368        prepare_device
369    fi
370   
371    prepare_js_test_files || { return $ret_error; }
372
373    cd "${WORKDIR_PATH}"
374    if [[ -n "${BENCH_MULTIPLIER}" ]]
375    then
376        for js_test in $(find ${JS_TEST_PATH} -name "*js")
377        do
378            python3 "${cur_path}"/prerun_proc.py __MULTIPLIER__="${BENCH_MULTIPLIER}" "${js_test}"
379        done
380    fi
381    mkdir -p "${WORKDIR_PATH}/tmp"
382    echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
383    local -i benchs_time_start benchs_time benchs_minutes
384    benchs_time_start=$(date +%s)
385    python3  "${cur_path}"/run_js_test.py \
386                        -c "${CONFIG_PATH}" \
387                        -bp "${OPENHARMONY_ROOT_PATH}" \
388                        -p "${JS_TEST_PATH}" \
389                        -o "${JS_PERF_RESULT_PATH}" \
390                        -v "${D8_BINARY_PATH}" \
391                        -e "${VER_PLATFORM}" \
392                        --hdc "${HDC_PATH}" \
393                        -t "${TASKSET_MASK}" \
394                        -n "${ITERATIONS}"
395    benchs_time=$(($(date +%s) - benchs_time_start))
396    benchs_minutes=$((benchs_time / 60))
397    echo "Benchmark script time: ${benchs_minutes} min $((benchs_time - benchs_minutes * 60)) sec"
398
399    if [[ "${VER_PLATFORM}" == *arm64* && -n "${TASKSET_MASK}" ]]
400    then
401        echo "Reboot device after fix frequencies for restoring of default values"
402        ${HDC_PATH} shell reboot
403    fi
404}
405
406main "$@"
407exit $?
408