libvirt/src/util/virperf.c

306 lines
7.2 KiB
C
Raw Normal View History

/*
* virperf.c: methods for managing perf events
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Authors:
* Ren Qiaowei <qiaowei.ren@intel.com>
*/
#include <config.h>
#include <sys/ioctl.h>
#if defined HAVE_SYS_SYSCALL_H
# include <sys/syscall.h>
#endif
#include "virperf.h"
#include "viralloc.h"
#include "virerror.h"
#include "virlog.h"
#include "virfile.h"
#include "virstring.h"
#include "virtypedparam.h"
VIR_LOG_INIT("util.perf");
#define VIR_FROM_THIS VIR_FROM_PERF
VIR_ENUM_IMPL(virPerfEvent, VIR_PERF_EVENT_LAST,
"cmt", "mbmt", "mbml");
struct virPerfEvent {
int type;
int fd;
bool enabled;
union {
/* cmt */
struct {
int scale;
} cmt;
} efields;
};
typedef struct virPerfEvent *virPerfEventPtr;
struct virPerf {
struct virPerfEvent events[VIR_PERF_EVENT_LAST];
};
virPerfPtr
virPerfNew(void)
{
size_t i;
virPerfPtr perf;
if (VIR_ALLOC(perf) < 0)
return NULL;
for (i = 0; i < VIR_PERF_EVENT_LAST; i++) {
perf->events[i].type = i;
perf->events[i].fd = -1;
perf->events[i].enabled = false;
}
return perf;
}
void
virPerfFree(virPerfPtr perf)
{
size_t i;
if (perf == NULL)
return;
for (i = 0; i < VIR_PERF_EVENT_LAST; i++) {
if (perf->events[i].enabled)
virPerfEventDisable(perf, i);
}
VIR_FREE(perf);
}
#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
# include <linux/perf_event.h>
static virPerfEventPtr
virPerfGetEvent(virPerfPtr perf,
virPerfEventType type)
{
virPerfEventIsEnabled: Don't crash on shut off domains So imagine the following. You connect read only to a daemon and try to fetch stats for a shut off domain, e.g.: virsh -r domstats $dom but all of a sudden, virsh instead of printing the stats throws the following error at you: error: Disconnected from qemu:///system due to I/O error error: End of file while reading data: Input/output error The daemon crashed. This is its backtrace: #0 0x00007fa43e3751a8 in virPerfEventIsEnabled (perf=0x0, type=VIR_PERF_EVENT_MBMT) at util/virperf.c:241 #1 0x00007fa424a9f042 in qemuDomainGetStatsPerf (driver=0x7fa3f4022a30, dom=0x7fa3f40e24c0, record=0x7fa41c000e20, maxparams=0x7fa4360b38d0, privflags=1) at qemu/qemu_driver.c:19110 #2 0x00007fa424a9f2e7 in qemuDomainGetStats (conn=0x7fa41c001b20, dom=0x7fa3f40e24c0, stats=127, record=0x7fa4360b3970, flags=1) at qemu/qemu_driver.c:19213 #3 0x00007fa424a9f672 in qemuConnectGetAllDomainStats (conn=0x7fa41c001b20, doms=0x7fa41c0017f0, ndoms=1, stats=127, retStats=0x7fa4360b3a50, flags=0) at qemu/qemu_driver.c:19303 #4 0x00007fa43e4e15f6 in virDomainListGetStats (doms=0x7fa41c0017f0, stats=0, retStats=0x7fa4360b3a50, flags=0) at libvirt-domain.c:11615 Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7f28d1a38700 (LWP 16154)] 0x00007f28da4fa1a8 in virPerfEventIsEnabled (perf=0x0, type=VIR_PERF_EVENT_MBMT) at util/virperf.c:241 241 return event->enabled; Problem is, shut off domains don't have priv->perf allocated. Therefore if in frame #1 qemuDomainGetStatsPerf() tries to check if perf events are enabled, NULL is passed to virPerfEventIsEnabled() which due to some incredible implementation dereference it. Fix this by checking whether passed object is not NULL. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2016-06-03 08:09:17 +00:00
if (!perf)
return NULL;
if (type >= VIR_PERF_EVENT_LAST) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Event '%d' is not supported"),
type);
return NULL;
}
return perf->events + type;
}
static int
virPerfRdtEnable(virPerfEventPtr event,
pid_t pid)
{
struct perf_event_attr rdt_attr;
char *buf = NULL;
char *tmp = NULL;
unsigned int event_type, scale;
if (virFileReadAll("/sys/devices/intel_cqm/type",
10, &buf) < 0)
goto error;
if ((tmp = strchr(buf, '\n')))
*tmp = '\0';
if (virStrToLong_ui(buf, NULL, 10, &event_type) < 0) {
virReportSystemError(errno, "%s",
_("failed to get rdt event type"));
goto error;
}
VIR_FREE(buf);
memset(&rdt_attr, 0, sizeof(rdt_attr));
rdt_attr.size = sizeof(rdt_attr);
rdt_attr.type = event_type;
rdt_attr.inherit = 1;
rdt_attr.disabled = 1;
rdt_attr.enable_on_exec = 0;
switch (event->type) {
case VIR_PERF_EVENT_CMT:
if (virFileReadAll("/sys/devices/intel_cqm/events/llc_occupancy.scale",
10, &buf) < 0)
goto error;
if (virStrToLong_ui(buf, NULL, 10, &scale) < 0) {
virReportSystemError(errno, "%s",
_("failed to get cmt scaling factor"));
goto error;
}
event->efields.cmt.scale = scale;
rdt_attr.config = 1;
break;
case VIR_PERF_EVENT_MBMT:
rdt_attr.config = 2;
break;
case VIR_PERF_EVENT_MBML:
rdt_attr.config = 3;
break;
}
event->fd = syscall(__NR_perf_event_open, &rdt_attr, pid, -1, -1, 0);
if (event->fd < 0) {
virReportSystemError(errno,
_("Unable to open perf type=%d for pid=%d"),
event_type, pid);
goto error;
}
if (ioctl(event->fd, PERF_EVENT_IOC_ENABLE) < 0) {
virReportSystemError(errno,
_("Unable to enable perf event for %s"),
virPerfEventTypeToString(event->type));
goto error;
}
event->enabled = true;
return 0;
error:
VIR_FORCE_CLOSE(event->fd);
VIR_FREE(buf);
return -1;
}
int
virPerfEventEnable(virPerfPtr perf,
virPerfEventType type,
pid_t pid)
{
virPerfEventPtr event = virPerfGetEvent(perf, type);
if (event == NULL)
return -1;
switch (type) {
case VIR_PERF_EVENT_CMT:
case VIR_PERF_EVENT_MBMT:
case VIR_PERF_EVENT_MBML:
if (virPerfRdtEnable(event, pid) < 0)
return -1;
break;
case VIR_PERF_EVENT_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected perf event type=%d"), type);
return -1;
}
return 0;
}
int
virPerfEventDisable(virPerfPtr perf,
virPerfEventType type)
{
virPerfEventPtr event = virPerfGetEvent(perf, type);
if (event == NULL)
return -1;
if (ioctl(event->fd, PERF_EVENT_IOC_DISABLE) < 0) {
virReportSystemError(errno,
_("Unable to disable perf event type=%d"),
event->type);
return -1;
}
event->enabled = false;
VIR_FORCE_CLOSE(event->fd);
return 0;
}
bool virPerfEventIsEnabled(virPerfPtr perf,
virPerfEventType type)
{
virPerfEventPtr event = virPerfGetEvent(perf, type);
if (event == NULL)
return false;
return event->enabled;
}
int
virPerfReadEvent(virPerfPtr perf,
virPerfEventType type,
uint64_t *value)
{
virPerfEventPtr event = virPerfGetEvent(perf, type);
if (event == NULL || !event->enabled)
return -1;
if (saferead(event->fd, value, sizeof(uint64_t)) < 0) {
virReportSystemError(errno, "%s",
_("Unable to read cache data"));
return -1;
}
if (type == VIR_PERF_EVENT_CMT)
*value *= event->efields.cmt.scale;
return 0;
}
#else
int
virPerfEventEnable(virPerfPtr perf ATTRIBUTE_UNUSED,
virPerfEventType type ATTRIBUTE_UNUSED,
pid_t pid ATTRIBUTE_UNUSED)
{
virReportSystemError(ENXIO, "%s",
_("Perf not supported on this platform"));
return -1;
}
int
virPerfEventDisable(virPerfPtr perf ATTRIBUTE_UNUSED,
virPerfEventType type ATTRIBUTE_UNUSED)
{
virReportSystemError(ENXIO, "%s",
_("Perf not supported on this platform"));
return -1;
}
bool
virPerfEventIsEnabled(virPerfPtr perf ATTRIBUTE_UNUSED,
virPerfEventType type ATTRIBUTE_UNUSED)
{
return false;
}
int
virPerfReadEvent(virPerfPtr perf ATTRIBUTE_UNUSED,
virPerfEventType type ATTRIBUTE_UNUSED,
uint64_t *value ATTRIBUTE_UNUSED)
{
virReportSystemError(ENXIO, "%s",
_("Perf not supported on this platform"));
return -1;
}
#endif