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            ;;
109        old)
110            local sha=${OT_SHA_OLD}
111            ;;
112        *)
113            exit 128
114            ;;
115    esac
116
117    local folder="$1_$2"
118    local config_name="ot-core-config-check-size-$1.h"
119    local config_file="../examples/config/${config_name}"
120
121    mkdir -p "${OT_TMP_DIR}/${folder}"
122    script/git-tool clone https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}/${folder}"
123    rm -rf "${OT_TMP_DIR}/${folder}/openthread/*" # replace openthread submodule with latest commit
124    git archive "${sha}" | tar x -C "${OT_TMP_DIR}/${folder}/openthread"
125
126    if [ ! -e "${OT_TMP_DIR}/${folder}/openthread/examples/config/${config_name}" ]; then
127        # Check if the the config headers are not present, copy from
128        # the main sha.
129        case "$1" in
130            br)
131                rm -rf "${OT_TMP_DIR}/${folder}/openthread/*"
132                git archive "${OT_SHA_NEW}" | tar x -C "${OT_TMP_DIR}/${folder}/openthread"
133                ;;
134            *)
135                mkdir -p "${OT_TMP_DIR}/${folder}/openthread/examples/config"
136                cp "./examples/config/${config_name}" "${OT_TMP_DIR}/${folder}/openthread/examples/config"
137                ;;
138        esac
139    fi
140
141    local cur_dir
142
143    cur_dir=$(pwd)
144
145    cd "${OT_TMP_DIR}/${folder}"
146    OT_CMAKE_BUILD_DIR=build script/build nrf52840 UART_trans \
147        -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=${ot_rcp} \
148        -DOT_FTD=${ot_ftd} -DOT_MTD=${ot_mtd} -DOT_RCP=${ot_rcp} \
149        -DBUILD_TESTING=OFF \
150        -DOT_PROJECT_CONFIG="${config_file}"
151
152    if [[ $1 == "br" ]]; then
153        mv ./build/bin/ot-cli-ftd ./build/bin/ot-cli-ftd-br
154        mv ./build/lib/libopenthread-ftd.a ./build/lib/libopenthread-ftd-br.a
155        mv ./build/lib/libopenthread-cli-ftd.a ./build/lib/libopenthread-cli-ftd-br.a
156    fi
157
158    cd "${cur_dir}"
159}
160
161generate_table_header()
162{
163    {
164        printf "+----------------------------+----------+----------+----------+----------+----------+\n"
165        printf "| name                       | branch   | text     | data     | bss      | total    |\n"
166        printf "+============================+==========+==========+==========+==========+==========+\n"
167    } >>"${OT_REPORT_FILE_TABLE}"
168
169    {
170        printf "# Size Report of **OpenThread**\n"
171        printf "Merging PR into main\n\n"
172        printf "|  name  |  branch  |  text  | data  | bss  | total |\n"
173        printf "| :----: | :------: | -----: | ----: | ---: | ----: |\n"
174    } >>"${OT_REPORT_FILE_PR}"
175
176    {
177        printf "\n<details><summary>Library files</summary>\n\n\n"
178        printf "|  name  |  branch  |  text  | data  | bss  | total |\n"
179        printf "| :----: | :------: | -----: | ----: | ---: | ----: |\n"
180    } >>"${OT_REPORT_FILE_PR}_libs"
181
182    if [ -n "${OT_REPORTER}" ]; then
183        "${OT_REPORTER}" init OpenThread
184    fi
185
186}
187
188generate_size_diff()
189{
190    local name
191    local old_file
192    local new_file
193
194    old_file="$1"
195    new_file="$2"
196
197    name=$(basename "${old_file}")
198
199    case "${name}" in
200        lib*)
201            table_report_file="${OT_REPORT_FILE_TABLE}_libs"
202            pr_report_file="${OT_REPORT_FILE_PR}"_libs
203            ;;
204        *)
205            table_report_file="${OT_REPORT_FILE_TABLE}"
206            pr_report_file="${OT_REPORT_FILE_PR}"
207            ;;
208    esac
209
210    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}')"
211    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}')"
212
213    local -a size_diff
214
215    for i in 0 1 2 3; do
216        size_diff[$i]="$((size_new["$i"] - size_old["$i"]))"
217        if [[ ${size_diff["$i"]} != 0 ]]; then
218            size_diff["$i"]=$(printf '%+d' "${size_diff["$i"]}")
219        fi
220    done
221
222    # Generate table format report
223
224    {
225        printf "| %-26s | %-8s " "${name}" "${OT_SHA_OLD:0:8}"
226        printf "| %8u | %8u | %8u | %8u |" "${size_old[0]}" "${size_old[1]}" "${size_old[2]}" "${size_old[3]}"
227        printf "\n"
228
229        printf "| %-26s | %-8s " "" "${OT_SHA_NEW:0:8}"
230        printf "| %8u | %8u | %8u | %8u |" "${size_new[0]}" "${size_new[1]}" "${size_new[2]}" "${size_new[3]}"
231        printf "\n"
232
233        printf "| %-26s | %-8s " "" "+/-"
234        printf "| %+8d | %+8d | %+8d | %+8d |" "${size_diff[0]}" "${size_diff[1]}" "${size_diff[2]}" "${size_diff[3]}"
235        printf "\n" >>"${table_report_file}"
236
237        printf "+----------------------------+----------+----------+----------+----------+----------+\n"
238    } >>"${table_report_file}"
239
240    # Generate PR post format report
241
242    {
243        printf "| %s | %s " "${name}" "${OT_SHA_OLD:0:8}"
244        printf "| %u | %u | %u | %u |" "${size_old[0]}" "${size_old[1]}" "${size_old[2]}" "${size_old[3]}"
245        printf "\n"
246
247        printf "|  | %s " "${OT_SHA_NEW:0:8}"
248        printf "| %u | %u | %u | %u |" "${size_new[0]}" "${size_new[1]}" "${size_new[2]}" "${size_new[3]}"
249        printf "\n"
250
251        printf "|  | %s " "+/-"
252        printf "| %+d | %+d | %+d | %+d |" "${size_diff[0]}" "${size_diff[1]}" "${size_diff[2]}" "${size_diff[3]}"
253        printf "\n"
254    } >>"${pr_report_file}"
255
256    if [ -n "${OT_REPORTER}" ]; then
257        "${OT_REPORTER}" size "${old_file}" "${new_file}"
258    fi
259}
260
261generate_report()
262{
263    local type="${1}"
264    shift
265
266    local old_file
267    local new_file
268
269    for file in "$@"; do
270        case "${file}" in
271            lib*)
272                old_file="${OT_TMP_DIR}"/${type}_old/build/lib/"${file}"
273                new_file="${OT_TMP_DIR}"/${type}_new/build/lib/"${file}"
274                ;;
275            *)
276                old_file="${OT_TMP_DIR}"/${type}_old/build/bin/"${file}"
277                new_file="${OT_TMP_DIR}"/${type}_new/build/bin/"${file}"
278                ;;
279        esac
280
281        generate_size_diff "${old_file}" "${new_file}"
282
283        echo "nm_size ${old_file} ${new_file}"
284        nm_size "${old_file}" "${new_file}"
285    done
286}
287
288finalize_report()
289{
290    cat "${OT_REPORT_FILE_TABLE}"
291    cat "${OT_REPORT_FILE_TABLE}_libs"
292
293    printf "</details>" >>${OT_REPORT_FILE_PR}_libs
294    cat "${OT_REPORT_FILE_PR}_libs" >>${OT_REPORT_FILE_PR}
295
296    if [ -n "${OT_REPORTER}" ]; then
297        "${OT_REPORTER}" post
298    fi
299}
300
301size_nrf52840()
302{
303    export OT_SHA_NEW OT_SHA_OLD
304
305    rm -rf "${OT_TMP_DIR}"
306    mkdir -p "${OT_TMP_DIR}"
307
308    if [[ "${GITHUB_ACTIONS+x}" ]]; then
309        git fetch --depth 1 --no-recurse-submodules origin "${OT_SHA_OLD}"
310    fi
311
312    generate_table_header
313
314    build_nrf52840 ftd new
315    build_nrf52840 mtd new
316    build_nrf52840 br new
317
318    build_nrf52840 ftd old
319    build_nrf52840 mtd old
320    build_nrf52840 br old
321
322    local ftd_files=(
323        "ot-cli-ftd"
324        "ot-ncp-ftd"
325        "libopenthread-ftd.a"
326        "libopenthread-cli-ftd.a"
327        "libopenthread-ncp-ftd.a"
328    )
329
330    local mtd_files=(
331        "ot-cli-mtd"
332        "ot-ncp-mtd"
333        "libopenthread-mtd.a"
334        "libopenthread-cli-mtd.a"
335        "libopenthread-ncp-mtd.a"
336    )
337
338    local br_files=(
339        "ot-cli-ftd-br"
340        "libopenthread-ftd-br.a"
341        "libopenthread-cli-ftd-br.a"
342    )
343
344    # `rcp`` is using same config as `ftd`.
345    local rcp_files=(
346        "ot-rcp"
347        "libopenthread-rcp.a"
348        "libopenthread-radio.a"
349    )
350
351    generate_report ftd "${ftd_files[@]}"
352    generate_report mtd "${mtd_files[@]}"
353    generate_report br "${br_files[@]}"
354    generate_report ftd "${rcp_files[@]}"
355
356    finalize_report
357}
358
359main()
360{
361    if [[ $# == 0 ]]; then
362        setup
363        size_nrf52840
364        cd
365    elif [[ $1 == setup ]]; then
366        setup
367    elif [[ $1 == nrf52840 ]]; then
368        size_nrf52840
369    else
370        echo "USAGE: $0 [setup|nrf52840]"
371        exit 128
372    fi
373}
374
375main "$@"
376