mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
tests: Add CPU detection tests
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@redhat.com>
This commit is contained in:
parent
a54234c37b
commit
3704b9003f
@ -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 \
|
||||
|
160
tests/cputest.c
160
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,118 @@ 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 = NULL;
|
||||
virCPUDefPtr cpu = NULL;
|
||||
char *result = NULL;
|
||||
|
||||
if (virAsprintf(&hostFile, "%s/cputestdata/%s-cpuid-%s.xml",
|
||||
abs_srcdir, data->arch, data->host) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virTestLoadFile(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:
|
||||
VIR_FREE(hostFile);
|
||||
VIR_FREE(host);
|
||||
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 +630,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 +691,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 +845,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;
|
||||
}
|
||||
|
||||
|
35
tests/cputestdata/cpu-gather.sh
Executable file
35
tests/cputestdata/cpu-gather.sh
Executable file
@ -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
|
57
tests/cputestdata/cpu-parse.sh
Executable file
57
tests/cputestdata/cpu-parse.sh
Executable file
@ -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
|
Loading…
Reference in New Issue
Block a user