Skip to content

Commit 01c37d3

Browse files
[qemu] Add interactive runner using ssh (#1302)
Summary: Adds a way to get a shell inside qemu using SSH. To run the interactive qemu session without any test runfiles, run: `bazel run //bazel/test_runners/qemu_with_kernel:run_interactive`. To run with a test's runfiles, run `bazel run //<path/to/test>_qemu_interactive`, eg. `bazel run //src/stirling/obj_tools:elf_reader_symbolizer_bpf_test_qemu_interactive`. Note it won't work if you add `--config=qemu-bpf` because of the way bazel's `run_under` flag works, so just leave out the `--config`. Type of change: /kind test-infra. Test Plan: Tested that the interactive runner opens a shell with the expected runfiles. Also tested that closing the shell at any point doesn't leave zombie qemu processes. Signed-off-by: James Bartlett <jamesbartlett@pixielabs.ai>
1 parent c6c9764 commit 01c37d3

20 files changed

Lines changed: 210 additions & 69 deletions

File tree

.bazelrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ test:qemu-bpf --test_tag_filters=requires_bpf,-disabled,-no_qemu
131131
build:gcc --copt -Wno-error=sign-compare
132132
build:gcc --copt -Wno-error=stringop-truncation
133133
build:gcc --copt -Wno-error=maybe-uninitialized
134-
build:gcc --build_tag_filters=-no_gcc
135-
build:gcc --test_tag_filters=-no_gcc,-requires_root,-requires_bpf,-disabled
134+
build:gcc --build_tag_filters=-no_gcc,-qemu_interactive
135+
build:gcc --test_tag_filters=-no_gcc,-requires_root,-requires_bpf,-disabled,-qemu_interactive
136136
build:gcc --//bazel/cc_toolchains:compiler=gcc
137137
test:gcc --config=tmp-sandbox
138138

bazel/pl_build_system.bzl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ load("@io_bazel_rules_docker//go:image.bzl", "go_image")
2121
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_context", "go_library", "go_test")
2222
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
2323
load("@rules_python//python:defs.bzl", "py_test")
24+
load("//bazel:toolchain_transitions.bzl", "qemu_interactive_runner")
2425

2526
pl_supported_go_sdk_versions = ["1.16", "1.17", "1.18", "1.19", "1.20"]
2627

@@ -476,3 +477,27 @@ def pl_py_test(**kwargs):
476477
def pl_sh_test(**kwargs):
477478
_add_test_runner(kwargs)
478479
native.sh_test(**kwargs)
480+
481+
def pl_cc_bpf_test(**kwargs):
482+
pl_cc_test(**kwargs)
483+
qemu_interactive_runner(
484+
name = kwargs["name"] + "_qemu_interactive",
485+
test = ":" + kwargs["name"],
486+
tags = [
487+
"manual",
488+
"qemu_interactive",
489+
],
490+
testonly = True,
491+
)
492+
493+
def pl_sh_bpf_test(**kwargs):
494+
pl_sh_test(**kwargs)
495+
qemu_interactive_runner(
496+
name = kwargs["name"] + "_qemu_interactive",
497+
test = ":" + kwargs["name"],
498+
tags = [
499+
"manual",
500+
"qemu_interactive",
501+
],
502+
testonly = True,
503+
)

bazel/test_runners/qemu_with_kernel/BUILD.bazel

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
1818
load("//bazel:pl_qemu_kernels.bzl", "kernel_flag_name", "qemu_image_to_deps")
19+
load("//bazel:toolchain_transitions.bzl", "qemu_interactive_runner")
1920
load("//bazel/test_runners/qemu_with_kernel:runner.bzl", "qemu_with_kernel_test_runner")
2021

22+
exports_files(["interactive_runner.sh"])
23+
2124
kernel_image_deps = qemu_image_to_deps()
2225

2326
string_flag(
@@ -41,3 +44,12 @@ qemu_with_kernel_test_runner(
4144
kernel_image = select(kernel_select_list),
4245
visibility = ["//visibility:public"],
4346
)
47+
48+
qemu_interactive_runner(
49+
name = "run_interactive",
50+
tags = [
51+
"manual",
52+
"qemu_interactive",
53+
],
54+
visibility = ["//visibility:public"],
55+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env -S -i /bin/bash
2+
3+
# Copyright 2018- The Pixie Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# SPDX-License-Identifier: Apache-2.0
18+
19+
# shellcheck shell=bash
20+
21+
execroot="$(realpath "${PWD%%/execroot/px/*}/execroot")"
22+
# Make OLDPWD look like what bazel's test setup does.
23+
export OLDPWD="${execroot}/px"
24+
export RUNFILES_DIR="$PWD"
25+
export INTERACTIVE_MODE="true"
26+
27+
# shellcheck disable=SC2288
28+
%qemu_runner_path% %test_path%

bazel/test_runners/qemu_with_kernel/launcher.sh

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,13 @@ if [ -f "${RUNFILES_DIR}_manifest" ]; then
109109
rewrite_manifest "${RUNFILES_DIR}_manifest" > "${runfiles_path}_manifest"
110110
fi
111111

112-
# Copy over testlog file.
113-
testlogs_dir="${TEST_WARNINGS_OUTPUT_FILE%%/testlogs/*}/testlogs/"
114-
qemu_warnings_file=$(strip_pwd_from_path "${TEST_WARNINGS_OUTPUT_FILE}")
115-
qemu_testlogs_dir="${qemu_warnings_file/${qemu_warnings_file##*/testlogs/}}"
116-
cp -afL "${testlogs_dir}/" "${tmpdir_for_sandbox}/${qemu_testlogs_dir}/"
112+
if [[ "${INTERACTIVE_MODE}" != "true" ]]; then
113+
# Copy over testlog file.
114+
testlogs_dir="${TEST_WARNINGS_OUTPUT_FILE%%/testlogs/*}/testlogs/"
115+
qemu_warnings_file=$(strip_pwd_from_path "${TEST_WARNINGS_OUTPUT_FILE}")
116+
qemu_testlogs_dir="${qemu_warnings_file/${qemu_warnings_file##*/testlogs/}}"
117+
cp -afL "${testlogs_dir}/" "${tmpdir_for_sandbox}/${qemu_testlogs_dir}/"
118+
fi
117119

118120
# Create test tmp dir.
119121
if [[ -n "${TEST_TMPDIR}" ]]; then
@@ -125,13 +127,27 @@ test_base=${PWD//"${OLDPWD}"/\/test_fs}
125127
test_cmd_path="${tmpdir_for_sandbox}/test_cmd.sh"
126128
test_cmd_path_in_qemu="/test_fs/test_cmd.sh"
127129

128-
cat <<EOF > "${test_cmd_path}"
130+
if [[ "${INTERACTIVE_MODE}" != "true" ]]; then
131+
cat <<EOF > "${test_cmd_path}"
129132
#!/bin/bash -e
130133
source /test_fs/test_env.sh
131134
cd ${test_base}
132135
export TESTING_UNDER_QEMU=true
133136
${@:1}
134137
EOF
138+
else
139+
cat <<EOF > "${test_cmd_path}"
140+
#!/bin/bash
141+
cd ${test_base}
142+
if [[ -n "${@:1}" ]]; then
143+
echo "-------------------------------"
144+
echo "Command to run test: "
145+
echo " ${@:1}"
146+
echo "-------------------------------"
147+
fi
148+
/bin/bash -l
149+
EOF
150+
fi
135151
chmod +x "${test_cmd_path}"
136152

137153
printf "export test_base=%s\n" "${test_base}" >> "${test_env_file}"
@@ -177,6 +193,9 @@ ssh_opts=(
177193
-p "${host_ssh_port}"
178194
root@localhost
179195
)
196+
if [[ "${INTERACTIVE_MODE}" == "true" ]]; then
197+
ssh_opts+=(-t)
198+
fi
180199

181200
# Wait for qemu to boot and ssh to be ready
182201
echo 'Waiting for SSH to come online'
@@ -194,10 +213,12 @@ fi
194213
retval=0
195214
ssh "${ssh_opts[@]}" '/bin/bash -c '"${test_cmd_path_in_qemu}" || retval=$?
196215

197-
# We use a known path to find the testlogs directory so that we can copy the results back from qemu.
198-
testlogs_dir="${TEST_WARNINGS_OUTPUT_FILE%%/testlogs/*}/testlogs/"
199-
qemu_warnings_file=$(strip_pwd_from_path "${TEST_WARNINGS_OUTPUT_FILE}")
200-
qemu_testlogs_dir="${qemu_warnings_file/${qemu_warnings_file##*/testlogs/}}"
201-
cp -afL "${tmpdir_for_sandbox}/${qemu_testlogs_dir}/" "${testlogs_dir}/"
216+
if [[ "${INTERACTIVE_MODE}" != "true" ]]; then
217+
# We use a known path to find the testlogs directory so that we can copy the results back from qemu.
218+
testlogs_dir="${TEST_WARNINGS_OUTPUT_FILE%%/testlogs/*}/testlogs/"
219+
qemu_warnings_file=$(strip_pwd_from_path "${TEST_WARNINGS_OUTPUT_FILE}")
220+
qemu_testlogs_dir="${qemu_warnings_file/${qemu_warnings_file##*/testlogs/}}"
221+
cp -afL "${tmpdir_for_sandbox}/${qemu_testlogs_dir}/" "${testlogs_dir}/"
222+
fi
202223

203224
exit "${retval}"

bazel/test_runners/qemu_with_kernel/run_qemu.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ flags+=(-netdev "user,id=net0,hostfwd=tcp::0-:22")
8484
flags+=(-monitor "unix:${MONITOR_SOCK},server,nowait")
8585

8686
retval=0
87-
qemu-system-x86_64 "${flags[@]}" || retval=$?
87+
exec qemu-system-x86_64 "${flags[@]}" || retval=$?
8888

8989
if [[ "${retval}" -gt 0 ]]; then
9090
if [[ "${retval}" -lt 128 ]]; then

bazel/test_runners/qemu_with_kernel/runner.bzl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,49 @@ qemu_with_kernel_test_runner = rule(
122122
],
123123
executable = True,
124124
)
125+
126+
def _interactive_impl(ctx):
127+
transitive_runfiles = []
128+
transitive_runfiles.append(ctx.attr._qemu_runner[DefaultInfo].default_runfiles)
129+
if ctx.attr.test:
130+
transitive_runfiles.append(ctx.attr.test[DefaultInfo].default_runfiles)
131+
runfiles = ctx.runfiles()
132+
runfiles = runfiles.merge_all(transitive_runfiles)
133+
134+
test_path = ""
135+
if ctx.attr.test:
136+
test_path = ctx.attr.test[DefaultInfo].files_to_run.executable.short_path
137+
# TODO(james): Once bazel supports an ArgsProvider we can add test args here.
138+
139+
output_script = ctx.actions.declare_file(ctx.attr.name + ".sh")
140+
ctx.actions.expand_template(
141+
template = ctx.files._interactive_runner_tpl[0],
142+
output = output_script,
143+
substitutions = {
144+
"%qemu_runner_path%": "bazel/test_runners/qemu_with_kernel/test_runner.sh",
145+
"%test_path%": test_path,
146+
},
147+
is_executable = True,
148+
)
149+
return DefaultInfo(
150+
files = depset(
151+
[output_script],
152+
),
153+
runfiles = runfiles,
154+
executable = output_script,
155+
)
156+
157+
qemu_with_kernel_interactive_runner = rule(
158+
implementation = _interactive_impl,
159+
attrs = {
160+
"test": attr.label(),
161+
"_interactive_runner_tpl": attr.label(
162+
default = Label("//bazel/test_runners/qemu_with_kernel:interactive_runner.sh"),
163+
allow_single_file = True,
164+
),
165+
"_qemu_runner": attr.label(
166+
default = Label("//bazel/test_runners/qemu_with_kernel:runner"),
167+
),
168+
},
169+
executable = True,
170+
)

bazel/toolchain_transitions.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# SPDX-License-Identifier: Apache-2.0
1616

1717
load("@com_github_fmeum_rules_meta//meta:defs.bzl", "meta")
18+
load("//bazel/test_runners/qemu_with_kernel:runner.bzl", "qemu_with_kernel_interactive_runner")
1819

1920
cc_static_musl_binary = meta.wrap_with_transition(
2021
native.cc_binary,
@@ -40,3 +41,11 @@ cc_clang_binary = meta.wrap_with_transition(
4041
},
4142
executable = True,
4243
)
44+
45+
qemu_interactive_runner = meta.wrap_with_transition(
46+
qemu_with_kernel_interactive_runner,
47+
{
48+
"@//bazel/cc_toolchains:libc_version": meta.replace_with("glibc2_36"),
49+
},
50+
executable = True,
51+
)

src/stirling/bpf_tools/BUILD.bazel

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#
1515
# SPDX-License-Identifier: Apache-2.0
1616

17-
load("//bazel:pl_build_system.bzl", "pl_cc_binary", "pl_cc_library", "pl_cc_test")
17+
load("//bazel:pl_build_system.bzl", "pl_cc_binary", "pl_cc_bpf_test", "pl_cc_library", "pl_cc_test")
1818

1919
package(default_visibility = ["//src/stirling:__subpackages__"])
2020

@@ -60,7 +60,7 @@ pl_cc_test(
6060
],
6161
)
6262

63-
pl_cc_test(
63+
pl_cc_bpf_test(
6464
name = "bcc_wrapper_bpf_test",
6565
srcs = ["bcc_wrapper_bpf_test.cc"],
6666
tags = [
@@ -74,7 +74,7 @@ pl_cc_test(
7474
],
7575
)
7676

77-
pl_cc_test(
77+
pl_cc_bpf_test(
7878
name = "bcc_symbolizer_bpf_test",
7979
srcs = ["bcc_symbolizer_bpf_test.cc"],
8080
tags = [
@@ -87,7 +87,7 @@ pl_cc_test(
8787
],
8888
)
8989

90-
pl_cc_test(
90+
pl_cc_bpf_test(
9191
name = "bpftrace_wrapper_bpf_test",
9292
srcs = ["bpftrace_wrapper_bpf_test.cc"],
9393
tags = [
@@ -100,7 +100,7 @@ pl_cc_test(
100100
],
101101
)
102102

103-
pl_cc_test(
103+
pl_cc_bpf_test(
104104
name = "task_struct_resolver_bpf_test",
105105
srcs = ["task_struct_resolver_bpf_test.cc"],
106106
tags = [
@@ -116,7 +116,7 @@ pl_cc_test(
116116
],
117117
)
118118

119-
pl_cc_test(
119+
pl_cc_bpf_test(
120120
name = "uprobe_extra_trigger_bpf_test",
121121
srcs = ["uprobe_extra_trigger_bpf_test.cc"],
122122
tags = [

src/stirling/e2e_tests/BUILD.bazel

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
#
1515
# SPDX-License-Identifier: Apache-2.0
1616

17-
load("//bazel:pl_build_system.bzl", "pl_cc_test", "pl_sh_test")
17+
load("//bazel:pl_build_system.bzl", "pl_cc_bpf_test", "pl_cc_test", "pl_sh_bpf_test", "pl_sh_test")
1818
load("//src/stirling/source_connectors/perf_profiler/testing:testing.bzl", "agent_libs", "agent_libs_arg")
1919

2020
package(default_visibility = ["//src/stirling:__subpackages__"])
2121

22-
pl_cc_test(
22+
pl_cc_bpf_test(
2323
name = "stirling_bpf_test",
2424
timeout = "moderate",
2525
srcs = ["stirling_bpf_test.cc"],
@@ -33,7 +33,7 @@ pl_cc_test(
3333
],
3434
)
3535

36-
pl_cc_test(
36+
pl_cc_bpf_test(
3737
name = "bpf_map_leak_bpf_test",
3838
timeout = "moderate",
3939
srcs = ["bpf_map_leak_bpf_test.cc"],
@@ -64,7 +64,7 @@ pl_cc_test(
6464
],
6565
)
6666

67-
pl_sh_test(
67+
pl_sh_bpf_test(
6868
name = "stirling_wrapper_kprobe_leak_bpf_test",
6969
srcs = ["stirling_wrapper_kprobe_leak_bpf_test.sh"],
7070
args = [
@@ -84,7 +84,7 @@ pl_sh_test(
8484
deps = ["//src/stirling/scripts:sh_library"],
8585
)
8686

87-
pl_sh_test(
87+
pl_sh_bpf_test(
8888
name = "stirling_wrapper_bpf_test",
8989
srcs = ["stirling_wrapper_bpf_test.sh"],
9090
args = [
@@ -110,7 +110,7 @@ pl_sh_test(
110110
deps = ["//src/stirling/scripts:sh_library"],
111111
)
112112

113-
pl_sh_test(
113+
pl_sh_bpf_test(
114114
name = "stirling_wrapper_bpftrace_bpf_test",
115115
srcs = ["stirling_wrapper_bpftrace_bpf_test.sh"],
116116
args = [
@@ -134,7 +134,7 @@ pl_sh_test(
134134
],
135135
)
136136

137-
pl_sh_test(
137+
pl_sh_bpf_test(
138138
name = "stirling_wrapper_container_bpf_test",
139139
srcs = ["stirling_wrapper_container_bpf_test.sh"],
140140
args = [
@@ -163,7 +163,7 @@ pl_sh_test(
163163
deps = ["//src/stirling/scripts:sh_library"],
164164
)
165165

166-
pl_sh_test(
166+
pl_sh_bpf_test(
167167
name = "stirling_perf_bpf_test",
168168
srcs = ["stirling_perf_bpf_test.sh"],
169169
args = [
@@ -188,7 +188,7 @@ pl_sh_test(
188188
deps = ["//src/stirling/scripts:sh_library"],
189189
)
190190

191-
pl_sh_test(
191+
pl_sh_bpf_test(
192192
name = "stirling_wrapper_jvm_stats_bpf_test",
193193
srcs = ["stirling_wrapper_jvm_stats_bpf_test.sh"],
194194
args = [
@@ -230,7 +230,7 @@ pl_sh_test(
230230
deps = ["//src/stirling/scripts:sh_library"],
231231
)
232232

233-
pl_sh_test(
233+
pl_sh_bpf_test(
234234
name = "probe_cleaner_bpf_test",
235235
srcs = ["probe_cleaner_bpf_test.sh"],
236236
args = [

0 commit comments

Comments
 (0)