aboutgitcodebugslistschat
path: root/test/lib/perf_report
blob: d1ef50bfe0d5a5d0125cd3fbe8e84dc7e1b45b08 (plain) (blame)
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
#!/bin/sh
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
#  for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
#  for network namespace/tap device mode
#
# test/lib/perf_report - Prepare JavaScript report for performance tests
#
# Copyright (c) 2021 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>

PERF_INIT=0
PERF_LINK_COUNT=0
PERF_JS="${LOGDIR}/web/perf.js"

PERF_TEMPLATE_HTML="document.write('"'
Throughput in Gbps, latency in µs. Threads are <span style="font-family: monospace;">iperf3</span> threads, <i>passt</i> and <i>pasta</i> are currently single-threaded.<br/>
Click on numbers to show test execution. Measured at head, commit <span style="font-family: monospace;">__commit__</span>.

<style type="text/CSS">
table.passt td { border: 0px solid; padding: 6px; line-height: 1; }
table.passt td { text-align: right; }
table.passt th { text-align: center; font-weight: bold; }
table.passt tr:not(:first-of-type) td:not(:first-of-type) { font-family: monospace; font-weight: bolder; }
table.passt tr:nth-child(3n+0) { background-color: #112315; }
table.passt tr:not(:nth-child(3n+0)) td { background-color: #101010; }
table.passt td:nth-child(6n+7) { background-color: #603302; }
table.passt tr:nth-child(1) { background-color: #363e61; }
td:empty { visibility: hidden; }
</style>

<ul>
<li><p>passt</p>
<table class="passt" width="70%">
	<tr>
		<th/>
		<th id="perf_passt_tcp" colspan="__passt_tcp_cols__">TCP, __passt_tcp_threads__ at __passt_tcp_freq__ GHz</th>
		<th id="perf_passt_udp" colspan="__passt_udp_cols__">UDP, __passt_udp_threads__ at __passt_udp_freq__ GHz</th>
	</tr>
	<tr>
		<td align="right">MTU:</td>
		__passt_tcp_header__
		__passt_udp_header__
	</tr>
	__passt_tcp_LINE__ __passt_udp_LINE__
</table>

<style type="text/CSS">
table.pasta_local td { border: 0px solid; padding: 6px; line-height: 1; }
table.pasta_local td { text-align: right; }
table.pasta_local th { text-align: center; font-weight: bold; }
table.pasta_local tr:not(:first-of-type) td:not(:first-of-type) { font-family: monospace; font-weight: bolder; }
table.pasta_local tr:nth-child(3n+0) { background-color: #112315; }
table.pasta_local tr:not(:nth-child(3n+0)) td { background-color: #101010; }
table.pasta_local td:nth-child(4n+2) { background-color: #603302; }
table.pasta_local tr:nth-child(1) { background-color: #363e61; }
table.pasta td { border: 0px solid; padding: 6px; line-height: 1; }
table.pasta td { text-align: right; }
table.pasta th { text-align: center; font-weight: bold; }
table.pasta tr:not(:first-of-type) td:not(:first-of-type) { font-family: monospace; font-weight: bolder; }
table.pasta tr:nth-child(3n+0) { background-color: #112315; }
table.pasta tr:not(:nth-child(3n+0)) td { background-color: #101010; }
table.pasta td:nth-child(4n+5) { background-color: #603302; }
table.pasta tr:nth-child(1) { background-color: #363e61; }
td:empty { visibility: hidden; }
</style>

</li><li><p>pasta: local connections/traffic</p>
<table class="pasta_local" width="70%">
	<tr>
		<th/>
		<th id="perf_pasta_lo_tcp" colspan="__pasta_lo_tcp_cols__">TCP, __pasta_lo_tcp_threads__ at __pasta_lo_tcp_freq__ GHz</th>
		<th id="perf_pasta_lo_udp" colspan="__pasta_lo_udp_cols__">UDP, __pasta_lo_udp_threads__ at __pasta_lo_udp_freq__ GHz</th>
	</th>
	<tr>
		<td align="right">MTU:</td>
		__pasta_lo_tcp_header__
		__pasta_lo_udp_header__
	</tr>
	__pasta_lo_tcp_LINE__ __pasta_lo_udp_LINE__
</table>

</li><li><p>pasta: connections/traffic via tap</p>
<table class="pasta" width="70%">
	<tr>
		<th/>
		<th id="perf_pasta_tap_tcp" colspan="__pasta_tap_tcp_cols__">TCP, __pasta_tap_tcp_threads__ at __pasta_tap_tcp_freq__ GHz</th>
		<th id="perf_pasta_tap_udp" colspan="__pasta_tap_udp_cols__">UDP, __pasta_tap_udp_threads__ at __pasta_tap_udp_freq__ GHz</th>
	</tr>
	<tr>
		<td align="right">MTU:</td>
		__pasta_tap_tcp_header__
		__pasta_tap_udp_header__
	</tr>
	__pasta_tap_tcp_LINE__ __pasta_tap_udp_LINE__
</table>

</li></ul>'

PERF_TEMPLATE_JS="');

var perf_links = [
"

PERF_TEMPLATE_POST='];

for (var i = 0; i < perf_links.length; i++) {
	var obj = document.getElementById(perf_links[i][0]);

	obj.addEventListener("click", function(event) {
		var ci_video = document.getElementById("ci");
		var top = ci_video.offsetTop - 5;
		var seek;

		for (var i = 0; i < perf_links.length; i++) {
			if (this.id == perf_links[i][0]) {
				seek = perf_links[i][1];
			}
		}

		event.preventDefault();
		ci_player.dispose();
		ci_player = AsciinemaPlayer.create("/builds/latest/web/ci.cast",
						   ci_video,
						   { cols: 240, rows: 51, poster: "npt:999:0", startAt: seek, autoplay: true });

		window.scrollTo({ top: top, behavior: "smooth" })
	}, false);
}
'

# perf_init() - Process first part of template
perf_init() {
        mkdir -p "$(dirname "${PERF_JS}")"
	echo "${PERF_TEMPLATE_HTML}" > "${PERF_JS}"
	perf_report_sub commit "$(echo ${COMMIT} | sed "s/'/\\\'/g")"
	PERF_INIT=1
}

# perf_fill_lines() - Fill multiple "LINE" directives in template, matching rows
perf_fill_lines() {
	while true; do
		__file_line="$(sed -n '/__.*_LINE__/{=;q}' "${PERF_JS}")"
		[ -z "${__file_line}" ] && break

		__line_no=0
		__done=0
		__line_buf="<tr>"
		while true; do
			__match_first_td=0
			for __t in $(sed -n '/__.*_LINE__/{p;q}' "${PERF_JS}"); do
				if [ ${__match_first_td} -eq 1 ]; then
					__matching_line_no=0
					while true; do
						__line_part=
						__var_name="$(echo $__t | sed -n 's/__\(.*\)__/\1_'"${__matching_line_no}"'/p')"
						[ -z "$(eval echo \$${__var_name})" ] && break
						__line_part="$(eval echo \$${__var_name})"
						__td_check="$(echo "${__line_part}" | sed -n 's/^<td>\([^>]*\)<\/td>.*$/\1/p')"
						if [ "${__td_check}" = "${__td_match}" ]; then
							__line_part="$(echo "${__line_part}" | sed -n 's/^<td>[^>]*<\/td>\(.*\)$/\1/p')"
							break
						fi
						__matching_line_no=$((__matching_line_no + 1))
					done
				else
					__var_name="$(echo $__t | sed -n 's/__\(.*\)__/\1_'"${__line_no}"'/p')"
					[ -z "$(eval echo \$${__var_name})" ] && __done=1 && break
					__line_part="$(eval echo \$${__var_name})"
					__td_match="$(echo "${__line_part}" | sed -n 's/^<td>\([^>]*\)<\/td>.*$/\1/p')"
				fi
				__line_buf="${__line_buf}${__line_part}"
				__match_first_td=1
			done
			[ ${__done} -eq 1 ] && break
			__line_no=$((__line_no + 1))
			__line_buf="${__line_buf}</tr><tr>"
		done
		__line_buf="${__line_buf}</tr>"
		__line_buf="$(printf '%s\n' "${__line_buf}" | sed -e 's/[]\/$*.^[]/\\&/g')"
		sed -i "${__file_line}s/.*/${__line_buf}/" "${PERF_JS}"
	done
}

# perf_finish() - Add trailing backslashes and process ending templates
perf_finish() {
	PERF_INIT=0
	perf_fill_lines
	sed -i 's/^.*$/&\\/g' "${PERF_JS}"
	echo "${PERF_TEMPLATE_JS}" >> "${PERF_JS}"
	echo "${PERF_TEMPLATE_POST}" >> "${PERF_JS}"
}

# perf_report_sub() - Apply simple substitutions in template
perf_report_sub() {
	__et="$(printf '%s\n' "${1}" | sed -e 's/[\/&]/\\&/g')"
	__es="$(printf '%s\n' "${2}" | sed -e 's/[]\/$*.^[]/\\&/g')"

	sed -i 's/__'"${__et}"'__/'"${__es}"'/g' "${PERF_JS}"
}

# perf_report_append_js() - Append generic string to current template buffer
perf_report_append_js() {
	PERF_TEMPLATE_JS="${PERF_TEMPLATE_JS}${@}"
}

# perf_report() - Start of single test report
perf_report() {
	__mode="${1}"
	__proto="${2}"
	__threads="${3}"
	__freq="${4}"

	REPORT_IN="${__mode}_${__proto}"

	[ ${__threads} -eq 1 ] && __threads="one thread" || __threads="${__threads} threads"
	perf_report_sub "${__mode}_${__proto}_threads" "${__threads}"
	perf_report_sub "${__mode}_${__proto}_freq" "${__freq}"

	perf_report_append_js "[ 'perf_${__mode}_${__proto}', $(video_time_now) ],"
}

# perf_th() - Table header for a set of tests
perf_th() {
	[ ${PERF_INIT} -eq 0 ] && return

	shift

	__th_buf=
	__cols_count=0
	for __arg; do
		__th_buf="${__th_buf}<td>${__arg}</td>"
		__cols_count=$((__cols_count + 1))
	done
	perf_report_sub "${REPORT_IN}_header" "${__th_buf}"
	perf_report_sub "${REPORT_IN}_cols" ${__cols_count}
}

# perf_tr() - Main table row
perf_tr() {
	[ ${PERF_INIT} -eq 0 ] && return

	__line_no=0
	shift
	while true; do
		[ -z "$(eval echo \$${REPORT_IN}_LINE_${__line_no})" ] && break
		__line_no=$((__line_no + 1))
	done
	eval ${REPORT_IN}_LINE_${__line_no}="\"<td>${@}</td>\""
}

# perf_td() - Single cell with test result
perf_td() {
	[ ${PERF_INIT} -eq 0 ] && return

	__rewind="${1}"
	shift

	__line_no=0
	while true; do
		[ -z "$(eval echo \$${REPORT_IN}_LINE_${__line_no})" ] && break
		__line_no=$((__line_no + 1))
	done
	__line_no=$((__line_no - 1))
	[ -z "${1}" ] && __id=0 || __id="perf_${PERF_LINK_COUNT}"
	eval ${REPORT_IN}_LINE_${__line_no}=\""\${${REPORT_IN}_LINE_${__line_no}}<td id=\"${__id}\">${1}</td>"\"
	[ -z "${1}" ] && return

	perf_report_append_js "[ '${__id}', $(($(video_time_now) - ${__rewind})) ],"
	PERF_LINK_COUNT=$((PERF_LINK_COUNT + 1))
}

# perf_te() - End of a table, currently unused
pert_te() {
	:
}