1#!/bin/bash
2#
3#  Copyright (c) 2019, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30set -euo pipefail
31
32OT_TMP_DIR=/tmp/ot-size-report
33readonly OT_TMP_DIR
34
35OT_SHA_NEW=${GITHUB_SHA:-$(git rev-parse HEAD)}
36readonly OT_SHA_NEW
37
38OT_SHA_OLD="$(git cat-file -p "${OT_SHA_NEW}" | grep 'parent ' | head -n1 | cut -d' ' -f2)"
39readonly OT_SHA_OLD
40
41OT_REPORT_FILE_TABLE="${OT_TMP_DIR}/report_table"
42readonly OT_REPORT_FILE_TABLE
43
44OT_REPORT_FILE_PR="${OT_TMP_DIR}/report_pr"
45readonly OT_REPORT_FILE_TABLE
46
47OT_REPORTER="${OT_SIZE_REPORTER-}"
48readonly OT_REPORTER
49
50setup_arm_gcc_7()
51{
52    if arm-none-eabi-gcc --version | grep -q 'Arm Embedded Processors 7'; then
53        return 0
54    fi
55
56    (cd /tmp/ \
57        && wget --tries 4 --no-check-certificate --quiet https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2 \
58        && tar xjf gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2)
59    export PATH=/tmp/gcc-arm-none-eabi-7-2018-q2-update/bin:$PATH
60
61    arm-none-eabi-gcc --version
62}
63
64setup_ninja_build()
65{
66    sudo apt-get --no-install-recommends install -y ninja-build
67}
68
69setup()
70{
71    setup_arm_gcc_7
72    setup_ninja_build
73}
74
75nm_size()
76{
77    arm-none-eabi-nm --print-size --defined-only -C "$1" | cut -d' ' -f2- >nmsize_old
78    arm-none-eabi-nm --print-size --defined-only -C "$2" | cut -d' ' -f2- >nmsize_new
79    diff -Nuar nmsize_old nmsize_new || true
80}
81
82build_nrf52840()
83{
84    case "$1" in
85        ftd)
86            local ot_ftd=ON
87            local ot_mtd=OFF
88            local ot_rcp=ON
89            ;;
90        mtd)
91            local ot_ftd=OFF
92            local ot_mtd=ON
93            local ot_rcp=ON
94            ;;
95        br)
96            local ot_ftd=ON
97            local ot_mtd=OFF
98            local ot_rcp=OFF
99            ;;
100        *)
101            exit 128
102            ;;
103    esac
104
105    case "$2" in
106        new)
107            local sha=${OT_SHA_NEW}
108            local clone_options=("clone")
109            ;;
110        old)
111            local sha=${OT_SHA_OLD}
112            local clone_options=("clone" "no-depend")
113            ;;
114        *)
115            exit 128
116            ;;
117    esac
118
119    local folder="$1_$2"
120    local config_name="ot-core-config-check-size-$1.h"
121    local config_file="../examples/config/${config_name}"
122
123    mkdir -p "${OT_TMP_DIR}/${folder}"
124    script/git-tool "${clone_options[@]}" https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}/${folder}"
125    rm -rf "${OT_TMP_DIR}/${folder}/openthread/*" # replace openthread submodule with latest commit
126    git archive "${sha}" | tar x -C "${OT_TMP_DIR}/${folder}/openthread"
127
128    if [ ! -e "${OT_TMP_DIR}/${folder}/openthread/examples/config/${config_name}" ]; then
129        # Check if the the config headers are not present, copy from
130        # the main sha.
131        case "$1" in
132            br)
133                rm -rf "${OT_TMP_DIR}/${folder}/openthread/*"
134                git archive "${OT_SHA_NEW}" | tar x -C "${OT_TMP_DIR}/${folder}/openthread"
135                ;;
136            *)
137                mkdir -p "${OT_TMP_DIR}/${folder}/openthread/examples/config"
138                cp "./examples/config/${config_name}" "${OT_TMP_DIR}/${folder}/openthread/examples/config"
139                ;;
140        esac
141    fi
142
143    local cur_dir
144
145    cur_dir=$(pwd)
146
147    cd "${OT_TMP_DIR}/${folder}"
148    OT_CMAKE_BUILD_DIR=build script/build nrf52840 UART_trans \
149        -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=${ot_rcp} \
150        -DOT_FTD=${ot_ftd} -DOT_MTD=${ot_mtd} -DOT_RCP=${ot_rcp} \
151        -DBUILD_TESTING=OFF \
152        -DOT_PROJECT_CONFIG="${config_file}" \
153        "$@"
154
155    if [[ $1 == "br" ]]; then
156        mv ./build/bin/ot-cli-ftd ./build/bin/ot-cli-ftd-br
157        mv ./build/lib/libopenthread-ftd.a ./build/lib/libopenthread-ftd-br.a
158        mv ./build/lib/libopenthread-cli-ftd.a ./build/lib/libopenthread-cli-ftd-br.a
159    fi
160
161    cd "${cur_dir}"
162}
163
164generate_table_header()
165{
166    {
167        printf "+----------------------------+----------+----------+----------+----------+----------+\n"
168        printf "| name                       | branch   | text     | data     | bss      | total    |\n"
169        printf "+============================+==========+==========+==========+==========+==========+\n"
170    } >>"${OT_REPORT_FILE_TABLE}"
171
172    {
173        printf "# Size Report of **OpenThread**\n"
174        printf "Merging PR into main\n\n"
175        printf "|  name  |  branch  |  text  | data  | bss  | total |\n"
176        printf "| :----: | :------: | -----: | ----: | ---: | ----: |\n"
177    } >>"${OT_REPORT_FILE_PR}"
178
179    {
180        printf "\n<details><summary>Library files</summary>\n\n\n"
181        printf "|  name  |  branch  |  text  | data  | bss  | total |\n"
182        printf "| :----: | :------: | -----: | ----: | ---: | ----: |\n"
183    } >>"${OT_REPORT_FILE_PR}_libs"
184
185    if [ -n "${OT_REPORTER}" ]; then
186        "${OT_REPORTER}" init OpenThread
187    fi
188
189}
190
191generate_size_diff()
192{
193    local name
194    local old_file
195    local new_file
196
197    old_file="$1"
198    new_file="$2"
199
200    name=$(basename "${old_file}")
201
202    case "${name}" in
203        lib*)
204            table_report_file="${OT_REPORT_FILE_TABLE}_libs"
205            pr_report_file="${OT_REPORT_FILE_PR}"_libs
206            ;;
207        *)
208            table_report_file="${OT_REPORT_FILE_TABLE}"
209            pr_report_file="${OT_REPORT_FILE_PR}"
210            ;;
211    esac
212
213    read -r -a size_old <<<"$(size "${old_file}" | awk '{text+=$1} {bss+=$2} {data+=$3} {total+=$4} END {printf "%d %d %d %d", text, bss, data, total}')"
214    read -r -a size_new <<<"$(size "${new_file}" | awk '{text+=$1} {bss+=$2} {data+=$3} {total+=$4} END {printf "%d %d %d %d", text, bss, data, total}')"
215
216    local -a size_diff
217
218    for i in 0 1 2 3; do
219        size_diff[i]="$((size_new[i] - size_old[i]))"
220        if [[ ${size_diff[i]} != 0 ]]; then
221            size_diff[i]=$(printf '%+d' "${size_diff[i]}")
222        fi
223    done
224
225    # Generate table format report
226
227    {
228        printf "| %-26s | %-8s " "${name}" "${OT_SHA_OLD:0:8}"
229        printf "| %8u | %8u | %8u | %8u |" "${size_old[0]}" "${size_old[1]}" "${size_old[2]}" "${size_old[3]}"
230        printf "\n"
231
232        printf "| %-26s | %-8s " "" "${OT_SHA_NEW:0:8}"
233        printf "| %8u | %8u | %8u | %8u |" "${size_new[0]}" "${size_new[1]}" "${size_new[2]}" "${size_new[3]}"
234        printf "\n"
235
236        printf "| %-26s | %-8s " "" "+/-"
237        printf "| %+8d | %+8d | %+8d | %+8d |" "${size_diff[0]}" "${size_diff[1]}" "${size_diff[2]}" "${size_diff[3]}"
238        printf "\n" >>"${table_report_file}"
239
240        printf "+----------------------------+----------+----------+----------+----------+----------+\n"
241    } >>"${table_report_file}"
242
243    # Generate PR post format report
244
245    {
246        printf "| %s | %s " "${name}" "${OT_SHA_OLD:0:8}"
247        printf "| %u | %u | %u | %u |" "${size_old[0]}" "${size_old[1]}" "${size_old[2]}" "${size_old[3]}"
248        printf "\n"
249
250        printf "|  | %s " "${OT_SHA_NEW:0:8}"
251        printf "| %u | %u | %u | %u |" "${size_new[0]}" "${size_new[1]}" "${size_new[2]}" "${size_new[3]}"
252        printf "\n"
253
254        printf "|  | %s " "+/-"
255        printf "| %+d | %+d | %+d | %+d |" "${size_diff[0]}" "${size_diff[1]}" "${size_diff[2]}" "${size_diff[3]}"
256        printf "\n"
257    } >>"${pr_report_file}"
258
259    if [ -n "${OT_REPORTER}" ]; then
260        "${OT_REPORTER}" size "${old_file}" "${new_file}"
261    fi
262}
263
264generate_report()
265{
266    local type="${1}"
267    shift
268
269    local old_file
270    local new_file
271
272    for file in "$@"; do
273        case "${file}" in
274            lib*)
275                old_file="${OT_TMP_DIR}"/${type}_old/build/lib/"${file}"
276                new_file="${OT_TMP_DIR}"/${type}_new/build/lib/"${file}"
277                ;;
278            *)
279                old_file="${OT_TMP_DIR}"/${type}_old/build/bin/"${file}"
280                new_file="${OT_TMP_DIR}"/${type}_new/build/bin/"${file}"
281                ;;
282        esac
283
284        generate_size_diff "${old_file}" "${new_file}"
285
286        echo "nm_size ${old_file} ${new_file}"
287        nm_size "${old_file}" "${new_file}"
288    done
289}
290
291finalize_report()
292{
293    cat "${OT_REPORT_FILE_TABLE}"
294    cat "${OT_REPORT_FILE_TABLE}_libs"
295
296    printf "</details>" >>${OT_REPORT_FILE_PR}_libs
297    cat "${OT_REPORT_FILE_PR}_libs" >>${OT_REPORT_FILE_PR}
298
299    if [ -n "${OT_REPORTER}" ]; then
300        "${OT_REPORTER}" post
301    fi
302}
303
304size_nrf52840()
305{
306    export OT_SHA_NEW OT_SHA_OLD
307
308    rm -rf "${OT_TMP_DIR}"
309    mkdir -p "${OT_TMP_DIR}"
310
311    if [[ "${GITHUB_ACTIONS+x}" ]]; then
312        git fetch --depth 1 --no-recurse-submodules origin "${OT_SHA_OLD}"
313    fi
314
315    generate_table_header
316
317    build_nrf52840 ftd new "$@"
318    build_nrf52840 mtd new "$@"
319    build_nrf52840 br new "$@"
320
321    build_nrf52840 ftd old "$@"
322    build_nrf52840 mtd old "$@"
323    build_nrf52840 br old "$@"
324
325    local ftd_files=(
326        "ot-cli-ftd"
327        "ot-ncp-ftd"
328        "libopenthread-ftd.a"
329        "libopenthread-cli-ftd.a"
330        "libopenthread-ncp-ftd.a"
331    )
332
333    local mtd_files=(
334        "ot-cli-mtd"
335        "ot-ncp-mtd"
336        "libopenthread-mtd.a"
337        "libopenthread-cli-mtd.a"
338        "libopenthread-ncp-mtd.a"
339    )
340
341    local br_files=(
342        "ot-cli-ftd-br"
343        "libopenthread-ftd-br.a"
344        "libopenthread-cli-ftd-br.a"
345    )
346
347    # `rcp`` is using same config as `ftd`.
348    local rcp_files=(
349        "ot-rcp"
350        "libopenthread-rcp.a"
351        "libopenthread-radio.a"
352    )
353
354    generate_report ftd "${ftd_files[@]}"
355    generate_report mtd "${mtd_files[@]}"
356    generate_report br "${br_files[@]}"
357    generate_report ftd "${rcp_files[@]}"
358
359    finalize_report
360}
361
362main()
363{
364    if [[ $# == 0 ]]; then
365        setup
366        size_nrf52840
367        cd
368    elif [[ $1 == setup ]]; then
369        setup
370    elif [[ $1 == nrf52840 ]]; then
371        shift
372        size_nrf52840 "$@"
373    else
374        echo "USAGE: $0 [setup | nrf52840 [CMAKE_OPTION ...]]"
375        exit 128
376    fi
377}
378
379main "$@"
380