1#!/bin/sh
2#
3# This script generates statistics (build size, speed) for successive
4# revisions of the code.  It checks out git commits one an a time, compiles
5# various ports to determine their size, and runs pystone on the unix port.
6# Results are collected in the output file.
7#
8# Note: you will need to copy this file out of the tools directory before
9# executing because it does not exist in old revisions of the repository.
10
11# check that we are in the root directory of the repository
12if [ ! -d py -o ! -d ports/unix -o ! -d ports/stm32 ]; then
13    echo "script must be run from root of the repository"
14    exit 1
15fi
16
17# output file for the data; data is appended if file already exists
18output=codestats.dat
19
20# utility programs
21RM=/bin/rm
22AWK=awk
23MAKE="make -j2"
24
25# these are the binaries that are built; some have 2 or 3 depending on version
26bin_unix=ports/unix/micropython
27bin_stm32=ports/stm32/build-PYBV10/firmware.elf
28bin_barearm_1=ports/bare-arm/build/flash.elf
29bin_barearm_2=ports/bare-arm/build/firmware.elf
30bin_minimal=ports/minimal/build/firmware.elf
31bin_cc3200_1=ports/cc3200/build/LAUNCHXL/application.axf
32bin_cc3200_2=ports/cc3200/build/LAUNCHXL/release/application.axf
33bin_cc3200_3=ports/cc3200/build/WIPY/release/application.axf
34
35# start at zero size; if build fails reuse previous valid size
36size_unix="0"
37size_stm32="0"
38size_barearm="0"
39size_minimal="0"
40size_cc3200="0"
41
42# start at zero pystones
43pystones="0"
44
45# this code runs pystone and averages the results
46pystoneavg=/tmp/pystoneavg.py
47cat > $pystoneavg << EOF
48import pystone
49samples = [pystone.pystones(300000)[1] for i in range(5)]
50samples.sort()
51stones = sum(samples[1:-1]) / (len(samples) - 2) # exclude smallest and largest
52print("stones %g" % stones)
53EOF
54
55function get_size() {
56    if [ -r $2 ]; then
57        size $2 | tail -n1 | $AWK '{print $1}'
58    else
59        echo $1
60    fi
61}
62
63function get_size2() {
64    if [ -r $2 ]; then
65        size $2 | tail -n1 | $AWK '{print $1}'
66    elif [ -r $3 ]; then
67        size $3 | tail -n1 | $AWK '{print $1}'
68    else
69        echo $1
70    fi
71}
72
73function get_size3() {
74    if [ -r $2 ]; then
75        size $2 | tail -n1 | $AWK '{print $1}'
76    elif [ -r $3 ]; then
77        size $3 | tail -n1 | $AWK '{print $1}'
78    elif [ -r $4 ]; then
79        size $4 | tail -n1 | $AWK '{print $1}'
80    else
81        echo $1
82    fi
83}
84
85# get the last revision in the data file; or start at v1.0 if no file
86if [ -r $output ]; then
87    last_rev=$(tail -n1 $output | $AWK '{print $1}')
88else
89    echo "# hash size_unix size_stm32 size_barearm size_minimal size_cc3200 pystones" > $output
90    last_rev="v1.0"
91fi
92
93# get a list of hashes between last revision (exclusive) and master
94hashes=$(git log --format=format:"%H" --reverse ${last_rev}..master)
95#hashes=$(git log --format=format:"%H" --reverse ${last_rev}..master | $AWK '{if (NR % 10 == 0) print $0}') # do every 10th one
96
97for hash in $hashes; do
98
99    #### checkout the revision ####
100
101    git checkout $hash
102    if [ $? -ne 0 ]; then
103        echo "aborting"
104        exit 1
105    fi
106
107    #### apply patches to get it to build ####
108
109    if grep -q '#if defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000000) // POSIX' unix/modtime.c; then
110        echo apply patch
111        git apply - << EOF
112diff --git a/unix/modtime.c b/unix/modtime.c
113index 77d2945..dae0644 100644
114--- a/unix/modtime.c
115+++ b/unix/modtime.c
116@@ -55,10 +55,8 @@ void msec_sleep_tv(struct timeval *tv) {
117 #define MP_CLOCKS_PER_SEC CLOCKS_PER_SEC
118 #endif
119 
120-#if defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000000) // POSIX
121-#define CLOCK_DIV 1000.0
122-#elif defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000) // WIN32
123-#define CLOCK_DIV 1.0
124+#if defined(MP_CLOCKS_PER_SEC)
125+#define CLOCK_DIV (MP_CLOCKS_PER_SEC / 1000.0F)
126 #else
127 #error Unsupported clock() implementation
128 #endif
129EOF
130    fi
131
132    #### unix ####
133
134    $RM $bin_unix
135    $MAKE -C ports/unix CFLAGS_EXTRA=-DNDEBUG
136    size_unix=$(get_size $size_unix $bin_unix)
137
138    # undo patch if it was applied
139    git checkout unix/modtime.c
140
141    #### stm32 ####
142
143    $RM $bin_stm32
144    $MAKE -C ports/stm32 board=PYBV10
145    size_stm32=$(get_size $size_stm32 $bin_stm32)
146
147    #### bare-arm ####
148
149    $RM $bin_barearm_1 $bin_barearm_2
150    $MAKE -C ports/bare-arm
151    size_barearm=$(get_size2 $size_barearm $bin_barearm_1 $bin_barearm_2)
152
153    #### minimal ####
154
155    if [ -r ports/minimal/Makefile ]; then
156        $RM $bin_minimal
157        $MAKE -C ports/minimal CROSS=1
158        size_minimal=$(get_size $size_minimal $bin_minimal)
159    fi
160
161    #### cc3200 ####
162
163    if [ -r ports/cc3200/Makefile ]; then
164        $RM $bin_cc3200_1 $bin_cc3200_2 $bin_cc3200_3
165        $MAKE -C ports/cc3200 BTARGET=application
166        size_cc3200=$(get_size3 $size_cc3200 $bin_cc3200_1 $bin_cc3200_2 $bin_cc3200_3)
167    fi
168
169    #### run pystone ####
170
171    if [ -x $bin_unix ]; then
172        new_pystones=$($bin_unix $pystoneavg)
173        # only update the variable if pystone executed successfully
174        if echo $new_pystones | grep -q "^stones"; then
175            pystones=$(echo $new_pystones | $AWK '{print $2}')
176        fi
177    fi
178
179    #### output data for this commit ####
180
181    echo "$hash $size_unix $size_stm32 $size_barearm $size_minimal $size_cc3200 $pystones" >> $output
182
183done
184
185# checkout master and cleanup
186git checkout master
187$RM $pystoneavg
188