mirror of https://gitee.com/openkylin/libvirt.git
Add a systemtap script for watching QEMU monitor interactions
This change adds some systemtap/dtrace probes to the QEMU monitor client code. In particular it allows watching of all operations for a VM * examples/systemtap/qemu-monitor.stp: Watch all monitor commands * src/Makefile.am: Passing libdir/bindir/sbindir to dtrace2systemtap.pl * src/dtrace2systemtap.pl: Accept libdir/bindir/sbindir as args and look for '# binary:' comment to mark probes against libvirtd vs libvirt.so * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_text.c: Add probes for key functions
This commit is contained in:
parent
95d3b4de71
commit
9b76b08ae4
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/stap
|
||||
#
|
||||
# Copyright (C) 2011 Red Hat, Inc.
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Author: Daniel P. Berrange <berrange@redhat.com>
|
||||
#
|
||||
# This script will monitor all messages sent/received between libvirt
|
||||
# and the QEMU monitor
|
||||
#
|
||||
# stap qemu-monitor.stp
|
||||
# 0.000 begin
|
||||
# 3.848 ! 0x7f2dc00017b0 {"timestamp": {"seconds": 1319466931, "microseconds": 187755}, "event": "SHUTDOWN"}
|
||||
# 5.773 > 0x7f2dc0007960 {"execute":"qmp_capabilities","id":"libvirt-1"}
|
||||
# 5.774 < 0x7f2dc0007960 {"return": {}, "id": "libvirt-1"}
|
||||
# 5.774 > 0x7f2dc0007960 {"execute":"query-commands","id":"libvirt-2"}
|
||||
# 5.777 < 0x7f2dc0007960 {"return": [{"name": "quit"}, {"name": ....snip....
|
||||
# 5.777 > 0x7f2dc0007960 {"execute":"query-chardev","id":"libvirt-3"}
|
||||
# 5.778 < 0x7f2dc0007960 {"return": [{"filename": ....snip....
|
||||
# 5.779 > 0x7f2dc0007960 {"execute":"query-cpus","id":"libvirt-4"}
|
||||
# 5.780 < 0x7f2dc0007960 {"return": [{"current": true, "CPU": 0, "pc": 1048560, "halted": false, "thread_id": 13299}], "id": "libvirt-4"}
|
||||
# 5.780 > 0x7f2dc0007960 {"execute":"set_password","arguments":{"protocol":"vnc","password":"123456","connected":"keep"},"id":"libvirt-5"}
|
||||
# 5.782 < 0x7f2dc0007960 {"return": {}, "id": "libvirt-5"}
|
||||
# 5.782 > 0x7f2dc0007960 {"execute":"expire_password","arguments":{"protocol":"vnc","time":"never"},"id":"libvirt-6"}
|
||||
# 5.783 < 0x7f2dc0007960 {"return": {}, "id": "libvirt-6"}
|
||||
# 5.783 > 0x7f2dc0007960 {"execute":"balloon","arguments":{"value":224395264},"id":"libvirt-7"}
|
||||
# 5.785 < 0x7f2dc0007960 {"return": {}, "id": "libvirt-7"}
|
||||
# 5.785 > 0x7f2dc0007960 {"execute":"cont","id":"libvirt-8"}
|
||||
# 5.789 ! 0x7f2dc0007960 {"timestamp": {"seconds": 1319466933, "microseconds": 129980}, "event": "RESUME"}
|
||||
# 5.789 < 0x7f2dc0007960 {"return": {}, "id": "libvirt-8"}
|
||||
# 7.537 ! 0x7f2dc0007960 {"timestamp": {"seconds": 1319466934, "microseconds": 881214}, "event": "SHUTDOWN"}
|
||||
#
|
||||
|
||||
|
||||
global start
|
||||
|
||||
# Print a string, with a timestamp relative to the start of the script
|
||||
function print_ts(msg)
|
||||
{
|
||||
now = gettimeofday_ns() / (1000*1000)
|
||||
delta = (now - start)
|
||||
|
||||
printf("%3d.%03d %s\n", (delta / 1000), (delta % 1000), msg);
|
||||
}
|
||||
|
||||
|
||||
# Just so we know the script is now running
|
||||
probe begin {
|
||||
start = gettimeofday_ns() / (1000*1000)
|
||||
print_ts("begin")
|
||||
}
|
||||
|
||||
probe libvirt.qemu.monitor_send_msg {
|
||||
if (fd != -1) {
|
||||
print_ts(sprintf("> %p %s (fd=%d)", mon, substr(msg, 0, strlen(msg)-2), fd));
|
||||
} else {
|
||||
print_ts(sprintf("> %p %s", mon, substr(msg, 0, strlen(msg)-2)));
|
||||
}
|
||||
}
|
||||
|
||||
probe libvirt.qemu.monitor_recv_reply {
|
||||
print_ts(sprintf("< %p %s", mon, reply));
|
||||
}
|
||||
|
||||
probe libvirt.qemu.monitor_recv_event {
|
||||
print_ts(sprintf("! %p %s", mon, event));
|
||||
}
|
|
@ -1288,7 +1288,7 @@ libvirt_functions.stp: $(RPC_PROBE_FILES) $(srcdir)/rpc/gensystemtap.pl
|
|||
$(AM_V_GEN)perl -w $(srcdir)/rpc/gensystemtap.pl $(RPC_PROBE_FILES) > $@
|
||||
|
||||
libvirt_probes.stp: probes.d $(srcdir)/dtrace2systemtap.pl
|
||||
$(AM_V_GEN)perl -w $(srcdir)/dtrace2systemtap.pl $< > $@
|
||||
$(AM_V_GEN)perl -w $(srcdir)/dtrace2systemtap.pl $(bindir) $(sbindir) $(libdir) $< > $@
|
||||
|
||||
CLEANFILES += probes.h probes.o libvirt_functions.stp libvirt_probes.stp
|
||||
endif
|
||||
|
|
|
@ -31,6 +31,10 @@ my $file;
|
|||
my @files;
|
||||
my %files;
|
||||
|
||||
my $bindir = shift @ARGV;
|
||||
my $sbindir = shift @ARGV;
|
||||
my $libdir = shift @ARGV;
|
||||
|
||||
my $probe;
|
||||
my $args;
|
||||
|
||||
|
@ -41,12 +45,18 @@ while (<>) {
|
|||
next if /^\s*provider\s+\w+\s*{\s*$/;
|
||||
next if /^\s*};\s*$/;
|
||||
|
||||
if (m,^\s*\#\s*file:\s*(\S+)\s*$,) {
|
||||
$file = $1;
|
||||
push @files, $file;
|
||||
$files{$file} = { prefix => undef, probes => [] };
|
||||
} elsif (m,^\s*\#\s*prefix:\s*(\S+)\s*$,) {
|
||||
$files{$file}->{prefix} = $1;
|
||||
if (m,^\s*\#,) {
|
||||
if (m,^\s*\#\s*file:\s*(\S+)\s*$,) {
|
||||
$file = $1;
|
||||
push @files, $file;
|
||||
$files{$file} = { prefix => undef, probes => [] };
|
||||
} elsif (m,^\s*\#\s*prefix:\s*(\S+)\s*$,) {
|
||||
$files{$file}->{prefix} = $1;
|
||||
} elsif (m,^\s*\#\s*binary:\s*(\S+)\s*$,) {
|
||||
$files{$file}->{binary} = $1;
|
||||
} else {
|
||||
# ignore unknown comments
|
||||
}
|
||||
} else {
|
||||
if (m,\s*probe\s+([a-zA-Z0-9_]+)\((.*?)(\);)?$,) {
|
||||
$probe = $1;
|
||||
|
@ -84,7 +94,12 @@ foreach my $file (@files) {
|
|||
my $pname = $name;
|
||||
$pname =~ s/${prefix}_/libvirt.$prefix./;
|
||||
|
||||
print "probe $pname = process(\"libvirt.so\").mark(\"$name\") {\n";
|
||||
my $binary = "$libdir/libvirt.so";
|
||||
if (exists $files{$file}->{binary}) {
|
||||
$binary = $sbindir . "/" . $files{$file}->{binary};
|
||||
}
|
||||
|
||||
print "probe $pname = process(\"$binary\").mark(\"$name\") {\n";
|
||||
|
||||
my @args = split /,/, $args;
|
||||
for (my $i = 0 ; $i <= $#args ; $i++) {
|
||||
|
|
20
src/probes.d
20
src/probes.d
|
@ -69,4 +69,24 @@ provider libvirt {
|
|||
probe rpc_tls_session_handshake_pass(void *sess);
|
||||
probe rpc_tls_session_handshake_fail(void *sess);
|
||||
|
||||
|
||||
# file: src/qemu/qemu_monitor.c
|
||||
# prefix: qemu
|
||||
# binary: libvirtd
|
||||
# Monitor lifecycle
|
||||
probe qemu_monitor_new(void *mon, int refs, int fd);
|
||||
probe qemu_monitor_ref(void *mon, int refs);
|
||||
probe qemu_monitor_unref(void *mon, int refs);
|
||||
probe qemu_monitor_close(void *monm, int refs);
|
||||
|
||||
# High level monitor message processing
|
||||
probe qemu_monitor_send_msg(void *mon, const char *msg, int fd);
|
||||
probe qemu_monitor_recv_reply(void *mon, const char *reply);
|
||||
probe qemu_monitor_recv_event(void *mon, const char *event);
|
||||
|
||||
# Low level monitor I/O processing
|
||||
probe qemu_monitor_io_process(void *mon, const char *buf, unsigned int len);
|
||||
probe qemu_monitor_io_read(void *mon, const char *buf, unsigned int len, int ret, int errno);
|
||||
probe qemu_monitor_io_write(void *mon, const char *buf, unsigned int len, int ret, int errno);
|
||||
probe qemu_monitor_io_send_fd(void *mon, int fd, int ret, int errno);
|
||||
};
|
||||
|
|
|
@ -183,6 +183,8 @@ static void qemuMonitorFree(qemuMonitorPtr mon)
|
|||
int qemuMonitorRef(qemuMonitorPtr mon)
|
||||
{
|
||||
mon->refs++;
|
||||
PROBE(QEMU_MONITOR_REF,
|
||||
"mon=%p refs=%d", mon, mon->refs);
|
||||
return mon->refs;
|
||||
}
|
||||
|
||||
|
@ -190,6 +192,8 @@ int qemuMonitorUnref(qemuMonitorPtr mon)
|
|||
{
|
||||
mon->refs--;
|
||||
|
||||
PROBE(QEMU_MONITOR_UNREF,
|
||||
"mon=%p refs=%d", mon, mon->refs);
|
||||
if (mon->refs == 0) {
|
||||
qemuMonitorUnlock(mon);
|
||||
qemuMonitorFree(mon);
|
||||
|
@ -305,6 +309,9 @@ qemuMonitorIOProcess(qemuMonitorPtr mon)
|
|||
# endif
|
||||
#endif
|
||||
|
||||
PROBE(QEMU_MONITOR_IO_PROCESS,
|
||||
"mon=%p buf=%s len=%zu", mon, mon->buffer, mon->bufferOffset);
|
||||
|
||||
if (mon->json)
|
||||
len = qemuMonitorJSONIOProcess(mon,
|
||||
mon->buffer, mon->bufferOffset,
|
||||
|
@ -403,6 +410,18 @@ qemuMonitorIOWrite(qemuMonitorPtr mon)
|
|||
mon->msg->txLength - mon->msg->txOffset,
|
||||
mon->msg->txFD);
|
||||
|
||||
PROBE(QEMU_MONITOR_IO_WRITE,
|
||||
"mon=%p buf=%s len=%d ret=%d errno=%d",
|
||||
mon,
|
||||
mon->msg->txBuffer + mon->msg->txOffset,
|
||||
mon->msg->txLength - mon->msg->txOffset,
|
||||
done, errno);
|
||||
|
||||
if (mon->msg->txFD != -1)
|
||||
PROBE(QEMU_MONITOR_IO_SEND_FD,
|
||||
"mon=%p fd=%d ret=%d errno=%d",
|
||||
mon, mon->msg->txFD, done, errno);
|
||||
|
||||
if (done < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
@ -696,7 +715,9 @@ qemuMonitorOpen(virDomainObjPtr vm,
|
|||
}
|
||||
qemuMonitorRef(mon);
|
||||
|
||||
VIR_DEBUG("New mon %p fd =%d watch=%d", mon, mon->fd, mon->watch);
|
||||
PROBE(QEMU_MONITOR_NEW,
|
||||
"mon=%p refs=%d fd=%d",
|
||||
mon, mon->refs, mon->fd);
|
||||
qemuMonitorUnlock(mon);
|
||||
|
||||
return mon;
|
||||
|
@ -719,9 +740,9 @@ void qemuMonitorClose(qemuMonitorPtr mon)
|
|||
if (!mon)
|
||||
return;
|
||||
|
||||
VIR_DEBUG("mon=%p", mon);
|
||||
|
||||
qemuMonitorLock(mon);
|
||||
PROBE(QEMU_MONITOR_CLOSE,
|
||||
"mon=%p refs=%d", mon, mon->refs);
|
||||
|
||||
if (mon->fd >= 0) {
|
||||
if (mon->watch)
|
||||
|
@ -762,6 +783,10 @@ int qemuMonitorSend(qemuMonitorPtr mon,
|
|||
mon->msg = msg;
|
||||
qemuMonitorUpdateWatch(mon);
|
||||
|
||||
PROBE(QEMU_MONITOR_SEND_MSG,
|
||||
"mon=%p msg=%s fd=%d",
|
||||
mon, mon->msg->txBuffer, mon->msg->txFD);
|
||||
|
||||
while (!mon->msg->finished) {
|
||||
if (virCondWait(&mon->notify, &mon->lock) < 0) {
|
||||
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
|
|
|
@ -126,9 +126,13 @@ qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon,
|
|||
if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
|
||||
ret = 0;
|
||||
} else if (virJSONValueObjectHasKey(obj, "event") == 1) {
|
||||
PROBE(QEMU_MONITOR_RECV_EVENT,
|
||||
"mon=%p event=%s", mon, line);
|
||||
ret = qemuMonitorJSONIOProcessEvent(mon, obj);
|
||||
} else if (virJSONValueObjectHasKey(obj, "error") == 1 ||
|
||||
virJSONValueObjectHasKey(obj, "return") == 1) {
|
||||
PROBE(QEMU_MONITOR_RECV_REPLY,
|
||||
"mon=%p reply=%s", mon, line);
|
||||
if (msg) {
|
||||
msg->rxObject = obj;
|
||||
msg->finished = 1;
|
||||
|
|
|
@ -198,6 +198,9 @@ int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|||
VIR_DEBUG("Finished 0 byte reply");
|
||||
#endif
|
||||
}
|
||||
PROBE(QEMU_MONITOR_RECV_REPLY,
|
||||
"mon=%p reply=%s",
|
||||
mon, msg->rxBuffer);
|
||||
msg->finished = 1;
|
||||
used += end - (data + used);
|
||||
used += strlen(BASIC_PROMPT);
|
||||
|
|
Loading…
Reference in New Issue