So far we only test CPUID -> CPU def conversion on artificial CPUID data computed from another CPU def. This patch adds the infrastructure to test this conversion on real data gathered from a host CPU and two helper scripts for adding new test data: - cpu-gather.sh runs cpuid tool and qemu-system-x86_64 to get CPUID data from the host CPU; this is what users can be asked to run if they run into an issue with host CPU detection in libvirt - cpu-parse.sh takes the data generated by cpu-gather.sh and creates data files for CPU detection tests The CPUID data queried from QEMU will eventually switch to the format used by query-host-cpu QMP command once QEMU implements it. Until then we just spawn QEMU with -cpu host and query the guest CPU in QOM. They should both provide the same CPUID results, but query-host-cpu does not require any guest CPU to be created by QEMU. Signed-off-by: Jiri Denemark <jdenemar@xxxxxxxxxx> --- tests/Makefile.am | 4 ++ tests/cputest.c | 156 +++++++++++++++++++++++++++++++++++++++- tests/cputestdata/cpu-gather.sh | 35 +++++++++ tests/cputestdata/cpu-parse.sh | 57 +++++++++++++++ 4 files changed, 249 insertions(+), 3 deletions(-) create mode 100755 tests/cputestdata/cpu-gather.sh create mode 100755 tests/cputestdata/cpu-parse.sh diff --git a/tests/Makefile.am b/tests/Makefile.am index 9238a73..8d37298 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -861,6 +861,10 @@ cputest_SOURCES = \ cputest.c \ testutils.c testutils.h cputest_LDADD = $(LDADDS) $(LIBXML_LIBS) +if WITH_QEMU +cputest_SOURCES += testutilsqemu.c testutilsqemu.h +cputest_LDADD += libqemumonitortestutils.la $(qemu_LDADDS) $(GNULIB_LIBS) +endif WITH_QEMU metadatatest_SOURCES = \ metadatatest.c \ diff --git a/tests/cputest.c b/tests/cputest.c index 431b587..2b243bb 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -40,6 +40,12 @@ #include "cpu/cpu_map.h" #include "virstring.h" +#if WITH_QEMU && WITH_YAJL +# include "testutilsqemu.h" +# include "qemumonitortestutils.h" +# include "qemu/qemu_monitor_json.h" +#endif + #define VIR_FROM_THIS VIR_FROM_CPU enum cpuTestBoolWithError { @@ -53,7 +59,10 @@ enum api { API_GUEST_DATA, API_BASELINE, API_UPDATE, - API_HAS_FEATURE + API_HAS_FEATURE, + API_HOST_CPUID, + API_GUEST_CPUID, + API_JSON_CPUID, }; static const char *apis[] = { @@ -61,7 +70,10 @@ static const char *apis[] = { "guest data", "baseline", "update", - "has feature" + "has feature", + "host CPUID", + "guest CPUID", + "json CPUID", }; struct data { @@ -77,6 +89,10 @@ struct data { int result; }; +#if WITH_QEMU && WITH_YAJL +static virQEMUDriver driver; +#endif + static virCPUDefPtr cpuTestLoadXML(const char *arch, const char *name) @@ -458,12 +474,114 @@ cpuTestHasFeature(const void *arg) } +static int +cpuTestCPUID(const void *arg) +{ + const struct data *data = arg; + int ret = -1; + virCPUDataPtr hostData = NULL; + char *hostFile = NULL; + char *host; + virCPUDefPtr cpu = NULL; + char *result = NULL; + + if (virAsprintf(&hostFile, "%s/cputestdata/%s-cpuid-%s.xml", + abs_srcdir, data->arch, data->host) < 0 || + virtTestLoadFile(hostFile, &host) < 0 || + !(hostData = cpuDataParse(host))) + goto cleanup; + + if (VIR_ALLOC(cpu) < 0) + goto cleanup; + + cpu->arch = hostData->arch; + if (data->api == API_GUEST_CPUID) { + cpu->type = VIR_CPU_TYPE_GUEST; + cpu->match = VIR_CPU_MATCH_EXACT; + cpu->fallback = VIR_CPU_FALLBACK_FORBID; + } else { + cpu->type = VIR_CPU_TYPE_HOST; + } + + if (cpuDecode(cpu, hostData, NULL, 0, NULL) < 0) + goto cleanup; + + if (virAsprintf(&result, "cpuid-%s-%s", + data->host, + data->api == API_HOST_CPUID ? "host" : "guest") < 0) + goto cleanup; + + ret = cpuTestCompareXML(data->arch, cpu, result, false); + + cleanup: + cpuDataFree(hostData); + virCPUDefFree(cpu); + VIR_FREE(result); + return ret; +} + + +#if WITH_QEMU && WITH_YAJL +static int +cpuTestJSONCPUID(const void *arg) +{ + const struct data *data = arg; + virCPUDataPtr cpuData = NULL; + virCPUDefPtr cpu = NULL; + qemuMonitorTestPtr testMon = NULL; + char *json = NULL; + char *result = NULL; + int ret = -1; + + if (virAsprintf(&json, "%s/cputestdata/%s-cpuid-%s.json", + abs_srcdir, data->arch, data->host) < 0 || + virAsprintf(&result, "cpuid-%s-json", data->host) < 0) + goto cleanup; + + if (!(testMon = qemuMonitorTestNewFromFile(json, driver.xmlopt, true))) + goto cleanup; + + if (qemuMonitorJSONGetCPUx86Data(qemuMonitorTestGetMonitor(testMon), + "feature-words", &cpuData) < 0) + goto cleanup; + + if (VIR_ALLOC(cpu) < 0) + goto cleanup; + + cpu->arch = cpuData->arch; + cpu->type = VIR_CPU_TYPE_GUEST; + cpu->match = VIR_CPU_MATCH_EXACT; + cpu->fallback = VIR_CPU_FALLBACK_FORBID; + + if (cpuDecode(cpu, cpuData, NULL, 0, NULL) < 0) + goto cleanup; + + ret = cpuTestCompareXML(data->arch, cpu, result, false); + + cleanup: + qemuMonitorTestFree(testMon); + cpuDataFree(cpuData); + virCPUDefFree(cpu); + VIR_FREE(result); + VIR_FREE(json); + return ret; +} +#endif + + static int (*cpuTest[])(const void *) = { cpuTestCompare, cpuTestGuestData, cpuTestBaseline, cpuTestUpdate, - cpuTestHasFeature + cpuTestHasFeature, + cpuTestCPUID, + cpuTestCPUID, +#if WITH_QEMU && WITH_YAJL + cpuTestJSONCPUID, +#else + NULL, +#endif }; @@ -508,6 +626,13 @@ mymain(void) { int ret = 0; +#if WITH_QEMU && WITH_YAJL + if (qemuTestDriverInit(&driver) < 0) + return EXIT_FAILURE; + + virEventRegisterDefaultImpl(); +#endif + #define DO_TEST(arch, api, name, host, cpu, \ models, nmodels, preferred, flags, result) \ do { \ @@ -562,6 +687,27 @@ mymain(void) models == NULL ? 0 : sizeof(models) / sizeof(char *), \ preferred, 0, result) +#if WITH_QEMU && WITH_YAJL +# define DO_TEST_CPUID_JSON(arch, host, json) \ + do { \ + if (json) { \ + DO_TEST(arch, API_JSON_CPUID, host, host, \ + NULL, NULL, 0, NULL, 0, 0); \ + } \ + } while (0) +#else +# define DO_TEST_CPUID_JSON(arch, host) +#endif + +#define DO_TEST_CPUID(arch, host, json) \ + do { \ + DO_TEST(arch, API_HOST_CPUID, host, host, \ + NULL, NULL, 0, NULL, 0, 0); \ + DO_TEST(arch, API_GUEST_CPUID, host, host, \ + NULL, NULL, 0, NULL, 0, 0); \ + DO_TEST_CPUID_JSON(arch, host, json); \ + } while (0) + /* host to host comparison */ DO_TEST_COMPARE("x86", "host", "host", VIR_CPU_COMPARE_IDENTICAL); DO_TEST_COMPARE("x86", "host", "host-better", VIR_CPU_COMPARE_INCOMPATIBLE); @@ -695,6 +841,10 @@ mymain(void) DO_TEST_GUESTDATA("ppc64", "host", "guest-legacy-incompatible", ppc_models, NULL, -1); DO_TEST_GUESTDATA("ppc64", "host", "guest-legacy-invalid", ppc_models, NULL, -1); +#if WITH_QEMU && WITH_YAJL + qemuTestDriverFree(&driver); +#endif + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/cputestdata/cpu-gather.sh b/tests/cputestdata/cpu-gather.sh new file mode 100755 index 0000000..c8439a6 --- /dev/null +++ b/tests/cputestdata/cpu-gather.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# The cpuid tool can be usually found in a package called "cpuid". If your +# distro does not provide such package, you can find the sources or binary +# packages at http://www.etallen.com/cpuid.html + +grep 'model name' /proc/cpuinfo | head -n1 + +cpuid -1r + +echo +qemu=qemu-system-x86_64 +for cmd in /usr/bin/$qemu /usr/bin/qemu-kvm /usr/libexec/qemu-kvm; do + if [[ -x $cmd ]]; then + qemu=$cmd + break + fi +done + +qom_get() +{ + path='/machine/unattached/device[0]' + echo '{"execute":"qom-get","arguments":{"path":"'$path'",' \ + '"property":"'$1'"},"id":"'$1'"}' +} + +$qemu -machine accel=kvm -cpu host -nodefaults -nographic -qmp stdio <<EOF +{"execute":"qmp_capabilities"} +`qom_get feature-words` +`qom_get family` +`qom_get model` +`qom_get stepping` +`qom_get model-id` +{"execute":"quit"} +EOF diff --git a/tests/cputestdata/cpu-parse.sh b/tests/cputestdata/cpu-parse.sh new file mode 100755 index 0000000..1b5ab4a --- /dev/null +++ b/tests/cputestdata/cpu-parse.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Usage: +# ./cpu-gather.sh | ./cpu-parse.sh + +data=`cat` + +model=`sed -ne '/^model name[ ]*:/ {s/^[^:]*: \(.*\)/\1/p; q}' <<<"$data"` + +fname=`sed -e 's/^ *//; + s/ *$//; + s/[ -]\+ \+/ /g; + s/(\([Rr]\|[Tt][Mm]\))//g; + s/.*\(Intel\|AMD\) //; + s/ \(Duo\|Quad\|II X[0-9]\+\) / /; + s/ \(CPU\|Processor\)\>//; + s/ @.*//; + s/ APU .*//; + s/ \(v[0-9]\|SE\)$//; + s/ /-/g' <<<"$model"` +fname="x86-cpuid-$fname" + +xml() +{ + hex='\(0x[0-9a-f]\+\)' + match="$hex $hex: eax=$hex ebx=$hex ecx=$hex edx=$hex" + subst="<cpuid eax_in='\\1' ecx_in='\\2' eax='\\3' ebx='\\4' ecx='\\5' edx='\\6'\\/>" + + echo "<!-- $model -->" + echo "<cpudata arch='x86'>" + sed -ne "s/^ *$match$/ $subst/p" + echo "</cpudata>" +} + +json() +{ + first=true + sed -ne '/{"QMP".*/d; + /{"return": {}}/d; + /{"timestamp":.*/d; + /^{/p' <<<"$data" | \ + while read; do + $first || echo + first=false + json_reformat <<<"$REPLY" | tr -s '\n' + done +} + +xml <<<"$data" >$fname.xml +echo $fname.xml + +json <<<"$data" >$fname.json +if [[ -s $fname.json ]]; then + echo $fname.json +else + rm $fname.new.json +fi -- 2.8.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list