aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xenmon
diff options
context:
space:
mode:
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>2005-11-15 15:09:58 +0100
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>2005-11-15 15:09:58 +0100
commit0d5ddb880686349ab6c35a283033f2ecc987dd0f (patch)
treee2cd550b6670d35f247d2cf14a4b1019a6dbafe7 /tools/xenmon
parent0e3a022f8cfe382c960da254f632eaec4ec6dfcf (diff)
downloadxen-0d5ddb880686349ab6c35a283033f2ecc987dd0f.tar.gz
xen-0d5ddb880686349ab6c35a283033f2ecc987dd0f.tar.bz2
xen-0d5ddb880686349ab6c35a283033f2ecc987dd0f.zip
The new userland monitoring tool, XenMon.
Signed-off-by: Rob Gardner <rob.gardner@hp.com>
Diffstat (limited to 'tools/xenmon')
-rw-r--r--tools/xenmon/COPYING340
-rw-r--r--tools/xenmon/Makefile51
-rw-r--r--tools/xenmon/README104
-rw-r--r--tools/xenmon/setmask.c90
-rw-r--r--tools/xenmon/xenbaked.c1029
-rw-r--r--tools/xenmon/xenbaked.h101
-rw-r--r--tools/xenmon/xenmon.py578
7 files changed, 2293 insertions, 0 deletions
diff --git a/tools/xenmon/COPYING b/tools/xenmon/COPYING
new file mode 100644
index 0000000000..5b6e7c66c2
--- /dev/null
+++ b/tools/xenmon/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/tools/xenmon/Makefile b/tools/xenmon/Makefile
new file mode 100644
index 0000000000..7fdf786445
--- /dev/null
+++ b/tools/xenmon/Makefile
@@ -0,0 +1,51 @@
+# Copyright (C) HP Labs, Palo Alto and Fort Collins, 2005
+# Author: Diwaker Gupta <diwaker.gupta@hp.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; under version 2 of the License.
+#
+# This program 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 General Public License for more details.
+
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+INSTALL_DATA = $(INSTALL) -m064
+
+prefix=/usr/local
+mandir=$(prefix)/share/man
+man1dir=$(mandir)/man1
+sbindir=$(prefix)/sbin
+
+XEN_ROOT=../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+CFLAGS += -Wall -Werror -g
+CFLAGS += -I $(XEN_XC)
+CFLAGS += -I $(XEN_LIBXC)
+LDFLAGS += -L $(XEN_LIBXC)
+
+BIN = setmask xenbaked
+SCRIPTS = xenmon.py
+
+all: build
+
+build: $(BIN)
+
+install: xenbaked setmask
+ [ -d $(DESTDIR)$(sbindir) ] || $(INSTALL_DIR) $(DESTDIR)$(sbindir)
+ $(INSTALL_PROG) xenbaked $(DESTDIR)$(sbindir)/xenbaked
+ $(INSTALL_PROG) setmask $(DESTDIR)$(sbindir)/setmask
+ $(INSTALL_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon.py
+
+clean:
+ rm -f $(BIN)
+
+
+%: %.c Makefile
+ $(CC) $(CFLAGS) $(LDFLAGS) -lxenctrl -o $@ $<
+
+
diff --git a/tools/xenmon/README b/tools/xenmon/README
new file mode 100644
index 0000000000..c717daef88
--- /dev/null
+++ b/tools/xenmon/README
@@ -0,0 +1,104 @@
+Xen Performance Monitor
+-----------------------
+
+The xenmon tools make use of the existing xen tracing feature to provide fine
+grained reporting of various domain related metrics. It should be stressed that
+the xenmon.py script included here is just an example of the data that may be
+displayed. The xenbake demon keeps a large amount of history in a shared memory
+area that may be accessed by tools such as xenmon.
+
+For each domain, xenmon reports various metrics. One part of the display is a
+group of metrics that have been accumulated over the last second, while another
+part of the display shows data measured over 10 seconds. Other measurement
+intervals are possible, but we have just chosen 1s and 10s as an example.
+
+
+Execution Count
+---------------
+ o The number of times that a domain was scheduled to run (ie, dispatched) over
+ the measurement interval
+
+
+CPU usage
+---------
+ o Total time used over the measurement interval
+ o Usage expressed as a percentage of the measurement interval
+ o Average cpu time used during each execution of the domain
+
+
+Waiting time
+------------
+This is how much time the domain spent waiting to run, or put another way, the
+amount of time the domain spent in the "runnable" state (or on the run queue)
+but not actually running. Xenmon displays:
+
+ o Total time waiting over the measurement interval
+ o Wait time expressed as a percentage of the measurement interval
+ o Average waiting time for each execution of the domain
+
+Blocked time
+------------
+This is how much time the domain spent blocked (or sleeping); Put another way,
+the amount of time the domain spent not needing/wanting the cpu because it was
+waiting for some event (ie, I/O). Xenmon reports:
+
+ o Total time blocked over the measurement interval
+ o Blocked time expressed as a percentage of the measurement interval
+ o Blocked time per I/O (see I/O count below)
+
+Allocation time
+---------------
+This is how much cpu time was allocated to the domain by the scheduler; This is
+distinct from cpu usage since the "time slice" given to a domain is frequently
+cut short for one reason or another, ie, the domain requests I/O and blocks.
+Xenmon reports:
+
+ o Average allocation time per execution (ie, time slice)
+ o Min and Max allocation times
+
+I/O Count
+---------
+This is a rough measure of I/O requested by the domain. The number of page
+exchanges (or page "flips") between the domain and dom0 are counted. The
+number of pages exchanged may not accurately reflect the number of bytes
+transferred to/from a domain due to partial pages being used by the network
+protocols, etc. But it does give a good sense of the magnitude of I/O being
+requested by a domain. Xenmon reports:
+
+ o Total number of page exchanges during the measurement interval
+ o Average number of page exchanges per execution of the domain
+
+
+Usage Notes and issues
+----------------------
+ - Start xenmon by simply running xenmon.py; The xenbake demon is started and
+ stopped automatically by xenmon.
+ - To see the various options for xenmon, run xenmon -h. Ditto for xenbaked.
+ - xenmon also has an option (-n) to output log data to a file instead of the
+ curses interface.
+ - NDOMAINS is defined to be 32, but can be changed by recompiling xenbaked
+ - Xenmon.py appears to create 1-2% cpu overhead; Part of this is just the
+ overhead of the python interpreter. Part of it may be the number of trace
+ records being generated. The number of trace records generated can be
+ limited by setting the trace mask (with a dom0 Op), which controls which
+ events cause a trace record to be emitted.
+ - To exit xenmon, type 'q'
+ - To cycle the display to other physical cpu's, type 'c'
+
+Future Work
+-----------
+o RPC interface to allow external entities to programmatically access processed data
+o I/O Count batching to reduce number of trace records generated
+
+Case Study
+----------
+We have written a case study which demonstrates some of the usefulness of
+this tool and the metrics reported. It is available at:
+http://www.hpl.hp.com/techreports/2005/HPL-2005-187.html
+
+Authors
+-------
+Diwaker Gupta <diwaker.gupta@hp.com>
+Rob Gardner <rob.gardner@hp.com>
+Lucy Cherkasova <lucy.cherkasova.hp.com>
+
diff --git a/tools/xenmon/setmask.c b/tools/xenmon/setmask.c
new file mode 100644
index 0000000000..333280d359
--- /dev/null
+++ b/tools/xenmon/setmask.c
@@ -0,0 +1,90 @@
+/******************************************************************************
+ * tools/xenmon/setmask.c
+ *
+ * Simple utility for getting/setting the event mask
+ *
+ * Copyright (C) 2005 by Hewlett-Packard, Palo Alto and Fort Collins
+ *
+ * Authors: Lucy Cherkasova, lucy.cherkasova.hp.com
+ * Rob Gardner, rob.gardner@hp.com
+ * Diwaker Gupta, diwaker.gupta@hp.com
+ * Date: August, 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <xenctrl.h>
+#include <xen/xen.h>
+typedef struct { int counter; } atomic_t;
+#include <xen/trace.h>
+
+#define XENMON (TRC_SCHED_DOM_ADD | TRC_SCHED_DOM_REM | TRC_SCHED_SWITCH_INFPREV | TRC_SCHED_SWITCH_INFNEXT | TRC_SCHED_BLOCK | TRC_SCHED_SLEEP | TRC_SCHED_WAKE | TRC_MEM_PAGE_GRANT_TRANSFER)
+
+int main(int argc, char * argv[])
+{
+
+ dom0_op_t op;
+ int ret;
+
+ int xc_handle = xc_interface_open();
+ op.cmd = DOM0_TBUFCONTROL;
+ op.interface_version = DOM0_INTERFACE_VERSION;
+ op.u.tbufcontrol.op = DOM0_TBUF_GET_INFO;
+ ret = xc_dom0_op(xc_handle, &op);
+ if ( ret != 0 )
+ {
+ perror("Failure to get event mask from Xen");
+ exit(1);
+ }
+ else
+ {
+ printf("Current event mask: 0x%.8x\n", op.u.tbufcontrol.evt_mask);
+ }
+
+ op.cmd = DOM0_TBUFCONTROL;
+ op.interface_version = DOM0_INTERFACE_VERSION;
+ op.u.tbufcontrol.op = DOM0_TBUF_SET_EVT_MASK;
+ op.u.tbufcontrol.evt_mask = XENMON;
+
+ ret = xc_dom0_op(xc_handle, &op);
+ printf("Setting mask to 0x%.8x\n", op.u.tbufcontrol.evt_mask);
+ if ( ret != 0 )
+ {
+ perror("Failure to get scheduler ID from Xen");
+ exit(1);
+ }
+
+ op.cmd = DOM0_TBUFCONTROL;
+ op.interface_version = DOM0_INTERFACE_VERSION;
+ op.u.tbufcontrol.op = DOM0_TBUF_GET_INFO;
+ ret = xc_dom0_op(xc_handle, &op);
+ if ( ret != 0 )
+ {
+ perror("Failure to get event mask from Xen");
+ exit(1);
+ }
+ else
+ {
+ printf("Current event mask: 0x%.8x\n", op.u.tbufcontrol.evt_mask);
+ }
+ xc_interface_close(xc_handle);
+ return 0;
+}
diff --git a/tools/xenmon/xenbaked.c b/tools/xenmon/xenbaked.c
new file mode 100644
index 0000000000..41b50a5daa
--- /dev/null
+++ b/tools/xenmon/xenbaked.c
@@ -0,0 +1,1029 @@
+/******************************************************************************
+ * tools/xenbaked.c
+ *
+ * Tool for collecting raw trace buffer data from Xen and
+ * performing some accumulation operations and other processing
+ * on it.
+ *
+ * Copyright (C) 2004 by Intel Research Cambridge
+ * Copyright (C) 2005 by Hewlett Packard, Palo Alto and Fort Collins
+ *
+ * Authors: Diwaker Gupta, diwaker.gupta@hp.com
+ * Rob Gardner, rob.gardner@hp.com
+ * Lucy Cherkasova, lucy.cherkasova.hp.com
+ * Much code based on xentrace, authored by Mark Williamson, mark.a.williamson@intel.com
+ * Date: November, 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <argp.h>
+#include <signal.h>
+#include <xenctrl.h>
+#include <xen/xen.h>
+#include <string.h>
+
+#include "xc_private.h"
+typedef struct { int counter; } atomic_t;
+#define _atomic_read(v) ((v).counter)
+
+#include <xen/trace.h>
+#include "xenbaked.h"
+
+extern FILE *stderr;
+
+/***** Compile time configuration of defaults ********************************/
+
+/* when we've got more records than this waiting, we log it to the output */
+#define NEW_DATA_THRESH 1
+
+/* sleep for this long (milliseconds) between checking the trace buffers */
+#define POLL_SLEEP_MILLIS 100
+
+/* Size of time period represented by each sample */
+#define MS_PER_SAMPLE 100
+
+/* CPU Frequency */
+#define MHZ
+#define CPU_FREQ 2660 MHZ
+
+/***** The code **************************************************************/
+
+typedef struct settings_st {
+ char *outfile;
+ struct timespec poll_sleep;
+ unsigned long new_data_thresh;
+ unsigned long ms_per_sample;
+ double cpu_freq;
+} settings_t;
+
+settings_t opts;
+
+int interrupted = 0; /* gets set if we get a SIGHUP */
+int rec_count = 0;
+time_t start_time;
+int dom0_flips = 0;
+
+_new_qos_data *new_qos;
+_new_qos_data **cpu_qos_data;
+
+
+#define ID(X) ((X>NDOMAINS-1)?(NDOMAINS-1):X)
+
+// array of currently running domains, indexed by cpu
+int *running = NULL;
+
+// number of cpu's on this platform
+int NCPU = 0;
+
+
+void init_current(int ncpu)
+{
+ running = calloc(ncpu, sizeof(int));
+ NCPU = ncpu;
+ printf("Initialized with %d %s\n", ncpu, (ncpu == 1) ? "cpu" : "cpu's");
+}
+
+int is_current(int domain, int cpu)
+{
+ // int i;
+
+ // for (i=0; i<NCPU; i++)
+ if (running[cpu] == domain)
+ return 1;
+ return 0;
+}
+
+
+// return the domain that's currently running on the given cpu
+int current(int cpu)
+{
+ return running[cpu];
+}
+
+void set_current(int cpu, int domain)
+{
+ running[cpu] = domain;
+}
+
+
+
+void close_handler(int signal)
+{
+ interrupted = 1;
+}
+
+#if 0
+void dump_record(int cpu, struct t_rec *x)
+{
+ printf("record: cpu=%x, tsc=%lx, event=%x, d1=%lx\n",
+ cpu, x->cycles, x->event, x->data[0]);
+}
+#endif
+
+/**
+ * millis_to_timespec - convert a time in milliseconds to a struct timespec
+ * @millis: time interval in milliseconds
+ */
+struct timespec millis_to_timespec(unsigned long millis)
+{
+ struct timespec spec;
+
+ spec.tv_sec = millis / 1000;
+ spec.tv_nsec = (millis % 1000) * 1000;
+
+ return spec;
+}
+
+
+typedef struct
+{
+ int event_count;
+ int event_id;
+ char *text;
+} stat_map_t;
+
+stat_map_t stat_map[] = {
+ { 0, 0, "Other" },
+ { 0, TRC_SCHED_DOM_ADD, "Add Domain" },
+ { 0, TRC_SCHED_DOM_REM, "Remove Domain" },
+ { 0, TRC_SCHED_SLEEP, "Sleep" },
+ { 0, TRC_SCHED_WAKE, "Wake" },
+ { 0, TRC_SCHED_BLOCK, "Block" },
+ { 0, TRC_SCHED_SWITCH, "Switch" },
+ { 0, TRC_SCHED_S_TIMER_FN, "Timer Func"},
+ { 0, TRC_SCHED_SWITCH_INFPREV, "Switch Prev" },
+ { 0, TRC_SCHED_SWITCH_INFNEXT, "Switch Next" },
+ { 0, TRC_MEM_PAGE_GRANT_MAP, "Page Map" },
+ { 0, TRC_MEM_PAGE_GRANT_UNMAP, "Page Unmap" },
+ { 0, TRC_MEM_PAGE_GRANT_TRANSFER, "Page Transfer" },
+ { 0, 0, 0 }
+};
+
+
+void check_gotten_sum(void)
+{
+#if 0
+ uint64_t sum, ns;
+ extern uint64_t total_ns_gotten(uint64_t*);
+ double percent;
+ int i;
+
+ for (i=0; i<NCPU; i++) {
+ new_qos = cpu_qos_data[i];
+ ns = billion;
+ sum = total_ns_gotten(&ns);
+
+ printf("[cpu%d] ns_gotten over all domains = %lldns, over %lldns\n",
+ i, sum, ns);
+ percent = (double) sum;
+ percent = (100.0*percent) / (double)ns;
+ printf(" ==> ns_gotten = %7.3f%%\n", percent);
+ }
+#endif
+}
+
+
+
+void dump_stats(void)
+{
+ stat_map_t *smt = stat_map;
+ time_t end_time, run_time;
+
+ time(&end_time);
+
+ run_time = end_time - start_time;
+
+ printf("Event counts:\n");
+ while (smt->text != NULL) {
+ printf("%08d\t%s\n", smt->event_count, smt->text);
+ smt++;
+ }
+
+ printf("processed %d total records in %d seconds (%ld per second)\n",
+ rec_count, (int)run_time, rec_count/run_time);
+
+ check_gotten_sum();
+}
+
+void log_event(int event_id)
+{
+ stat_map_t *smt = stat_map;
+
+ // printf("event_id = 0x%x\n", event_id);
+
+ while (smt->text != NULL) {
+ if (smt->event_id == event_id) {
+ smt->event_count++;
+ return;
+ }
+ smt++;
+ }
+ if (smt->text == NULL)
+ stat_map[0].event_count++; // other
+}
+
+
+
+/**
+ * get_tbufs - get pointer to and size of the trace buffers
+ * @mfn: location to store mfn of the trace buffers to
+ * @size: location to store the size of a trace buffer to
+ *
+ * Gets the machine address of the trace pointer area and the size of the
+ * per CPU buffers.
+ */
+void get_tbufs(unsigned long *mfn, unsigned long *size)
+{
+ int ret;
+ dom0_op_t op; /* dom0 op we'll build */
+ int xc_handle = xc_interface_open(); /* for accessing control interface */
+
+ op.cmd = DOM0_TBUFCONTROL;
+ op.interface_version = DOM0_INTERFACE_VERSION;
+ op.u.tbufcontrol.op = DOM0_TBUF_GET_INFO;
+
+ ret = do_dom0_op(xc_handle, &op);
+
+ xc_interface_close(xc_handle);
+
+ if ( ret != 0 )
+ {
+ PERROR("Failure to get trace buffer pointer from Xen");
+ exit(EXIT_FAILURE);
+ }
+
+ *mfn = op.u.tbufcontrol.buffer_mfn;
+ *size = op.u.tbufcontrol.size;
+}
+
+/**
+ * map_tbufs - memory map Xen trace buffers into user space
+ * @tbufs_mfn: mfn of the trace buffers
+ * @num: number of trace buffers to map
+ * @size: size of each trace buffer
+ *
+ * Maps the Xen trace buffers them into process address space.
+ */
+struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
+ unsigned long size)
+{
+ int xc_handle; /* file descriptor for /proc/xen/privcmd */
+ struct t_buf *tbufs_mapped;
+
+ xc_handle = xc_interface_open();
+
+ if ( xc_handle < 0 )
+ {
+ PERROR("Open /proc/xen/privcmd when mapping trace buffers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tbufs_mapped = xc_map_foreign_range(xc_handle, 0 /* Dom 0 ID */,
+ size * num, PROT_READ | PROT_WRITE,
+ tbufs_mfn);
+
+ xc_interface_close(xc_handle);
+
+ if ( tbufs_mapped == 0 )
+ {
+ PERROR("Failed to mmap trace buffers");
+ exit(EXIT_FAILURE);
+ }
+
+ return tbufs_mapped;
+}
+
+/**
+ * init_bufs_ptrs - initialises an array of pointers to the trace buffers
+ * @bufs_mapped: the userspace address where the trace buffers are mapped
+ * @num: number of trace buffers
+ * @size: trace buffer size
+ *
+ * Initialises an array of pointers to individual trace buffers within the
+ * mapped region containing all trace buffers.
+ */
+struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
+ unsigned long size)
+{
+ int i;
+ struct t_buf **user_ptrs;
+
+ user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
+ if ( user_ptrs == NULL )
+ {
+ PERROR( "Failed to allocate memory for buffer pointers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* initialise pointers to the trace buffers - given the size of a trace
+ * buffer and the value of bufs_maped, we can easily calculate these */
+ for ( i = 0; i<num; i++ )
+ user_ptrs[i] = (struct t_buf *)((unsigned long)bufs_mapped + size * i);
+
+ return user_ptrs;
+}
+
+
+/**
+ * init_rec_ptrs - initialises data area pointers to locations in user space
+ * @tbufs_mfn: base mfn of the trace buffer area
+ * @tbufs_mapped: user virtual address of base of trace buffer area
+ * @meta: array of user-space pointers to struct t_buf's of metadata
+ * @num: number of trace buffers
+ *
+ * Initialises data area pointers to the locations that data areas have been
+ * mapped in user space. Note that the trace buffer metadata contains machine
+ * pointers - the array returned allows more convenient access to them.
+ */
+struct t_rec **init_rec_ptrs(struct t_buf **meta, unsigned int num)
+{
+ int i;
+ struct t_rec **data;
+
+ data = calloc(num, sizeof(struct t_rec *));
+ if ( data == NULL )
+ {
+ PERROR("Failed to allocate memory for data pointers\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for ( i = 0; i < num; i++ )
+ data[i] = (struct t_rec *)(meta[i] + 1);
+
+ return data;
+}
+
+
+
+/**
+ * get_num_cpus - get the number of logical CPUs
+ */
+unsigned int get_num_cpus()
+{
+ dom0_op_t op;
+ int xc_handle = xc_interface_open();
+ int ret;
+
+ op.cmd = DOM0_PHYSINFO;
+ op.interface_version = DOM0_INTERFACE_VERSION;
+
+ ret = xc_dom0_op(xc_handle, &op);
+
+ if ( ret != 0 )
+ {
+ PERROR("Failure to get logical CPU count from Xen");
+ exit(EXIT_FAILURE);
+ }
+
+ xc_interface_close(xc_handle);
+ opts.cpu_freq = (double)op.u.physinfo.cpu_khz/1000.0;
+
+ return (op.u.physinfo.threads_per_core *
+ op.u.physinfo.cores_per_socket *
+ op.u.physinfo.sockets_per_node *
+ op.u.physinfo.nr_nodes);
+}
+
+
+/**
+ * monitor_tbufs - monitor the contents of tbufs
+ */
+int monitor_tbufs()
+{
+ int i;
+ extern void process_record(int, struct t_rec *);
+ extern void alloc_qos_data(int ncpu);
+
+ void *tbufs_mapped; /* pointer to where the tbufs are mapped */
+ struct t_buf **meta; /* pointers to the trace buffer metadata */
+ struct t_rec **data; /* pointers to the trace buffer data areas
+ * where they are mapped into user space. */
+ unsigned long tbufs_mfn; /* mfn of the tbufs */
+ unsigned int num; /* number of trace buffers / logical CPUS */
+ unsigned long size; /* size of a single trace buffer */
+
+ int size_in_recs;
+
+ /* get number of logical CPUs (and therefore number of trace buffers) */
+ num = get_num_cpus();
+
+ init_current(num);
+ alloc_qos_data(num);
+
+ printf("CPU Frequency = %7.2f\n", opts.cpu_freq);
+
+ /* setup access to trace buffers */
+ get_tbufs(&tbufs_mfn, &size);
+
+ // printf("from dom0op: %ld, t_buf: %d, t_rec: %d\n",
+ // size, sizeof(struct t_buf), sizeof(struct t_rec));
+
+ tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
+
+ size_in_recs = (size - sizeof(struct t_buf)) / sizeof(struct t_rec);
+ // fprintf(stderr, "size_in_recs = %d\n", size_in_recs);
+
+ /* build arrays of convenience ptrs */
+ meta = init_bufs_ptrs (tbufs_mapped, num, size);
+ data = init_rec_ptrs(meta, num);
+
+ /* now, scan buffers for events */
+ while ( !interrupted )
+ {
+ for ( i = 0; ( i < num ) && !interrupted; i++ )
+ while ( meta[i]->cons != meta[i]->prod )
+ {
+ rmb(); /* read prod, then read item. */
+ process_record(i, data[i] + meta[i]->cons % size_in_recs);
+ mb(); /* read item, then update cons. */
+ meta[i]->cons++;
+ }
+
+ nanosleep(&opts.poll_sleep, NULL);
+ }
+
+ /* cleanup */
+ free(meta);
+ free(data);
+ /* don't need to munmap - cleanup is automatic */
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * Various declarations / definitions GNU argp needs to do its work
+ *****************************************************************************/
+
+
+/* command parser for GNU argp - see GNU docs for more info */
+error_t cmd_parser(int key, char *arg, struct argp_state *state)
+{
+ settings_t *setup = (settings_t *)state->input;
+
+ switch ( key )
+ {
+ case 't': /* set new records threshold for logging */
+ {
+ char *inval;
+ setup->new_data_thresh = strtol(arg, &inval, 0);
+ if ( inval == arg )
+ argp_usage(state);
+ }
+ break;
+
+ case 's': /* set sleep time (given in milliseconds) */
+ {
+ char *inval;
+ setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
+ if ( inval == arg )
+ argp_usage(state);
+ }
+ break;
+
+ case 'm': /* set ms_per_sample */
+ {
+ char *inval;
+ setup->ms_per_sample = strtol(arg, &inval, 0);
+ if ( inval == arg )
+ argp_usage(state);
+ }
+ break;
+
+ case ARGP_KEY_ARG:
+ {
+ if ( state->arg_num == 0 )
+ setup->outfile = arg;
+ else
+ argp_usage(state);
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+#define SHARED_MEM_FILE "/tmp/xenq-shm"
+void alloc_qos_data(int ncpu)
+{
+ int i, n, pgsize, off=0;
+ char *dummy;
+ int qos_fd;
+ void advance_next_datapoint(uint64_t);
+
+ cpu_qos_data = (_new_qos_data **) calloc(ncpu, sizeof(_new_qos_data *));
+
+
+ qos_fd = open(SHARED_MEM_FILE, O_RDWR|O_CREAT|O_TRUNC, 0777);
+ if (qos_fd < 0) {
+ PERROR(SHARED_MEM_FILE);
+ exit(2);
+ }
+ pgsize = getpagesize();
+ dummy = malloc(pgsize);
+
+ for (n=0; n<ncpu; n++) {
+
+ for (i=0; i<sizeof(_new_qos_data); i=i+pgsize)
+ write(qos_fd, dummy, pgsize);
+
+ new_qos = (_new_qos_data *) mmap(0, sizeof(_new_qos_data), PROT_READ|PROT_WRITE,
+ MAP_SHARED, qos_fd, off);
+ off += i;
+ if (new_qos == NULL) {
+ PERROR("mmap");
+ exit(3);
+ }
+ // printf("new_qos = %p\n", new_qos);
+ memset(new_qos, 0, sizeof(_new_qos_data));
+ new_qos->next_datapoint = 0;
+ advance_next_datapoint(0);
+ new_qos->structlen = i;
+ new_qos->ncpu = ncpu;
+ // printf("structlen = 0x%x\n", i);
+ cpu_qos_data[n] = new_qos;
+ }
+ free(dummy);
+ new_qos = NULL;
+}
+
+
+#define xstr(x) str(x)
+#define str(x) #x
+
+const struct argp_option cmd_opts[] =
+{
+ { .name = "log-thresh", .key='t', .arg="l",
+ .doc =
+ "Set number, l, of new records required to trigger a write to output "
+ "(default " xstr(NEW_DATA_THRESH) ")." },
+
+ { .name = "poll-sleep", .key='s', .arg="p",
+ .doc =
+ "Set sleep time, p, in milliseconds between polling the trace buffer "
+ "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
+
+ { .name = "ms_per_sample", .key='m', .arg="MS",
+ .doc =
+ "Specify the number of milliseconds per sample "
+ " (default " xstr(MS_PER_SAMPLE) ")." },
+
+ {0}
+};
+
+const struct argp parser_def =
+{
+ .options = cmd_opts,
+ .parser = cmd_parser,
+ // .args_doc = "[output file]",
+ .doc =
+ "Tool to capture and partially process Xen trace buffer data"
+ "\v"
+ "This tool is used to capture trace buffer data from Xen. The data is "
+ "saved in a shared memory structure to be further processed by xenmon."
+};
+
+
+const char *argp_program_version = "xenbaked v1.3";
+const char *argp_program_bug_address = "<rob.gardner@hp.com>";
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+ struct sigaction act;
+
+ time(&start_time);
+ opts.outfile = 0;
+ opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
+ opts.new_data_thresh = NEW_DATA_THRESH;
+ opts.ms_per_sample = MS_PER_SAMPLE;
+ opts.cpu_freq = CPU_FREQ;
+
+ argp_parse(&parser_def, argc, argv, 0, 0, &opts);
+ fprintf(stderr, "ms_per_sample = %ld\n", opts.ms_per_sample);
+
+
+ /* ensure that if we get a signal, we'll do cleanup, then exit */
+ act.sa_handler = close_handler;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ ret = monitor_tbufs();
+
+ dump_stats();
+ msync(new_qos, sizeof(_new_qos_data), MS_SYNC);
+
+ return ret;
+}
+
+int domain_runnable(int domid)
+{
+ return new_qos->domain_info[ID(domid)].runnable;
+}
+
+
+void update_blocked_time(int domid, uint64_t now)
+{
+ uint64_t t_blocked;
+ int id = ID(domid);
+
+ if (new_qos->domain_info[id].blocked_start_time != 0) {
+ if (now >= new_qos->domain_info[id].blocked_start_time)
+ t_blocked = now - new_qos->domain_info[id].blocked_start_time;
+ else
+ t_blocked = now + (~0ULL - new_qos->domain_info[id].blocked_start_time);
+ new_qos->qdata[new_qos->next_datapoint].ns_blocked[id] += t_blocked;
+ }
+
+ if (domain_runnable(id))
+ new_qos->domain_info[id].blocked_start_time = 0;
+ else
+ new_qos->domain_info[id].blocked_start_time = now;
+}
+
+
+// advance to next datapoint for all domains
+void advance_next_datapoint(uint64_t now)
+{
+ int new, old, didx;
+
+ old = new_qos->next_datapoint;
+ new = QOS_INCR(old);
+ new_qos->next_datapoint = new;
+ // memset(&new_qos->qdata[new], 0, sizeof(uint64_t)*(2+5*NDOMAINS));
+ for (didx = 0; didx < NDOMAINS; didx++) {
+ new_qos->qdata[new].ns_gotten[didx] = 0;
+ new_qos->qdata[new].ns_allocated[didx] = 0;
+ new_qos->qdata[new].ns_waiting[didx] = 0;
+ new_qos->qdata[new].ns_blocked[didx] = 0;
+ new_qos->qdata[new].switchin_count[didx] = 0;
+ new_qos->qdata[new].io_count[didx] = 0;
+ }
+ new_qos->qdata[new].ns_passed = 0;
+ new_qos->qdata[new].lost_records = 0;
+ new_qos->qdata[new].flip_free_periods = 0;
+
+ new_qos->qdata[new].timestamp = now;
+}
+
+
+
+void qos_update_thread(int cpu, int domid, uint64_t now)
+{
+ int n, id;
+ uint64_t last_update_time, start;
+ int64_t time_since_update, run_time = 0;
+
+ id = ID(domid);
+
+ n = new_qos->next_datapoint;
+ last_update_time = new_qos->domain_info[id].last_update_time;
+
+ time_since_update = now - last_update_time;
+
+ if (time_since_update < 0) {
+ // what happened here? either a timestamp wraparound, or more likely,
+ // a slight inconsistency among timestamps from various cpu's
+ if (-time_since_update < billion) {
+ // fairly small difference, let's just adjust 'now' to be a little
+ // beyond last_update_time
+ time_since_update = -time_since_update;
+ }
+ else if ( ((~0ULL - last_update_time) < billion) && (now < billion) ) {
+ // difference is huge, must be a wraparound
+ // last_update time should be "near" ~0ULL,
+ // and now should be "near" 0
+ time_since_update = now + (~0ULL - last_update_time);
+ printf("time wraparound\n");
+ }
+ else {
+ // none of the above, may be an out of order record
+ // no good solution, just ignore and update again later
+ return;
+ }
+ }
+
+ new_qos->domain_info[id].last_update_time = now;
+
+ if (new_qos->domain_info[id].runnable_at_last_update && is_current(domid, cpu)) {
+ start = new_qos->domain_info[id].start_time;
+ if (start > now) { // wrapped around
+ run_time = now + (~0ULL - start);
+ printf("warning: start > now\n");
+ }
+ else
+ run_time = now - start;
+ // if (run_time < 0) // should not happen
+ // printf("warning: run_time < 0; start = %lld now= %lld\n", start, now);
+ new_qos->domain_info[id].ns_oncpu_since_boot += run_time;
+ new_qos->domain_info[id].start_time = now;
+ new_qos->domain_info[id].ns_since_boot += time_since_update;
+#if 1
+ new_qos->qdata[n].ns_gotten[id] += run_time;
+ if (domid == 0 && cpu == 1)
+ printf("adding run time for dom0 on cpu1\r\n");
+#endif
+ }
+
+ new_qos->domain_info[id].runnable_at_last_update = domain_runnable(domid);
+
+ update_blocked_time(domid, now);
+
+ // how much time passed since this datapoint was updated?
+ if (now >= new_qos->qdata[n].timestamp) {
+ // all is right with the world, time is increasing
+ new_qos->qdata[n].ns_passed += (now - new_qos->qdata[n].timestamp);
+ }
+ else {
+ // time wrapped around
+ //new_qos->qdata[n].ns_passed += (now + (~0LL - new_qos->qdata[n].timestamp));
+ // printf("why timewrap?\r\n");
+ }
+ new_qos->qdata[n].timestamp = now;
+}
+
+
+// called by dump routines to update all structures
+void qos_update_all(uint64_t now, int cpu)
+{
+ int i;
+
+ for (i=0; i<NDOMAINS; i++)
+ if (new_qos->domain_info[i].in_use)
+ qos_update_thread(cpu, i, now);
+}
+
+
+void qos_update_thread_stats(int cpu, int domid, uint64_t now)
+{
+ if (new_qos->qdata[new_qos->next_datapoint].ns_passed > (million*opts.ms_per_sample)) {
+ qos_update_all(now, cpu);
+ advance_next_datapoint(now);
+ return;
+ }
+ qos_update_thread(cpu, domid, now);
+}
+
+
+void qos_init_domain(int cpu, int domid, uint64_t now)
+{
+ int i, id;
+
+ id = ID(domid);
+
+ if (new_qos->domain_info[id].in_use)
+ return;
+
+
+ memset(&new_qos->domain_info[id], 0, sizeof(_domain_info));
+ new_qos->domain_info[id].last_update_time = now;
+ // runnable_start_time[id] = 0;
+ new_qos->domain_info[id].runnable_start_time = 0; // invalidate
+ new_qos->domain_info[id].in_use = 1;
+ new_qos->domain_info[id].blocked_start_time = 0;
+ new_qos->domain_info[id].id = id;
+ if (domid == IDLE_DOMAIN_ID)
+ sprintf(new_qos->domain_info[id].name, "Idle Task%d", cpu);
+ else
+ sprintf(new_qos->domain_info[id].name, "Domain#%d", domid);
+
+ for (i=0; i<NSAMPLES; i++) {
+ new_qos->qdata[i].ns_gotten[id] = 0;
+ new_qos->qdata[i].ns_allocated[id] = 0;
+ new_qos->qdata[i].ns_waiting[id] = 0;
+ new_qos->qdata[i].ns_blocked[id] = 0;
+ new_qos->qdata[i].switchin_count[id] = 0;
+ new_qos->qdata[i].io_count[id] = 0;
+ }
+}
+
+
+// called when a new thread gets the cpu
+void qos_switch_in(int cpu, int domid, uint64_t now, unsigned long ns_alloc, unsigned long ns_waited)
+{
+ int id = ID(domid);
+
+ new_qos->domain_info[id].runnable = 1;
+ update_blocked_time(domid, now);
+ new_qos->domain_info[id].blocked_start_time = 0; // invalidate
+ new_qos->domain_info[id].runnable_start_time = 0; // invalidate
+ //runnable_start_time[id] = 0;
+
+ new_qos->domain_info[id].start_time = now;
+ new_qos->qdata[new_qos->next_datapoint].switchin_count[id]++;
+ new_qos->qdata[new_qos->next_datapoint].ns_allocated[id] += ns_alloc;
+ new_qos->qdata[new_qos->next_datapoint].ns_waiting[id] += ns_waited;
+ qos_update_thread_stats(cpu, domid, now);
+ set_current(cpu, id);
+
+ // count up page flips for dom0 execution
+ if (id == 0)
+ dom0_flips = 0;
+}
+
+// called when the current thread is taken off the cpu
+void qos_switch_out(int cpu, int domid, uint64_t now, unsigned long gotten)
+{
+ int id = ID(domid);
+ int n;
+
+ if (!is_current(id, cpu)) {
+ // printf("switching out domain %d but it is not current. gotten=%ld\r\n", id, gotten);
+ }
+
+ if (gotten == 0) {
+ printf("gotten==0 in qos_switchout(domid=%d)\n", domid);
+ }
+
+ if (gotten < 100) {
+ printf("gotten<100ns in qos_switchout(domid=%d)\n", domid);
+ }
+
+
+ n = new_qos->next_datapoint;
+#if 0
+ new_qos->qdata[n].ns_gotten[id] += gotten;
+ if (gotten > new_qos->qdata[n].ns_passed)
+ printf("inconsistency #257, diff = %lld\n",
+ gotten - new_qos->qdata[n].ns_passed );
+#endif
+ new_qos->domain_info[id].ns_oncpu_since_boot += gotten;
+ new_qos->domain_info[id].runnable_start_time = now;
+ // runnable_start_time[id] = now;
+ qos_update_thread_stats(cpu, id, now);
+
+ // process dom0 page flips
+ if (id == 0)
+ if (dom0_flips == 0)
+ new_qos->qdata[n].flip_free_periods++;
+}
+
+// called when domain is put to sleep, may also be called
+// when thread is already asleep
+void qos_state_sleeping(int cpu, int domid, uint64_t now)
+{
+ int id = ID(domid);
+
+ if (!domain_runnable(id)) // double call?
+ return;
+
+ new_qos->domain_info[id].runnable = 0;
+ new_qos->domain_info[id].blocked_start_time = now;
+ new_qos->domain_info[id].runnable_start_time = 0; // invalidate
+ // runnable_start_time[id] = 0; // invalidate
+ qos_update_thread_stats(cpu, domid, now);
+}
+
+
+
+void qos_kill_thread(int domid)
+{
+ new_qos->domain_info[ID(domid)].in_use = 0;
+}
+
+
+// called when thread becomes runnable, may also be called
+// when thread is already runnable
+void qos_state_runnable(int cpu, int domid, uint64_t now)
+{
+ int id = ID(domid);
+
+ if (domain_runnable(id)) // double call?
+ return;
+ new_qos->domain_info[id].runnable = 1;
+ update_blocked_time(domid, now);
+
+ qos_update_thread_stats(cpu, domid, now);
+
+ new_qos->domain_info[id].blocked_start_time = 0; /* invalidate */
+ new_qos->domain_info[id].runnable_start_time = now;
+ // runnable_start_time[id] = now;
+}
+
+
+void qos_count_packets(domid_t domid, uint64_t now)
+{
+ int i, id = ID(domid);
+ _new_qos_data *cpu_data;
+
+ for (i=0; i<NCPU; i++) {
+ cpu_data = cpu_qos_data[i];
+ if (cpu_data->domain_info[id].in_use) {
+ cpu_data->qdata[cpu_data->next_datapoint].io_count[id]++;
+ }
+ }
+
+ new_qos->qdata[new_qos->next_datapoint].io_count[0]++;
+ dom0_flips++;
+}
+
+
+int domain_ok(int cpu, int domid, uint64_t now)
+{
+ if (domid == IDLE_DOMAIN_ID)
+ domid = NDOMAINS-1;
+ if (domid < 0 || domid >= NDOMAINS) {
+ printf("bad domain id: %d\n", domid);
+ return 0;
+ }
+ if (new_qos->domain_info[domid].in_use == 0)
+ qos_init_domain(cpu, domid, now);
+ return 1;
+}
+
+
+void process_record(int cpu, struct t_rec *r)
+{
+ uint64_t now;
+
+
+ new_qos = cpu_qos_data[cpu];
+
+ rec_count++;
+
+ now = ((double)r->cycles) / (opts.cpu_freq / 1000.0);
+
+ log_event(r->event);
+
+ switch (r->event) {
+
+ case TRC_SCHED_SWITCH_INFPREV:
+ // domain data[0] just switched out and received data[1] ns of cpu time
+ if (domain_ok(cpu, r->data[0], now))
+ qos_switch_out(cpu, r->data[0], now, r->data[1]);
+ // printf("ns_gotten %ld\n", r->data[1]);
+ break;
+
+ case TRC_SCHED_SWITCH_INFNEXT:
+ // domain data[0] just switched in and
+ // waited data[1] ns, and was allocated data[2] ns of cpu time
+ if (domain_ok(cpu, r->data[0], now))
+ qos_switch_in(cpu, r->data[0], now, r->data[2], r->data[1]);
+ break;
+
+ case TRC_SCHED_DOM_ADD:
+ if (domain_ok(cpu, r->data[0], now))
+ qos_init_domain(cpu, r->data[0], now);
+ break;
+
+ case TRC_SCHED_DOM_REM:
+ if (domain_ok(cpu, r->data[0], now))
+ qos_kill_thread(r->data[0]);
+ break;
+
+ case TRC_SCHED_SLEEP:
+ if (domain_ok(cpu, r->data[0], now))
+ qos_state_sleeping(cpu, r->data[0], now);
+ break;
+
+ case TRC_SCHED_WAKE:
+ if (domain_ok(cpu, r->data[0], now))
+ qos_state_runnable(cpu, r->data[0], now);
+ break;
+
+ case TRC_SCHED_BLOCK:
+ if (domain_ok(cpu, r->data[0], now))
+ qos_state_sleeping(cpu, r->data[0], now);
+ break;
+
+ case TRC_MEM_PAGE_GRANT_TRANSFER:
+ if (domain_ok(cpu, r->data[0], now))
+ qos_count_packets(r->data[0], now);
+ break;
+
+ default:
+ break;
+ }
+ new_qos = NULL;
+}
+
+
+
diff --git a/tools/xenmon/xenbaked.h b/tools/xenmon/xenbaked.h
new file mode 100644
index 0000000000..b2a72c4890
--- /dev/null
+++ b/tools/xenmon/xenbaked.h
@@ -0,0 +1,101 @@
+/******************************************************************************
+ * tools/xenbaked.h
+ *
+ * Header file for xenbaked
+ *
+ * Copyright (C) 2005 by Hewlett Packard, Palo Alto and Fort Collins
+ *
+ * Authors: Diwaker Gupta, diwaker.gupta@hp.com
+ * Rob Gardner, rob.gardner@hp.com
+ * Lucy Cherkasova, lucy.cherkasova.hp.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __QOS_H__
+#define __QOS_H__
+
+///// qos stuff
+#define million 1000000LL
+#define billion 1000000000LL
+
+#define QOS_ADD(N,A) ((N+A)<(NSAMPLES-1) ? (N+A) : A)
+#define QOS_INCR(N) ((N<(NSAMPLES-2)) ? (N+1) : 0)
+#define QOS_DECR(N) ((N==0) ? (NSAMPLES-1) : (N-1))
+
+#define MAX_NAME_SIZE 32
+#define IDLE_DOMAIN_ID 32767
+
+/* Number of domains we can keep track of in memory */
+#define NDOMAINS 32
+
+/* Number of data points to keep */
+#define NSAMPLES 100
+
+
+// per domain stuff
+typedef struct
+{
+ uint64_t last_update_time;
+ uint64_t start_time; // when the thread started running
+ uint64_t runnable_start_time; // when the thread became runnable
+ uint64_t blocked_start_time; // when the thread became blocked
+ uint64_t ns_since_boot; // time gone by since boot
+ uint64_t ns_oncpu_since_boot; // total cpu time used by thread since boot
+ // uint64_t ns_runnable_since_boot;
+ int runnable_at_last_update; // true if the thread was runnable last time we checked.
+ int runnable; // true if thread is runnable right now
+ // tells us something about what happened during the
+ // sample period that we are analysing right now
+ int in_use; //
+ domid_t id;
+ char name[MAX_NAME_SIZE];
+} _domain_info;
+
+
+
+typedef struct
+{
+ struct
+ {
+// data point:
+// stuff that is recorded once for each measurement interval
+ uint64_t ns_gotten[NDOMAINS]; // ns used in the last sample period
+ uint64_t ns_allocated[NDOMAINS]; // ns allocated by scheduler
+ uint64_t ns_waiting[NDOMAINS]; // ns spent waiting to execute, ie, time from
+ // becoming runnable until actually running
+ uint64_t ns_blocked[NDOMAINS]; // ns spent blocked
+ uint64_t switchin_count[NDOMAINS]; // number of executions of the domain
+ uint64_t io_count[NDOMAINS];
+ uint64_t ns_passed; // ns gone by on the wall clock, ie, the sample period
+ uint64_t timestamp;
+ uint64_t lost_records; // # of lost trace records this time period
+ uint64_t flip_free_periods; // # of executions of dom0 in which no page flips happened
+ } qdata[NSAMPLES];
+
+ _domain_info domain_info[NDOMAINS];
+
+ // control information
+ int next_datapoint;
+ int ncpu;
+ int structlen;
+
+ // parameters
+ int measurement_frequency; // for example
+
+} _new_qos_data;
+
+
+
+#endif
diff --git a/tools/xenmon/xenmon.py b/tools/xenmon/xenmon.py
new file mode 100644
index 0000000000..0c1f33c61f
--- /dev/null
+++ b/tools/xenmon/xenmon.py
@@ -0,0 +1,578 @@
+#!/usr/bin/env python
+
+#####################################################################
+# xenmon is a front-end for xenbaked.
+# There is a curses interface for live monitoring. XenMon also allows
+# logging to a file. For options, run python xenmon.py -h
+#
+# Copyright (C) 2005 by Hewlett Packard, Palo Alto and Fort Collins
+# Authors: Lucy Cherkasova, lucy.cherkasova@hp.com
+# Rob Gardner, rob.gardner@hp.com
+# Diwaker Gupta, diwaker.gupta@hp.com
+#####################################################################
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; under version 2 of the License.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#####################################################################
+
+import mmap
+import struct
+import os
+import time
+import optparse as _o
+import curses as _c
+import math
+import sys
+
+# constants
+NSAMPLES = 100
+NDOMAINS = 32
+
+# the struct strings for qos_info
+ST_DOM_INFO = "6Q4i32s"
+ST_QDATA = "%dQ" % (6*NDOMAINS + 4)
+
+# size of mmaped file
+QOS_DATA_SIZE = struct.calcsize(ST_QDATA)*NSAMPLES + struct.calcsize(ST_DOM_INFO)*NDOMAINS + struct.calcsize("4i")
+
+# location of mmaped file, hard coded right now
+SHM_FILE = "/tmp/xenq-shm"
+
+# format strings
+TOTALS = 15*' ' + "%6.2f%%" + 35*' ' + "%6.2f%%"
+
+ALLOCATED = "Allocated"
+GOTTEN = "Gotten"
+BLOCKED = "Blocked"
+WAITED = "Waited"
+IOCOUNT = "I/O Count"
+EXCOUNT = "Exec Count"
+
+# globals
+# our curses screen
+stdscr = None
+
+# parsed options
+options, args = None, None
+
+# the optparse module is quite smart
+# to see help, just run xenmon -h
+def setup_cmdline_parser():
+ parser = _o.OptionParser()
+ parser.add_option("-l", "--live", dest="live", action="store_true",
+ default=True, help = "show the ncurses live monitoring frontend (default)")
+ parser.add_option("-n", "--notlive", dest="live", action="store_false",
+ default="True", help = "write to file instead of live monitoring")
+ parser.add_option("-p", "--prefix", dest="prefix",
+ default = "log", help="prefix to use for output files")
+ parser.add_option("-t", "--time", dest="duration",
+ action="store", type="int", default=10,
+ help="stop logging to file after this much time has elapsed (in seconds). set to 0 to keep logging indefinitely")
+ parser.add_option("-i", "--interval", dest="interval",
+ action="store", type="int", default=1000,
+ help="interval for logging (in ms)")
+ parser.add_option("--ms_per_sample", dest="mspersample",
+ action="store", type="int", default=100,
+ help = "determines how many ms worth of data goes in a sample")
+ return parser
+
+# encapsulate information about a domain
+class DomainInfo:
+ def __init__(self):
+ self.allocated_samples = []
+ self.gotten_samples = []
+ self.blocked_samples = []
+ self.waited_samples = []
+ self.execcount_samples = []
+ self.iocount_samples = []
+ self.ffp_samples = []
+
+ def gotten_stats(self, passed):
+ total = float(sum(self.gotten_samples))
+ per = 100*total/passed
+ exs = sum(self.execcount_samples)
+ if exs > 0:
+ avg = total/exs
+ else:
+ avg = 0
+ return [total/(float(passed)/10**9), per, avg]
+
+ def waited_stats(self, passed):
+ total = float(sum(self.waited_samples))
+ per = 100*total/passed
+ exs = sum(self.execcount_samples)
+ if exs > 0:
+ avg = total/exs
+ else:
+ avg = 0
+ return [total/(float(passed)/10**9), per, avg]
+
+ def blocked_stats(self, passed):
+ total = float(sum(self.blocked_samples))
+ per = 100*total/passed
+ ios = sum(self.iocount_samples)
+ if ios > 0:
+ avg = total/float(ios)
+ else:
+ avg = 0
+ return [total/(float(passed)/10**9), per, avg]
+
+ def allocated_stats(self, passed):
+ total = sum(self.allocated_samples)
+ exs = sum(self.execcount_samples)
+ if exs > 0:
+ return float(total)/exs
+ else:
+ return 0
+
+ def ec_stats(self, passed):
+ total = float(sum(self.execcount_samples))/(float(passed)/10**9)
+ return total
+
+ def io_stats(self, passed):
+ total = float(sum(self.iocount_samples))
+ exs = sum(self.execcount_samples)
+ if exs > 0:
+ avg = total/exs
+ else:
+ avg = 0
+ return [total/(float(passed)/10**9), avg]
+
+ def stats(self, passed):
+ return [self.gotten_stats(passed), self.allocated_stats(passed), self.blocked_stats(passed),
+ self.waited_stats(passed), self.ec_stats(passed), self.io_stats(passed)]
+
+# report values over desired interval
+def summarize(startat, endat, duration, samples):
+ dominfos = {}
+ for i in range(0, NDOMAINS):
+ dominfos[i] = DomainInfo()
+
+ passed = 1 # to prevent zero division
+ curid = startat
+ numbuckets = 0
+ lost_samples = []
+ ffp_samples = []
+
+ while passed < duration:
+ for i in range(0, NDOMAINS):
+ dominfos[i].gotten_samples.append(samples[curid][0*NDOMAINS + i])
+ dominfos[i].allocated_samples.append(samples[curid][1*NDOMAINS + i])
+ dominfos[i].waited_samples.append(samples[curid][2*NDOMAINS + i])
+ dominfos[i].blocked_samples.append(samples[curid][3*NDOMAINS + i])
+ dominfos[i].execcount_samples.append(samples[curid][4*NDOMAINS + i])
+ dominfos[i].iocount_samples.append(samples[curid][5*NDOMAINS + i])
+
+ passed += samples[curid][6*NDOMAINS]
+ lost_samples.append(samples[curid][6*NDOMAINS + 2])
+ ffp_samples.append(samples[curid][6*NDOMAINS + 3])
+
+ numbuckets += 1
+
+ if curid > 0:
+ curid -= 1
+ else:
+ curid = NSAMPLES - 1
+ if curid == endat:
+ break
+
+ lostinfo = [min(lost_samples), sum(lost_samples), max(lost_samples)]
+ ffpinfo = [min(ffp_samples), sum(ffp_samples), max(ffp_samples)]
+ ldoms = map(lambda x: dominfos[x].stats(passed), range(0, NDOMAINS))
+
+ return [ldoms, lostinfo, ffpinfo]
+
+# scale microseconds to milliseconds or seconds as necessary
+def time_scale(ns):
+ if ns < 1000:
+ return "%4.2f ns" % float(ns)
+ elif ns < 1000*1000:
+ return "%4.2f us" % (float(ns)/10**3)
+ elif ns < 10**9:
+ return "%4.2f ms" % (float(ns)/10**6)
+ else:
+ return "%4.2f s" % (float(ns)/10**9)
+
+# paint message on curses screen, but detect screen size errors
+def display(scr, row, col, str, attr=0):
+ try:
+ scr.addstr(row, col, str, attr)
+ except:
+ scr.erase()
+ _c.nocbreak()
+ scr.keypad(0)
+ _c.echo()
+ _c.endwin()
+ print "Your terminal screen is not big enough; Please resize it."
+ print "row=%d, col=%d, str='%s'" % (row, col, str)
+ sys.exit(1)
+
+
+# the live monitoring code
+def show_livestats():
+ cpu = 0 # cpu of interest to display data for
+ ncpu = 1 # number of cpu's on this platform
+ slen = 0 # size of shared data structure, incuding padding
+
+ # mmap the (the first chunk of the) file
+ shmf = open(SHM_FILE, "r+")
+ shm = mmap.mmap(shmf.fileno(), QOS_DATA_SIZE)
+
+ samples = []
+ doms = []
+
+ # initialize curses
+ stdscr = _c.initscr()
+ _c.noecho()
+ _c.cbreak()
+
+ stdscr.keypad(1)
+ stdscr.timeout(1000)
+ [maxy, maxx] = stdscr.getmaxyx()
+
+
+
+ # display in a loop
+ while True:
+
+ for cpuidx in range(0, ncpu):
+
+ # calculate offset in mmap file to start from
+ idx = cpuidx * slen
+
+
+ samples = []
+ doms = []
+
+ # read in data
+ for i in range(0, NSAMPLES):
+ len = struct.calcsize(ST_QDATA)
+ sample = struct.unpack(ST_QDATA, shm[idx:idx+len])
+ samples.append(sample)
+ idx += len
+
+ for i in range(0, NDOMAINS):
+ len = struct.calcsize(ST_DOM_INFO)
+ dom = struct.unpack(ST_DOM_INFO, shm[idx:idx+len])
+ doms.append(dom)
+ idx += len
+
+ len = struct.calcsize("4i")
+ oldncpu = ncpu
+ (next, ncpu, slen, freq) = struct.unpack("4i", shm[idx:idx+len])
+ idx += len
+
+ # xenbaked tells us how many cpu's it's got, so re-do
+ # the mmap if necessary to get multiple cpu data
+ if oldncpu != ncpu:
+ shm = mmap.mmap(shmf.fileno(), ncpu*slen)
+
+ # if we've just calculated data for the cpu of interest, then
+ # stop examining mmap data and start displaying stuff
+ if cpuidx == cpu:
+ break
+
+ # calculate starting and ending datapoints; never look at "next" since
+ # it represents live data that may be in transition.
+ startat = next - 1
+ if next + 10 < NSAMPLES:
+ endat = next + 10
+ else:
+ endat = 10
+
+ # get summary over desired interval
+ [h1, l1, f1] = summarize(startat, endat, 10**9, samples)
+ [h2, l2, f2] = summarize(startat, endat, 10 * 10**9, samples)
+
+ # the actual display code
+ row = 0
+ display(stdscr, row, 1, "CPU = %d" % cpu, _c.A_STANDOUT)
+
+ display(stdscr, row, 10, "%sLast 10 seconds%sLast 1 second" % (6*' ', 30*' '), _c.A_BOLD)
+ row +=1
+ display(stdscr, row, 1, "%s" % ((maxx-2)*'='))
+
+ total_h1_cpu = 0
+ total_h2_cpu = 0
+
+ for dom in range(0, NDOMAINS):
+ if h1[dom][0][1] > 0 or dom == NDOMAINS - 1:
+ # display gotten
+ row += 1
+ col = 2
+ display(stdscr, row, col, "%d" % dom)
+ col += 4
+ display(stdscr, row, col, "%s" % time_scale(h2[dom][0][0]))
+ col += 12
+ display(stdscr, row, col, "%3.2f%%" % h2[dom][0][1])
+ col += 12
+ display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][0][2]))
+ col += 18
+ display(stdscr, row, col, "%s" % time_scale(h1[dom][0][0]))
+ col += 12
+ display(stdscr, row, col, "%3.2f%%" % h1[dom][0][1])
+ col += 12
+ display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][0][2]))
+ col += 18
+ display(stdscr, row, col, "Gotten")
+
+ # display allocated
+ row += 1
+ col = 2
+ display(stdscr, row, col, "%d" % dom)
+ col += 28
+ display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][1]))
+ col += 42
+ display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][1]))
+ col += 18
+ display(stdscr, row, col, "Allocated")
+
+ # display blocked
+ row += 1
+ col = 2
+ display(stdscr, row, col, "%d" % dom)
+ col += 4
+ display(stdscr, row, col, "%s" % time_scale(h2[dom][2][0]))
+ col += 12
+ display(stdscr, row, col, "%3.2f%%" % h2[dom][2][1])
+ col += 12
+ display(stdscr, row, col, "%s/io" % time_scale(h2[dom][2][2]))
+ col += 18
+ display(stdscr, row, col, "%s" % time_scale(h1[dom][2][0]))
+ col += 12
+ display(stdscr, row, col, "%3.2f%%" % h1[dom][2][1])
+ col += 12
+ display(stdscr, row, col, "%s/io" % time_scale(h1[dom][2][2]))
+ col += 18
+ display(stdscr, row, col, "Blocked")
+
+ # display waited
+ row += 1
+ col = 2
+ display(stdscr, row, col, "%d" % dom)
+ col += 4
+ display(stdscr, row, col, "%s" % time_scale(h2[dom][3][0]))
+ col += 12
+ display(stdscr, row, col, "%3.2f%%" % h2[dom][3][1])
+ col += 12
+ display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][3][2]))
+ col += 18
+ display(stdscr, row, col, "%s" % time_scale(h1[dom][3][0]))
+ col += 12
+ display(stdscr, row, col, "%3.2f%%" % h1[dom][3][1])
+ col += 12
+ display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][3][2]))
+ col += 18
+ display(stdscr, row, col, "Waited")
+
+ # display ex count
+ row += 1
+ col = 2
+ display(stdscr, row, col, "%d" % dom)
+
+ col += 28
+ display(stdscr, row, col, "%d/s" % h2[dom][4])
+ col += 42
+ display(stdscr, row, col, "%d" % h1[dom][4])
+ col += 18
+ display(stdscr, row, col, "Execution count")
+
+ # display io count
+ row += 1
+ col = 2
+ display(stdscr, row, col, "%d" % dom)
+ col += 4
+ display(stdscr, row, col, "%d/s" % h2[dom][5][0])
+ col += 24
+ display(stdscr, row, col, "%d/ex" % h2[dom][5][1])
+ col += 18
+ display(stdscr, row, col, "%d" % h1[dom][5][0])
+ col += 24
+ display(stdscr, row, col, "%3.2f/ex" % h1[dom][5][1])
+ col += 18
+ display(stdscr, row, col, "I/O Count")
+
+ #row += 1
+ #stdscr.hline(row, 1, '-', maxx - 2)
+ total_h1_cpu += h1[dom][0][1]
+ total_h2_cpu += h2[dom][0][1]
+
+
+ row += 1
+ display(stdscr, row, 2, TOTALS % (total_h2_cpu, total_h1_cpu))
+ row += 1
+# display(stdscr, row, 2,
+# "\tFFP: %d (Min: %d, Max: %d)\t\t\tFFP: %d (Min: %d, Max %d)" %
+# (math.ceil(f2[1]), f2[0], f2[2], math.ceil(f1[1]), f1[0], f1[2]), _c.A_BOLD)
+
+ if l1[1] > 1 :
+ row += 1
+ display(stdscr, row, 2,
+ "\tRecords lost: %d (Min: %d, Max: %d)\t\t\tRecords lost: %d (Min: %d, Max %d)" %
+ (math.ceil(l2[1]), l2[0], l2[2], math.ceil(l1[1]), l1[0], l1[2]), _c.A_BOLD)
+
+ # grab a char from tty input; exit if interrupt hit
+ try:
+ c = stdscr.getch()
+ except:
+ break
+
+ # q = quit
+ if c == ord('q'):
+ break
+
+ # c = cycle to a new cpu of interest
+ if c == ord('c'):
+ cpu = (cpu + 1) % ncpu
+
+ stdscr.erase()
+
+ _c.nocbreak()
+ stdscr.keypad(0)
+ _c.echo()
+ _c.endwin()
+ shm.close()
+ shmf.close()
+
+
+# simple functions to allow initialization of log files without actually
+# physically creating files that are never used; only on the first real
+# write does the file get created
+class Delayed(file):
+ def __init__(self, filename, mode):
+ self.filename = filename
+ self.saved_mode = mode
+ self.delay_data = ""
+ self.opened = 0
+
+ def delayed_write(self, str):
+ self.delay_data = str
+
+ def write(self, str):
+ if not self.opened:
+ self.file = open(self.filename, self.saved_mode)
+ self.opened = 1
+ self.file.write(self.delay_data)
+ self.file.write(str)
+
+ def flush(self):
+ if self.opened:
+ self.file.flush()
+
+ def close(self):
+ if self.opened:
+ self.file.close()
+
+
+def writelog():
+ global options
+
+ ncpu = 1 # number of cpu's
+ slen = 0 # size of shared structure inc. padding
+
+ shmf = open(SHM_FILE, "r+")
+ shm = mmap.mmap(shmf.fileno(), QOS_DATA_SIZE)
+
+ interval = 0
+ outfiles = {}
+ for dom in range(0, NDOMAINS):
+ outfiles[dom] = Delayed("%s-dom%d.log" % (options.prefix, dom), 'w')
+ outfiles[dom].delayed_write("# passed cpu dom cpu(tot) cpu(%) cpu/ex allocated/ex blocked(tot) blocked(%) blocked/io waited(tot) waited(%) waited/ex ex/s io(tot) io/ex\n")
+
+ while options.duration == 0 or interval < (options.duration * 1000):
+ for cpuidx in range(0, ncpu):
+ idx = cpuidx * slen # offset needed in mmap file
+
+
+ samples = []
+ doms = []
+
+ for i in range(0, NSAMPLES):
+ len = struct.calcsize(ST_QDATA)
+ sample = struct.unpack(ST_QDATA, shm[idx:idx+len])
+ samples.append(sample)
+ idx += len
+
+ for i in range(0, NDOMAINS):
+ len = struct.calcsize(ST_DOM_INFO)
+ dom = struct.unpack(ST_DOM_INFO, shm[idx:idx+len])
+ doms.append(dom)
+ idx += len
+
+ len = struct.calcsize("4i")
+ oldncpu = ncpu
+ (next, ncpu, slen, freq) = struct.unpack("4i", shm[idx:idx+len])
+ idx += len
+
+ if oldncpu != ncpu:
+ shm = mmap.mmap(shmf.fileno(), ncpu*slen)
+
+ startat = next - 1
+ if next + 10 < NSAMPLES:
+ endat = next + 10
+ else:
+ endat = 10
+
+ [h1,l1, f1] = summarize(startat, endat, options.interval * 10**6, samples)
+ for dom in range(0, NDOMAINS):
+ if h1[dom][0][1] > 0 or dom == NDOMAINS - 1:
+ outfiles[dom].write("%.3f %d %d %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n" %
+ (interval, cpuidx, dom,
+ h1[dom][0][0], h1[dom][0][1], h1[dom][0][2],
+ h1[dom][1],
+ h1[dom][2][0], h1[dom][2][1], h1[dom][2][2],
+ h1[dom][3][0], h1[dom][3][1], h1[dom][3][2],
+ h1[dom][4],
+ h1[dom][5][0], h1[dom][5][1]))
+ outfiles[dom].flush()
+
+ interval += options.interval
+ time.sleep(1)
+
+ for dom in range(0, NDOMAINS):
+ outfiles[dom].close()
+
+# start xenbaked
+def start_xenbaked():
+ global options
+ global args
+
+ os.system("killall -9 xenbaked")
+ # assumes that xenbaked is in your path
+ os.system("xenbaked --ms_per_sample=%d &" %
+ options.mspersample)
+ time.sleep(1)
+
+# stop xenbaked
+def stop_xenbaked():
+ os.system("killall -s INT xenbaked")
+
+def main():
+ global options
+ global args
+ global domains
+
+ parser = setup_cmdline_parser()
+ (options, args) = parser.parse_args()
+
+ start_xenbaked()
+ if options.live:
+ show_livestats()
+ else:
+ try:
+ writelog()
+ except:
+ print 'Quitting.'
+ stop_xenbaked()
+
+if __name__ == "__main__":
+ main()