mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-09-09 01:04:59 +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 \
|
cputest.c \
|
||||||
testutils.c testutils.h
|
testutils.c testutils.h
|
||||||
cputest_LDADD = $(LDADDS) $(LIBXML_LIBS)
|
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_SOURCES = \
|
||||||
metadatatest.c \
|
metadatatest.c \
|
||||||
|
160
tests/cputest.c
160
tests/cputest.c
@ -40,6 +40,12 @@
|
|||||||
#include "cpu/cpu_map.h"
|
#include "cpu/cpu_map.h"
|
||||||
#include "virstring.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
|
#define VIR_FROM_THIS VIR_FROM_CPU
|
||||||
|
|
||||||
enum cpuTestBoolWithError {
|
enum cpuTestBoolWithError {
|
||||||
@ -53,7 +59,10 @@ enum api {
|
|||||||
API_GUEST_DATA,
|
API_GUEST_DATA,
|
||||||
API_BASELINE,
|
API_BASELINE,
|
||||||
API_UPDATE,
|
API_UPDATE,
|
||||||
API_HAS_FEATURE
|
API_HAS_FEATURE,
|
||||||
|
API_HOST_CPUID,
|
||||||
|
API_GUEST_CPUID,
|
||||||
|
API_JSON_CPUID,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *apis[] = {
|
static const char *apis[] = {
|
||||||
@ -61,7 +70,10 @@ static const char *apis[] = {
|
|||||||
"guest data",
|
"guest data",
|
||||||
"baseline",
|
"baseline",
|
||||||
"update",
|
"update",
|
||||||
"has feature"
|
"has feature",
|
||||||
|
"host CPUID",
|
||||||
|
"guest CPUID",
|
||||||
|
"json CPUID",
|
||||||
};
|
};
|
||||||
|
|
||||||
struct data {
|
struct data {
|
||||||
@ -77,6 +89,10 @@ struct data {
|
|||||||
int result;
|
int result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if WITH_QEMU && WITH_YAJL
|
||||||
|
static virQEMUDriver driver;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static virCPUDefPtr
|
static virCPUDefPtr
|
||||||
cpuTestLoadXML(const char *arch, const char *name)
|
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 *) = {
|
static int (*cpuTest[])(const void *) = {
|
||||||
cpuTestCompare,
|
cpuTestCompare,
|
||||||
cpuTestGuestData,
|
cpuTestGuestData,
|
||||||
cpuTestBaseline,
|
cpuTestBaseline,
|
||||||
cpuTestUpdate,
|
cpuTestUpdate,
|
||||||
cpuTestHasFeature
|
cpuTestHasFeature,
|
||||||
|
cpuTestCPUID,
|
||||||
|
cpuTestCPUID,
|
||||||
|
#if WITH_QEMU && WITH_YAJL
|
||||||
|
cpuTestJSONCPUID,
|
||||||
|
#else
|
||||||
|
NULL,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -508,6 +630,13 @@ mymain(void)
|
|||||||
{
|
{
|
||||||
int ret = 0;
|
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, \
|
#define DO_TEST(arch, api, name, host, cpu, \
|
||||||
models, nmodels, preferred, flags, result) \
|
models, nmodels, preferred, flags, result) \
|
||||||
do { \
|
do { \
|
||||||
@ -562,6 +691,27 @@ mymain(void)
|
|||||||
models == NULL ? 0 : sizeof(models) / sizeof(char *), \
|
models == NULL ? 0 : sizeof(models) / sizeof(char *), \
|
||||||
preferred, 0, result)
|
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 */
|
/* host to host comparison */
|
||||||
DO_TEST_COMPARE("x86", "host", "host", VIR_CPU_COMPARE_IDENTICAL);
|
DO_TEST_COMPARE("x86", "host", "host", VIR_CPU_COMPARE_IDENTICAL);
|
||||||
DO_TEST_COMPARE("x86", "host", "host-better", VIR_CPU_COMPARE_INCOMPATIBLE);
|
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-incompatible", ppc_models, NULL, -1);
|
||||||
DO_TEST_GUESTDATA("ppc64", "host", "guest-legacy-invalid", 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;
|
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