forked from ThomasKaiser/sbc-bench
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sbc-bench.sh
executable file
·4072 lines (3867 loc) · 178 KB
/
sbc-bench.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
Version=0.9.8
InstallLocation=/usr/local/src # change to /tmp if you want tools to be deleted after reboot
Main() {
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/vc/bin
PathToMe="$( cd "$(dirname "$0")" ; pwd -P )/${0##*/}"
unset LC_ALL LC_MESSAGES LANGUAGE LANG 2>/dev/null # prevent localisation of decimal points and similar stuff
# Check if we're outputting to a terminal. If yes try to use bold and colors for messages
if test -t 1; then
ncolors=$(tput colors)
if test -n "$ncolors" && test $ncolors -ge 8; then
BOLD="$(tput bold)"
NC='\033[0m' # No Color
LGREEN='\033[1;32m'
LRED='\e[0;91m'
fi
fi
# The following allows to use sbc-bench on real ARM devices where for
# whatever reasons a fake /usr/bin/vcgencmd is lying around -- see for
# an example here: https://github.com/ThomasKaiser/sbc-bench/pull/13
VCGENCMD=$(command -v vcgencmd)
if [ -z "${USE_VCGENCMD}" -a -x "${VCGENCMD}" ]; then
# this seems to be a Raspberry Pi where we need to query
# ThreadX on the VC via vcgencmd to get real information
USE_VCGENCMD=true
else
USE_VCGENCMD=false
fi
# check in which mode we're supposed to run
while getopts 'chmtTpN' c ; do
case ${c} in
m)
# monitoring mode
interval=$2
MonitorBoard
exit 0
;;
c)
# Run Cpuminer test (NEON/SSE)
ExecuteCpuminer=yes
;;
h)
# print help
DisplayUsage
exit 0
;;
t|T)
# temperature tests sufficient for heatsink/fan and throttling settings testing
# 2nd argument (integer in degree C) is the value we wait for the board to cool
# down prior to next test
TargetTemp=${2:-50}
TempTest
exit 0
;;
g)
# graph performance chart instead of doing standard 7-zip benchmarks, thereby
# walking through different cpufreq OPP. An additional parameter in taskset's
# --cpu-list format can be provided, eg. '-p 0' to graph only cpu0 or '-p 4'
# to graph only cpu4 (which might be an A72 on big.LITTLE systems). Mixing
# CPUs from different clusters (e.g. '-p 0,4' on a RK3399) will result in
# garbage since big and little cores have other cpufreq OPP.
# 3 special modes exist: cores, clusters and all.
# * '-p cores' will test single-threaded through cluster's cores (0 and 4 on
# RK3399, 0 and 2 on S922X and so on)
# * '-p clusters' tests all cores of each cluster (0-3 and 4-5 on RK3399,
# 0-1 and 2-5 on S922X and so on)
# * '-p all' performs both tests from above and then runs the test on all
# cores as well. This will take ages.
PlotCpufreqOPPs=yes
CPUList=${2}
[ "X${CPUList}" = "X" -o "X${CPUList}" = "Xall" -o "X${CPUList}" = "Xcores" -o "X${CPUList}" = "Xclusters" -o "X${CPUList}" = "Xcoreclusters" ] \
|| taskset -c ${CPUList} echo "foo" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo -e "\nInvalid option \"-p ${CPUList}\". Please check taskset manual page for --cpu-list format" >&2
DisplayUsage
exit 1
fi
;;
p)
# Phoronix Test Suite piggyback mode. PTS lacks sanity checks (measuring
# clockspeeds) and environment monitoring (too much background activity
# or throttling for example). You need to install PTS by yourself and run
# 'phoronix-test-suite batch-setup' once to configure batch operation mode:
#
# Save test results when in batch mode (Y/n): y
# Open the web browser automatically when in batch mode (y/N): n
# Auto upload the results to OpenBenchmarking.org (Y/n): Y
# Prompt for test identifier (Y/n): n
# Prompt for test description (Y/n): n
# Prompt for saved results file-name (Y/n): n
# Run all test options (Y/n): n
#
# If you want PTS progress info use 'tail -f /tmp/sbc-bench.*/temp.log'
# in another shell. You'll need this anyway since if 'Run all test options'
# has been answered with no, then PTS of course expects you giving manual
# feedback even if in batch-benchmark mode.
#
# root@rock-5b:/tmp/sbc-bench.Pvhatc# tail -f temp.log
# SQLite 3.30.1:
# pts/sqlite-2.1.0
# Disk Test Configuration
# 1: 1
# 2: 8
# 3: 32
# 4: 64
# 5: 128
# 6: Test All Options
# ** Multiple items can be selected, delimit by a comma. **
#
# In the invoking shell you need to enter the desired config then PTS
# proceeds.
MODE="pts"
shift
PTSArguments="$@"
;;
N)
# internal Netio monitor mode. Do not use this unless you really know
# what you're doing. Requires enabled 'XML API' on your device (read-only
# w/o password: https://www.netio-products.com/en/glossary/xml-over-https)
# Needs address/name and socket number and a file where results will be
# written to. For example '-N powerbox-1.local 2 /tmp/my-consumption' if
# your Netio device is accessible as powerbox-1.local, this device is
# plugged into its 2nd socket and you want consumption to be written to
# /tmp/my-consumption where it can be picked up by tools like RPi Monitor.
# The last 2 arguments are optional: sleep time (defaults to 0.8) and
# count of samples (defaults to 30). The defaults already put significant
# load on the device (compare http://ix.io/3E91 and http://ix.io/3Ebd) so
# to monitor idle consumption better choose 5 and 30 and deal with a 150
# seconds average value.
MonitorNetio "$2" "$3" "$4" "$5" "$6"
exit 0
;;
esac
done
[ "X${MODE}" = "Xpts" ] || CheckRelease
CheckLoadAndDmesg
[ -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ] && \
read OriginalCPUFreqGovernor </sys/devices/system/cpu/cpufreq/policy0/scaling_governor 2>/dev/null
BasicSetup performance >/dev/null 2>&1
GetTempSensor
[ "X${MODE}" = "Xpts" ] && CheckPTS
InstallPrerequisits
InitialMonitoring
CheckClockspeedsAndSensors
CheckTimeInState before
if [ "${PlotCpufreqOPPs}" = "yes" ]; then
PlotPerformanceGraph
else
CheckNetio
[ "X${MODE}" = "Xpts" ] || RunTinyMemBench
RunRamlat
if [ "X${MODE}" = "Xpts" ]; then
RunPTS
else
RunOpenSSLBenchmark
Run7ZipBenchmark
if [ "${ExecuteCpuminer}" = "yes" -o "X${MODE}" = "Xextensive" ]; then
if [ -x "${InstallLocation}"/cpuminer-multi/cpuminer ]; then
RunCpuminerBenchmark
else
echo -e "\x08\x08 Done.\n(${InstallLocation}/cpuminer-multi/cpuminer missing or not executable)...\c"
fi
fi
fi
fi
CheckTimeInState after
CheckClockspeedsAndSensors # test again loaded system after heating the SoC to the max
SummarizeResults
UploadResults
BasicSetup ${OriginalCPUFreqGovernor} >/dev/null 2>&1
} # Main
GetARMCore() {
grep "${1}/${2}:" <<<"41:Arm
41/810:ARM810
41/920:ARM920
41/922:ARM922
41/926:ARM926
41/940:ARM940
41/946:ARM946
41/966:ARM966
41/a20:ARM1020
41/a22:ARM1022
41/a26:ARM1026
41/b02:ARM11 MPCore
41/b36:ARM1136
41/b56:ARM1156
41/b76:ARM1176
41/c05:Cortex-A5
41/c07:Cortex-A7
41/c08:Cortex-A8
41/c09:Cortex-A9
41/c0d:Cortex-A17
41/c0f:Cortex-A15
41/c0e:Cortex-A17
41/c14:Cortex-R4
41/c15:Cortex-R5
41/c17:Cortex-R7
41/c18:Cortex-R8
41/c20:Cortex-M0
41/c21:Cortex-M1
41/c23:Cortex-M3
41/c24:Cortex-M4
41/c27:Cortex-M7
41/c60:Cortex-M0+
41/d01:Cortex-A32
41/d03:Cortex-A53
41/d04:Cortex-A35
41/d05:Cortex-A55
41/d07:Cortex-A57
41/d08:Cortex-A72
41/d09:Cortex-A73
41/d0a:Cortex-A75
41/d0b:Cortex-A76
41/d0c:Neoverse-N1
41/d0d:Cortex-A77
41/d0e:Cortex-A76AE
41/d13:Cortex-R52
41/d20:Cortex-M23
41/d21:Cortex-M33
41/d22:Cortex-M55
41/d40:Neoverse-V1
41/d41:Cortex-A78
41/d42:Cortex-A78AE
41/d44:Cortex-X1
41/d46:Cortex-A510
41/d47:Cortex-A710
41/d48:Cortex-X2
41/d49:Neoverse-N2
41/d4a:Neoverse-E1
41/d4b:Cortex-A78C
41/d4d:Cortex-A715
41/d4e:Cortex-X3
42:Broadcom
42/00f:Broadcom Brahma B15
42/100:Broadcom Brahma B53
42/516:Broadcom ThunderX2
43:Cavium
43/0a0:Cavium ThunderX
43/0a1:Cavium ThunderX 88XX
43/0a2:Cavium ThunderX 81XX
43/0a3:Cavium ThunderX 83XX
43/0af:Cavium ThunderX2 99xx
44:DEC
44/a10:DEC SA110
44/a11:DEC SA1100
46:Fujitsu
46/001:A64FX
48:HiSilicon
48/d01:Kunpeng-920
49:Infineon
4d:Motorola/Freescale
4e:NVidia
4e/000:NVidia Denver
4e/003:NVidia Denver 2
4e/004:NVidia Carmel
50:APM
50/000:APM X-Gene
51:Qualcomm
51/00f:Qualcomm Scorpion
51/02d:Qualcomm Scorpion
51/04d:Qualcomm Krait
51/06f:Qualcomm Krait
51/201:Qualcomm Kryo
51/205:Qualcomm Kryo
51/211:Qualcomm Kryo
51/800:Qualcomm Falkor V1/Kryo
51/801:Qualcomm Kryo V2
51/803:Qualcomm Kryo 3XX Silver
51/804:Qualcomm Kryo 4XX Gold
51/805:Qualcomm Kryo 4XX Silver
51/c00:Qualcomm Falkor
51/c01:Qualcomm Saphira
53:Samsung
53/001:Samsung Exynos-m1
56:Marvell
56/131:Marvell Feroceon 88FR131
56/581:Marvell PJ4/PJ4b
56/584:Marvell PJ4B-MP
61:Apple
61/020:Apple Icestorm A14
61/021:Apple Firestorm A14
61/022:Apple Icestorm M1
61/023:Apple Firestorm M1
61/024:Apple Icestorm M1Pro
61/025:Apple Firestorm M1Pro
61/028:Apple Icestorm M1Max
61/029:Apple Firestorm M1Max
61/030:Apple Blizzard A15
61/031:Apple Avalanche A15
61/032:Apple Blizzard M2
61/033:Apple Avalanche M2
66:Faraday
66/526:Faraday FA526
66/626:Faraday FA626
69:Intel
69/200:Intel i80200
69/210:Intel PXA250A
69/212:Intel PXA210A
69/242:Intel i80321-400
69/243:Intel i80321-600
69/290:Intel PXA250B/PXA26x
69/292:Intel PXA210B
69/2c2:Intel i80321-400-B0
69/2c3:Intel i80321-600-B0
69/2d0:Intel PXA250C/PXA255/PXA26x
69/2d2:Intel PXA210C
69/411:Intel PXA27x
69/41c:Intel IPX425-533
69/41d:Intel IPX425-400
69/41f:Intel IPX425-266
69/682:Intel PXA32x
69/683:Intel PXA930/PXA935
69/688:Intel PXA30x
69/689:Intel PXA31x
69/b11:Intel SA1110
69/c12:Intel IPX1200
70:Phytium
70/660:Phytium FTC660
70/661:Phytium FTC661
70/662:Phytium FTC662
70/663:Phytium FTC663
c0:Ampere" | cut -f2 -d:
} # GetARMCore
GetCoreType() {
# function that returns name of ARM/RISC-V cores
# $1 is the CPU in question, 1st CPU is always cpu0
case ${CPUArchitecture} in
arm*|aarch*)
if [ -n "${ARMTypes}" ]; then
GetARMCore "${ARMTypes[$(( $1 * 2 ))]}" "${ARMTypes[$(( $(( $1 * 2 )) + 1 ))]}"
fi
;;
riscv*)
# relying on uarch doesn't work with older RISC-V kernels since missing
grep -q '^uarch' /proc/cpuinfo
case $? in
0)
awk -F": " '/^uarch/ {print $2}' /proc/cpuinfo | sed -n $(( $1 + 1 ))p
;;
*)
awk -F": " '/^isa/ {print $2}' /proc/cpuinfo | sed -n $(( $1 + 1 ))p
;;
esac
;;
esac
} # GetCoreType
GetARMStepping() {
# Parse '^CPU variant|^CPU revision' fields from /proc/cpuinfo and transform them
# into 'Stepping' like lscpu does (the latter only showing info for cpu0 so partially
# useless on systems with different CPU clusters)
if [ -n "${ARMStepping}" ]; then
echo "r$(awk -Wposix '{printf("%d", $1)}' <<<${ARMStepping[$(( $1 * 2 ))]})p${ARMStepping[$(( $(( $1 * 2 )) + 1 ))]}"
fi
} # GetARMStepping
GetCPUInfo() {
# function that returns ARM/RISC-V core type in brackets if possible otherwise empty string
CoreType="$(GetCoreType $1)"
[ -n "${CoreType}" ] && echo " (${CoreType})"
} # GetCPUInfo
GetLastClusterCore() {
NextCore=${ClusterConfig[$1]}
[ "${NextCore}" = "" ] && NextCore=$(awk -F" " '/^CPU...:/ {print $2}' <<<"${LSCPU}")
echo -n $(( ${NextCore} - 1 ))
} # GetLastClusterCore
GetLastClusterCoreByType() {
NextCore=${ClusterConfigByCoreType[$1]}
[ "${NextCore}" = "" ] && NextCore=$(awk -F" " '/^CPU...:/ {print $2}' <<<"${LSCPU}")
echo -n $(( ${NextCore} - 1 ))
} # GetLastClusterCoreByType
BashBench(){
# quick integer performance assessment using a simple bash loop.
# Depends not only on CPU performance but also on bash version.
#
# 5.0.17 / E5-2665 @ 2.40GHz: 147485025
# 4.4.20 / Xeon Silver 4110 @ 3.00GHz: 64434988
# 5.0.3 / Xeon Silver 4110 @ 3.00GHz: 38958875
# 5.0.17 / N5100 @ 2.8GHz: 56864747
# 4.4.20 / Allwinner H3: 566944214
# 5.0.3 / BCM2711: 152325522
# 5.0.17 / Apple Firestorm: 28319838
local i
StartTime=$(date +"%s%N")
i=0
while [ $i -lt 10000 ]
do
((i++))
done
FinishedTime=$(date +"%s%N")
RawTime=$(( ${FinishedTime} - ${StartTime} ))
case ${BASH_VERSION} in
5.*)
# multiply by 1.2 since this version seems faster
awk '{printf ("%0.0f",$1*1.2); }' <<<${RawTime}
;;
*)
echo -n ${RawTime}
;;
esac
} # BashBench
PlotPerformanceGraph() {
# function that walks through all cpufreq OPP and plots a performance graph using
# 7-ZIP MIPS. Needs gnuplot and htmldoc (Debian/Ubuntu: gnuplot-nox htmldoc packages)
local i
if [ ! -f /sys/devices/system/cpu/cpufreq/policy0/scaling_governor ]; then
# no cpufreq support -> no way to test through different clockspeeds. Stop
echo -e " Done.\nNo cpufreq support available. Not able to draw performance graph(s)."
exit 1
fi
# repeat every measurement this many times and do not measure any cpufreq below
Repetitions=3 # how many times should each measurement be repeated
SkipBelow=400 # minimum cpufreq in MHz to measure
if [ -n "${OutputCurrent}" ]; then
# We are in Netio monitoring mode, so measure idle consumption first,
# set CPUs to lowest clockspeed
for i in $(ls /sys/devices/system/cpu/cpufreq/policy?/scaling_governor); do
echo powersave >${i}
done
NetioConsumptionFile="${TempDir}/netio.current"
echo -n $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${OutputCurrent[$(( ${NetioSocket} - 1 ))]}" ) * 10 )) >"${NetioConsumptionFile}"
export NetioConsumptionFile
/bin/bash "${PathToMe}" -N ${NetioDevice} ${NetioSocket} ${NetioConsumptionFile} "4.8" "30" >/dev/null 2>&1 &
NetioMonitoringPID=$!
echo -e "\x08\x08 Done.\nTrying to determine idle consumption...\c"
echo -e "System health while idling for 4 minutes:\n" >>${MonitorLog}
/bin/bash "${PathToMe}" -m 30 >>${MonitorLog} &
MonitoringPID=$!
sleep 240
read IdleConsumption <${NetioConsumptionFile}
kill ${NetioMonitoringPID} ${MonitoringPID}
IdleTemp=$(ReadSoCTemp)
echo -e "\n##########################################################################\n\nIdle temperature: ${IdleTemp}°C, idle consumption: $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${IdleConsumption}" ) * 10 ))mW" >>${ResultLog}
fi
# ramp up CPU clockspeeds and continue with normal consumption monitoring
for i in $(ls /sys/devices/system/cpu/cpufreq/policy?/scaling_governor); do
echo performance >${i}
done
CheckNetio
# check if cpulist parameter has been provided as well:
if [ "X${CPUList}" = "X" ]; then
# -p has been used without further restrictions, we run performance test on all cores
CheckPerformance "all CPU cores" $(tr -d '[:space:]' <<<${ClusterConfig[@]})
PlotGraph "all CPU cores" $(tr -d '[:space:]' <<<${ClusterConfig[@]})
RenderPDF
else
# -p with additional options has been called
case ${CPUList} in
cores)
# check each core of every cluster, on RK3399 for example 0 and 4
for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do
FirstCore=${ClusterConfig[$i]}
CheckPerformance "CPU ${FirstCore}" "${FirstCore}" "${FirstCore}"
CPUInfo="$(GetCPUInfo ${FirstCore})"
PlotGraph "CPU ${FirstCore}${CPUInfo}" "${FirstCore}"
done
RenderPDF
;;
clusters)
# check all cores of every cluster, on RK3399 for example 0-3 and 4-5
for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do
FirstCore=${ClusterConfig[$i]}
LastCore=$(GetLastClusterCore $(( $i + 1 )))
CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}"
CPUInfo="$(GetCPUInfo ${FirstCore})"
PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}"
done
RenderPDF
;;
coreclusters)
# check all identical cores of every cluster, on RK3588 for example 0-3 and 4-7
# though this SoC consists of 3 clusters: 0-3 (A55), 4-5 (A76) and 6-7 (A76)
for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do
FirstCore=${ClusterConfigByCoreType[$i]}
LastCore=$(GetLastClusterCoreByType $(( $i + 1 )))
CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}"
CPUInfo="$(GetCPUInfo ${FirstCore})"
PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}"
done
RenderPDF
;;
all)
# check each core of every cluster individually, check cores of each cluster,
# if real clusters and 'clusters of same type' (see RK3588 example above) differ
# then check 'clusters of same type' and then check all cores
for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do
FirstCore=${ClusterConfig[$i]}
CheckPerformance "CPU ${FirstCore}" "${FirstCore}" "${FirstCore}"
CPUInfo="$(GetCPUInfo ${FirstCore})"
PlotGraph "CPU ${FirstCore}${CPUInfo}" "${FirstCore}"
done
for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do
FirstCore=${ClusterConfig[$i]}
LastCore=$(GetLastClusterCore $(( $i + 1 )))
CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}"
CPUInfo="$(GetCPUInfo ${FirstCore})"
PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}"
done
if [ ${#ClusterConfigByCoreType[@]} -ne ${#ClusterConfig[@]} ]; then
for i in $(seq 0 $(( ${#ClusterConfigByCoreType[@]} -1 )) ) ; do
FirstCore=${ClusterConfigByCoreType[$i]}
LastCore=$(GetLastClusterCoreByType $(( $i + 1 )))
CheckPerformance "CPU ${FirstCore}-${LastCore}" "${FirstCore}" "${FirstCore}-${LastCore}"
CPUInfo="$(GetCPUInfo ${FirstCore})"
PlotGraph "CPU ${FirstCore}-${LastCore}${CPUInfo}" "${FirstCore}"
done
fi
if [ ${#ClusterConfig[@]} -gt 1 ]; then
# more than one CPU cluster, we test using all cores simultaneously
CheckPerformance "all CPU cores" $(tr -d '[:space:]' <<<${ClusterConfig[@]})
PlotGraph "all CPU cores" $(tr -d '[:space:]' <<<${ClusterConfig[@]})
fi
RenderPDF
;;
*)
# individual taskset options have been provided, e.g. 0-2 or 3
if [ ${#CPUList} -eq 1 ]; then
# single core to be tested, we need to determine correct policy node
for i in $(seq 0 $(( ${#ClusterConfig[@]} -1 )) ) ; do
FirstCore=${ClusterConfig[$i]}
LastCore=$(GetLastClusterCore $(( $i + 1 )))
if [ ${CPUList} -le ${LastCore} ]; then
CheckPerformance "CPU core(s) ${CPUList}" "${FirstCore}" "${CPUList}"
CPUInfo="$(GetCPUInfo ${CPUList})"
PlotGraph "core ${CPUList}${CPUInfo}" "${FirstCore}"
break
fi
done
else
CheckPerformance "CPU core(s) ${CPUList}" "${CPUList:0:1}" "${CPUList}"
PlotGraph "core(s) ${CPUList}" "${CPUList:0:1}"
fi
RenderPDF
;;
esac
fi
exit 0
} # PlotPerformanceGraph
CheckPerformance() {
# function that gets provided with two or three arguments:
# * $1 test focus to be displayed
# * $2 policy cores: the cores that need to be adjusted when measuring, e.g. "0" for
# cpu0, "4" for cpu4 or for example on an RK3399 "04" to handle both cpu clusters
# at the same time
# * $3 taskset options as provided via the -p switch when calling sbc-bench
local i
if [ -n "${3}" ]; then
# if taskset options are provided ensure that '-mmt=1' is set when only a single
# core is tested.
TasksetOptions="taskset -c ${3} "
if [ ${#3} -eq 1 ]; then
SevenZIPOptions="-mmt=1"
else
SevenZIPOptions=""
fi
else
TasksetOptions=""
SevenZIPOptions=""
fi
Clusters="$(ls -d /sys/devices/system/cpu/cpufreq/policy[${2}])"
echo -e "\x08\x08 Done.\nChecking ${1}: \c"
echo -e "\nSystem health while testing through ${1}:\n" >>${MonitorLog}
/bin/bash "${PathToMe}" -m $(( 30 * ${Repetitions} )) >>${MonitorLog} &
MonitoringPID=$!
SocTemp=$(ReadSoCTemp)
CpufreqDat="${TempDir}/cpufreq${2}.dat"
CpufreqLog="${TempDir}/cpufreq${2}.log"
if [ -s "${NetioConsumptionFile}" ]; then
NetioHeader=" / Watt"
echo -e "0\t0\t${IdleTemp}\t${IdleConsumption}" >"${CpufreqDat}"
echo -n "" >"${CpufreqDat}"
else
echo -n "" >"${CpufreqDat}"
fi
if [ ${USE_VCGENCMD} = true ] ; then
echo -e "Testing through ${1}:\n\nSysfs/ThreadX/Tested: MIPS / Temp${NetioHeader}" >"${CpufreqLog}"
else
echo -e "Testing through ${1}:\n\nSysfs/Tested: MIPS / Temp${NetioHeader}" >"${CpufreqLog}"
fi
# adjust min and max speeds (set max speeds on unaffected clusters to min speed)
for Cluster in $(ls -d /sys/devices/system/cpu/cpufreq/policy?); do
read MinSpeed <${Cluster}/cpuinfo_min_freq
read MaxSpeed <${Cluster}/cpuinfo_max_freq
echo ${MinSpeed} >${Cluster}/scaling_max_freq
done
for Cluster in ${Clusters}; do
read MinSpeed <${Cluster}/cpuinfo_min_freq
read MaxSpeed <${Cluster}/cpuinfo_max_freq
echo ${MinSpeed} >${Cluster}/scaling_min_freq
done
# now walk through higher cluster since this is supposed to provide more cpufreq OPP.
# On ARM usually little cores are the cores with lower numbers.
BiggestCluster="$(sort -n -r <<<${Clusters} | head -n1)"
for i in $((tr " " "\n" <${BiggestCluster}/scaling_available_frequencies ; tr " " "\n" <${BiggestCluster}/scaling_boost_frequencies) 2>/dev/null | sort -n | uniq | sed '/^[[:space:]]*$/d') ; do
# skip measuring cpufreq OPPs below $SkipBelow MHz
if [ $i -lt ${SkipBelow}000 ]; then
continue
fi
# try to set this speed on all clusters
for Cluster in ${Clusters}; do
echo ${i} >${Cluster}/scaling_max_freq
done
sleep 0.1
# if TasksetOptions is not provided measure clockspeed on highest core:
if [ "X${TasksetOptions}" = "X" ]; then
MeasureCore=$(awk '{print substr($0,length,1)}' <<<"${BiggestCluster}")
MeasuredSpeed=$(( $(taskset -c ${MeasureCore} "${InstallLocation}"/mhz/mhz 3 100000 | awk -F" cpu_MHz=" '{s+=$2} END {printf "%.0f", s}') / 3 ))
else
MeasuredSpeed=$(( $(${TasksetOptions} "${InstallLocation}"/mhz/mhz 3 100000 | awk -F" cpu_MHz=" '{s+=$2} END {printf "%.0f", s}') / 3 ))
fi
RoundedSpeed=$(( $(awk '{printf ("%0.0f",$1/10+0.5); }' <<<"${MeasuredSpeed}") * 10 ))
SysfsSpeed=$(( $i / 1000 ))
if [ ${USE_VCGENCMD} = true ] ; then
# On RPi we query ThreadX about clockspeeds too
ThreadXFreq=$("${VCGENCMD}" measure_clock arm | awk -F"=" '{printf ("%0.0f",$2/1000000); }' )
CoreVoltage=$("${VCGENCMD}" measure_volts | cut -f2 -d= | sed 's/000//')
echo -e "$(printf "%4s" ${SysfsSpeed}) / $(printf "%4s" ${ThreadXFreq}) /$(printf "%6s" ${RoundedSpeed}):\c" >>"${CpufreqLog}"
echo -e "${ThreadXFreq}\t\c" >>"${CpufreqDat}"
echo -e "${ThreadXFreq}MHz, \c"
else
echo -e "$(printf "%4s" ${SysfsSpeed}) / $(printf "%4s" ${RoundedSpeed}) :\c" >>"${CpufreqLog}"
echo -e "${MeasuredSpeed}\t\c" >>"${CpufreqDat}"
echo -e "${SysfsSpeed}MHz, \c"
fi
echo -n "" >"${TempDir}/plotvalues"
for check in $(seq 1 ${Repetitions}) ; do
# run 7-zip benchmark
${TasksetOptions} "${SevenZip}" b ${SevenZIPOptions} >${TempLog}
if [ -s "${NetioConsumptionFile}" ]; then
read ConsumptionNow <"${NetioConsumptionFile}"
fi
SocTemp=$(ReadSoCTemp)
MIPS="$(awk -F" " '/^Tot:/ {print $4}' <${TempLog})"
echo -e "${MIPS} ${SocTemp} ${ConsumptionNow}" >>"${TempDir}/plotvalues"
done
AveragedMIPS=$(( $(awk -F" " '{s+=$1} END {printf "%.0f", s}' <"${TempDir}/plotvalues") / ${Repetitions} ))
TempSum=$(awk -F" " '{s+=$2} END {printf "%.1f", s}' <"${TempDir}/plotvalues")
AveragedTemp=$(awk "{printf (\"%0.1f\",\$1/${Repetitions}); }" <<<"${TempSum}")
if [ -s "${NetioConsumptionFile}" ]; then
AveragedWatts=$(( $(awk -F" " '{s+=$3} END {printf "%.0f", s}' <"${TempDir}/plotvalues") / ${Repetitions} ))
ConsumptionInfo="$(printf "%6s" ${AveragedWatts})mW"
fi
echo "$(printf "%6s" ${AveragedMIPS}) $(printf "%5s" ${AveragedTemp})°C${ConsumptionInfo}" >>"${CpufreqLog}"
echo -e "${AveragedMIPS}\t${AveragedTemp}\t${AveragedWatts}" >>"${CpufreqDat}"
done
kill ${MonitoringPID} >/dev/null 2>&1
} # CheckPerformance
PlotGraph() {
# function that gets two arguments provided to graph the performance results:
# * $1 test focus to be displayed
# * $2 policy cores: the cores that need to be adjusted when measuring, e.g. "0" for
# cpu0, "4" for cpu4 or for example on an RK3399 "04" to handle both cpu clusters
# at the same time
# create random filename for graph png
GraphPNG="$(mktemp ${TempDir}/graph.XXXXXX)"
mv "${GraphPNG}" "${GraphPNG}.png"
# adjust y axis range by highest value
MaxMIPS=$(awk '{print $2}' <"${CpufreqDat}" | sort -n | tail -n1)
MaxTemp=$(awk '{print $3}' <"${CpufreqDat}" | sort -n | tail -n1)
Y2Range=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxTemp}")
# Count columns in plot data
CountofColums=$(awk '{print NF}' "${CpufreqDat}" | head -n1)
case ${CountofColums} in
3)
# no consumption numbers, only plot MIPS and temp
YLabel="7-Zip MIPS"
YRange=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxMIPS}")
PlotCommand="plot '${CpufreqDat}' using 1:2 lt rgb 'blue' w l title '7-Zip MIPS ${1}' axis x1y1, '' using 1:3 lt rgb 'green' w l title 'SoC temp' axis x1y2"
;;
4)
# also consumption numbers so include them in the graph too
YLabel="7-Zip MIPS / mW"
MaxWatt=$(awk '{print $4}' <"${CpufreqDat}" | sort -n | tail -n1)
YRangeWatt=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxWatt}")
YRangeMIPS=$(awk '{printf ("%0.0f",$1*1.2); }' <<<"${MaxMIPS}")
if [ ${MaxWatt} -gt ${MaxMIPS} ]; then
YRange=${YRangeWatt}
else
YRange=${YRangeMIPS}
fi
PlotCommand="plot '${CpufreqDat}' using 1:2 lt rgb 'blue' w l title '7-Zip MIPS ${1}' axis x1y1, '' using 1:4 lt rgb 'black' w l title 'Consumption in mW' axis x1y1, '' using 1:3 lt rgb 'green' w l title 'SoC temp' axis x1y2"
;;
esac
# add individual results and ASCII graph to results log
echo -e "\n##########################################################################\n\n$(cat ${TempDir}/cpufreq${2}.log)" >>${ResultLog}
gnuplot-nox -p -e \
"set terminal dumb size 75, 30; set autoscale; set yrange [0:${YRangeMIPS}]; plot '${CpufreqDat}' using 1:2 with lines notitle" \
>>${ResultLog}
# plot PNG with gnuplot
PNGWidth=900
PNGHeight=450
cat <<- EOF | gnuplot-nox
set title '${DeviceName}: ${1}'
set ylabel '${YLabel}'
set ytics nomirror
set y2tics 0,10
set y2label 'degree celsius'
set xlabel 'CPU clockspeed in MHz'
set datafile sep '\t'
set key top left autotitle columnheader
set grid
set autoscale
set yrange [0:${YRange}]
set y2range [0:${Y2Range}]
set terminal png size ${PNGWidth},${PNGHeight}
set output '${GraphPNG}.png'
${PlotCommand}
plot '${CpufreqDat}' using 1:2 lt rgb 'blue' w l title '7-Zip MIPS ${1}' axis x1y1, '' using 1:3 lt rgb 'green' w l title 'SoC temp' axis x1y2
EOF
} # PlotGraph
RenderPDF() {
# fire up monitoring tasks to get device's health after executing the benchmark
# CheckTimeInState after
"${SevenZip}" b >/dev/null 2>&1 & # run 7-zip bench in the background
CheckClockspeedsAndSensors # test again loaded system after heating the SoC to the max
SummarizeResults >/dev/null
# use HTMLdoc to combine graph and text
cat <<- EOF >${TempDir}/report.html
<html>
<head>
<title>sbc-bench performance graph</title>
</head>
<body>
<h3>sbc-bench v${Version} - ${DeviceName} - $(date)</h3>
sbc-bench has been called with <code>-p ${CPUList}</code>
EOF
ls -r --time=atime "${TempDir}"/*.png | while read Graph ; do
echo -e "<img src=\"${Graph}\">" >>${TempDir}/report.html
done
cat <<- EOF >>${TempDir}/report.html
<pre>$(cat ${ResultLog})</pre>
</body>
</html>
EOF
htmldoc --charset utf-8 --headfootfont helvetica-oblique --headfootsize 7 --header ..c --tocheader . --firstpage c1 --quiet --browserwidth 900 --pagemode outline --fontsize 8 --format pdf14 --bodyfont helvetica --bottom 1cm --pagelayout single --left 2.5cm --right 2cm --top 1.7cm --linkstyle plain --linkcolor blue --textcolor black --bodycolor white --links --size 210x297mm --portrait --compression=9 --jpeg=95 --webpage -f "${TempDir}/report.pdf" "${TempDir}/report.html"
if [ -s "${TempDir}/report.pdf" ]; then
FinalPDF="$(mktemp /tmp/sbc-bench.XXXXXX)"
cat "${TempDir}/report.pdf" >"${FinalPDF}"
mv "${FinalPDF}" "${FinalPDF}.pdf"
chmod 644 "${FinalPDF}.pdf"
echo -e "\x08\x08 Done.\n\nPlease check ${FinalPDF}.pdf"
else
echo -e "\x08\x08 Done.\n\nSomething went wrong"
fi
} # RenderPDF
GetTempSensor() {
# In Armbian we can not rely on /etc/armbianmonitor/datasources/soctemp at all any more
# since nobody is left there who cares about /usr/lib/armbian/armbian-hardware-monitor.
# Armbian always chooses /sys/class/hwmon/hwmon0 which can be something totally different
# than the SoC temperature, for example:
# * http://ix.io/3Q5y --> sun4i_ts (touch controller)
# * http://ix.io/3MFz --> w1_slave_temp (1-wire sensor)
# * http://ix.io/411x --> gpu_thermal (obviously _not_ cpu_thermal)
# * http://ix.io/41IL --> iwlwifi_1 (Wi-Fi card)
if [ -f /etc/armbianmonitor/datasources/soctemp ]; then
TempSource=/etc/armbianmonitor/datasources/soctemp
ThermalNode="$(readlink /etc/armbianmonitor/datasources/soctemp)"
if [ -f "${ThermalNode%/*}/name" ]; then
read ThermalSource <"${ThermalNode%/*}/name" 2>/dev/null
elif [ -f "${ThermalNode%/*}/type" ]; then
read ThermalSource <"${ThermalNode%/*}/type" 2>/dev/null
fi
case ${ThermalSource} in
# check name/type of thermal node Armbian 'has chosen' (it's an unmaintained
# mess since 2018)
aml_thermal|cpu|cpu_thermal*|cpu-thermal*|cpu0-thermal*|cpu0_thermal*|soc_thermal*|soc-thermal*|CPU-therm|x86_pkg_temp)
# Seems like a good find
TempInfo="Thermal source: ${ThermalNode%/*}/ (${ThermalSource})"
;;
nvme|w1_slave_temp)
# Obviously wrong
TempInfo="Wrong thermal source: /etc/armbianmonitor/datasources/soctemp (${ThermalSource})"
;;
*)
# Quick results check within few months showed the following types which
# smell all not that good if it's about CPU or SoC temperatures:
# scpi_sensors, w1_slave_temp, iio_hwmon, sun4i_ts, gpu_thermal, iwlwifi_1
NodeGuess=$(cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | sort -n | egrep "cpu|soc|CPU-therm|x86_pkg_temp|thermal-fan-est" | head -n1)
if [ "X${NodeGuess}" != "X" ]; then
# let's use this thermal node instead
TempSource="$(mktemp /tmp/soctemp.XXXXXX)"
ThermalZone="$(GetThermalZone "${NodeGuess}")"
ln -fs ${ThermalZone}/temp ${TempSource}
# TempInfo="Thermal source: ${ThermalZone}/ (${NodeGuess} / Armbian would have chosen ${ThermalSource} instead)"
TempInfo="Thermal source: ${ThermalZone}/ (${NodeGuess})\n (Armbian wants to use ${ThermalNode%/*} instead, that\n zone is named ${ThermalSource}. Please check and if wrong\n file a bug here: https://github.com/armbian/build/issues/)"
else
# use Armbian's 'choice' since no better match was found
OtherTempZones="$(cat /sys/devices/virtual/thermal/thermal_zone?/type | grep -v "${ThermalSource}" | tr '\n' ',' | sed -e 's/,/, /g' -e 's/, $//')"
if [ "X${OtherTempZones}" = "X" ]; then
# no other thermal zones available
TempInfo="Thermal source: ${ThermalNode%/*}/ (${ThermalSource})"
else
# report other thermal zones as well
TempInfo="Thermal source: ${ThermalNode%/*}/ (${ThermalSource})\n (other sensors found: ${OtherTempZones})"
fi
fi
;;
esac
else
TempSource="$(mktemp /tmp/soctemp.XXXXXX)"
# check platform
case $(lscpu | awk -F" " '/^Architecture/ {print $2}') in
x86*|i686)
cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | grep -q x86_pkg_temp
case $? in
0)
# use x86_pkg_temp sensor if available
ThermalZone="$(GetThermalZone x86_pkg_temp)"
ln -fs ${ThermalZone}/temp ${TempSource}
TempInfo="Thermal source: ${ThermalZone}/ (x86_pkg_temp)"
;;
*)
# try to get hwmon node based on thermal driver loaded (idea/feedback courtesy
# @linuxium and @dan-and: https://github.com/ThomasKaiser/sbc-bench/issues/33)
find /sys -name temp1_input -type f 2>/dev/null | while read ; do
read NodeName <"${REPLY%/*}/name"
case "${NodeName}" in
k10temp|k8temp|coretemp)
ln -fs "${REPLY}" ${TempSource}
TempInfo="Thermal source: ${REPLY%/*}/ (${NodeName})"
break
;;
esac
done
if [ ! -h "${TempSource}" ]; then
# still no thermal source found. If there is at least one thermal_zone
# with 'cpu' in its name then use it
CPUSensor="$(cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | grep -i cpu | head -n1)"
if [ "X${CPUSensor}" != "X" ]; then
ThermalZone="$(GetThermalZone "${CPUSensor}")"
ln -fs ${ThermalZone}/temp ${TempSource}
TempInfo="Thermal source: ${ThermalZone}/ (${CPUSensor})"
else
echo 0 >${TempSource}
fi
fi
;;
esac
;;
*)
if [[ -d "/sys/devices/platform/a20-tp-hwmon" ]]; then
# Allwinner A20 with old 3.4 kernel
ln -fs /sys/devices/platform/a20-tp-hwmon/temp1_input ${TempSource}
read ThermalSource </sys/devices/platform/a20-tp-hwmon/name 2>/dev/null && \
TempInfo="Thermal source: /sys/devices/platform/a20-tp-hwmon/ (${ThermalSource})"
else
NodeGuess=$(cat /sys/devices/virtual/thermal/thermal_zone?/type 2>/dev/null | egrep -i "aml_thermal|cpu|soc" | tail -n1)
if [ "X${NodeGuess}" != "X" ]; then
# let's use this thermal node
ThermalZone="$(GetThermalZone "${NodeGuess}")"
ln -fs ${ThermalZone}/temp ${TempSource}
TempInfo="Thermal source: ${ThermalZone}/ (${NodeGuess})"
else
echo 0 >${TempSource}
fi
fi
;;
esac
fi
export TempSource TempInfo
InitialTemp=$(ReadSoCTemp)
} # GetTempSensor
GetThermalZone() {
# get thermal zone for specific type string ($1)
for zone in /sys/devices/virtual/thermal/thermal_zone* ; do
grep -q "${1}" <"${zone}/type"
case $? in
0)
echo ${zone}
return
;;
esac
done
} # GetThermalZone
CheckNetio() {
# Function that checks connection with a Netio powermeter if $Netio is set and if
# successful spawns another execution of this script with -N (Netio monitor mode.).
if [ "X${Netio}" != "X" ]; then
# Try to fetch XML
XMLOutput="$(curl -q --connect-timeout 1 "http://${NetioDevice}/netio.xml" 2>/dev/null | tr '\015' '\012')"
if [ "X${XMLOutput}" = "X" -a "X${MODE}" != "Xunattended" ]; then
echo -e "\nError: not able to fetch \"http://${NetioDevice}/netio.xml\" within a second.\nPlease check parameters and connection manually." >&2
DisplayUsage
exit 1
else
# check current reading of the socket we're supposed to be plugged into
OutputCurrent=($(grep '^<Current>' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' '))
if [ ${OutputCurrent[$(( ${NetioSocket} - 1 ))]} -eq 0 -a "X${MODE}" != "Xunattended" ]; then
echo -e "\nWarning: socket ${NetioSocket} of Netio device ${NetioDevice} provides zero current.\n"
fi
NetioConsumptionFile="${TempDir}/netio.current"
[ -f "${NetioConsumptionFile}" ] || echo -n 0 >"${NetioConsumptionFile}"
export NetioConsumptionFile
/bin/bash "${PathToMe}" -N ${NetioDevice} ${NetioSocket} ${NetioConsumptionFile} >/dev/null 2>&1 &
NetioMonitoringPID=$!
trap "kill ${NetioMonitoringPID} ; rm -rf \"${TempDir}\" ; exit 0" 0 1 2 3 15
fi
fi
} # CheckNetio
MonitorNetio() {
# Poll Netio powermeter accessible at $1 every ~1 second, extract current and
# powerfactor readouts for socket $2, write most recent 30 readouts to a file
# and generate an average number from this to be written to $3
# By also providing $4 and $5 you can adjust poll interval and sample count.
NetioDevice="$1"
NetioSocket="$2"
ConsumptionFile="$3"
SleepInterval=${4:-0.8}
CountofSamples=${5:-30}
# Try to renice to 19 to not interfere with benchmark behaviour
renice 19 $BASHPID >/dev/null 2>&1
while true ; do
XMLOutput="$(curl -q --connect-timeout 1 "http://${NetioDevice}/netio.xml" 2>/dev/null | tr '\015' '\012')"
InputVoltage=$(grep '^<Voltage>' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g')
OutputCurrents=($(grep '^<Current>' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' '))
OutputCurrent=${OutputCurrents[$(( ${NetioSocket} - 1 ))]}
OutputPowerFactors=($(grep '^<PowerFactor>' <<<"${XMLOutput}" | sed -e 's/\(<[^<][^<]*>\)//g' | tr '\n' ' '))
OutputPowerFactor=${OutputPowerFactors[$(( ${NetioSocket} - 1 ))]}
Consumption=$(awk -F" " '{printf ("%0.0f",$1*$2*$3); }' <<<"${InputVoltage} ${OutputCurrent} ${OutputPowerFactor}")
# create a file consisting of $CountofSamples entries with last consumption readouts so we
# can generate an average value based this and $SleepInterval
touch ${TempDir}/netio-socket-${NetioSocket}
PriorValues="$(tail -n${CountofSamples} ${TempDir}/netio-socket-${NetioSocket})"
echo -e "${PriorValues}\n${Consumption}" | sed '/^[[:space:]]*$/d' >${TempDir}/netio-socket-${NetioSocket}
CountOfEntries="$(wc -l <${TempDir}/netio-socket-${NetioSocket})"
SumOfEntries=$(awk '{s+=$1} END {printf "%.0f", s}' <${TempDir}/netio-socket-${NetioSocket})
AverageConsumption=$(( ${SumOfEntries} / ${CountOfEntries} ))
echo -n $(( $(awk '{printf ("%0.0f",$1/10); }' <<<"${AverageConsumption}" ) * 10 )) >"${ConsumptionFile}"
sleep ${SleepInterval}
done
} # MonitorNetio
MonitorBoard() {
[ "X${TempSource}" = "X" ] && GetTempSensor
if test -t 1; then
# when called from a terminal we print some system information first