From 9b76b08ae41e725d916dccbb6f300d014ff421f2 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 24 Oct 2011 15:31:37 +0100 Subject: [PATCH] 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 --- examples/systemtap/qemu-monitor.stp | 80 +++++++++++++++++++++++++++++ src/Makefile.am | 2 +- src/dtrace2systemtap.pl | 29 ++++++++--- src/probes.d | 20 ++++++++ src/qemu/qemu_monitor.c | 31 +++++++++-- src/qemu/qemu_monitor_json.c | 4 ++ src/qemu/qemu_monitor_text.c | 3 ++ 7 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 examples/systemtap/qemu-monitor.stp diff --git a/examples/systemtap/qemu-monitor.stp b/examples/systemtap/qemu-monitor.stp new file mode 100644 index 0000000000..b9d9b253d1 --- /dev/null +++ b/examples/systemtap/qemu-monitor.stp @@ -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 +# +# 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)); +} diff --git a/src/Makefile.am b/src/Makefile.am index 2555f81d01..81ec7300d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/dtrace2systemtap.pl b/src/dtrace2systemtap.pl index fab066bfb0..4c6c249b18 100755 --- a/src/dtrace2systemtap.pl +++ b/src/dtrace2systemtap.pl @@ -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++) { diff --git a/src/probes.d b/src/probes.d index 6d95c846be..7f66ac0594 100644 --- a/src/probes.d +++ b/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); }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 182e63dc51..2b004b7ccb 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -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", diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index cd8f1e599c..2c4aee5e93 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -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; diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 4774df9986..c652321f76 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -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);